Add languages settings (#52)

This commit is contained in:
Ashinch 2022-05-07 18:40:27 +08:00 committed by GitHub
parent a225513088
commit dc2e18bcee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 265 additions and 30 deletions

View File

@ -77,6 +77,9 @@ class App : Application(), Configuration.Provider {
} }
} }
private fun dataStoreInit() {
}
private suspend fun accountInit() { private suspend fun accountInit() {
if (accountRepository.isNoAccount()) { if (accountRepository.isNoAccount()) {
val account = accountRepository.addDefaultAccount() val account = accountRepository.addDefaultAccount()
@ -85,9 +88,6 @@ class App : Application(), Configuration.Provider {
} }
} }
private fun dataStoreInit() {
}
private fun workerInit() { private fun workerInit() {
rssRepository.get().doSync() rssRepository.get().doSync()
} }

View File

@ -18,7 +18,9 @@ import coil.memory.MemoryCache
import coil.request.* import coil.request.*
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CompletableDeferred
import me.ash.reader.data.preference.LanguagesPreference
import me.ash.reader.data.preference.SettingsProvider import me.ash.reader.data.preference.SettingsProvider
import me.ash.reader.ui.ext.languages
import me.ash.reader.ui.page.common.HomeEntry import me.ash.reader.ui.page.common.HomeEntry
@AndroidEntryPoint @AndroidEntryPoint
@ -28,6 +30,13 @@ class MainActivity : ComponentActivity(), ImageLoader {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false) WindowCompat.setDecorFitsSystemWindows(window, false)
Log.i("RLog", "onCreate: ${ProfileInstallerInitializer().create(this)}") Log.i("RLog", "onCreate: ${ProfileInstallerInitializer().create(this)}")
// Set the language
LanguagesPreference.fromValue(languages).let {
if (it == LanguagesPreference.UseDeviceLanguages) return@let
it.setLocale(this)
}
setContent { setContent {
SettingsProvider { SettingsProvider {
HomeEntry() HomeEntry()

View File

@ -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
}
}
}

View File

@ -37,6 +37,8 @@ data class Settings(
val flowArticleListTime: FlowArticleListTimePreference = FlowArticleListTimePreference.default, val flowArticleListTime: FlowArticleListTimePreference = FlowArticleListTimePreference.default,
val flowArticleListDateStickyHeader: FlowArticleListDateStickyHeaderPreference = FlowArticleListDateStickyHeaderPreference.default, val flowArticleListDateStickyHeader: FlowArticleListDateStickyHeaderPreference = FlowArticleListDateStickyHeaderPreference.default,
val flowArticleListTonalElevation: FlowArticleListTonalElevationPreference = FlowArticleListTonalElevationPreference.default, val flowArticleListTonalElevation: FlowArticleListTonalElevationPreference = FlowArticleListTonalElevationPreference.default,
val languages: LanguagesPreference = LanguagesPreference.default,
) )
fun Preferences.toSettings(): Settings { fun Preferences.toSettings(): Settings {
@ -68,6 +70,8 @@ fun Preferences.toSettings(): Settings {
this this
), ),
flowArticleListTonalElevation = FlowArticleListTonalElevationPreference.fromPreferences(this), flowArticleListTonalElevation = FlowArticleListTonalElevationPreference.fromPreferences(this),
languages = LanguagesPreference.fromPreferences(this),
) )
} }
@ -109,6 +113,8 @@ fun SettingsProvider(
LocalFlowFilterBarFilled provides settings.flowFilterBarFilled, LocalFlowFilterBarFilled provides settings.flowFilterBarFilled,
LocalFlowFilterBarPadding provides settings.flowFilterBarPadding, LocalFlowFilterBarPadding provides settings.flowFilterBarPadding,
LocalFlowFilterBarTonalElevation provides settings.flowFilterBarTonalElevation, LocalFlowFilterBarTonalElevation provides settings.flowFilterBarTonalElevation,
LocalLanguages provides settings.languages,
) { ) {
content() content()
} }
@ -162,3 +168,6 @@ val LocalFlowArticleListDateStickyHeader =
compositionLocalOf<FlowArticleListDateStickyHeaderPreference> { FlowArticleListDateStickyHeaderPreference.default } compositionLocalOf<FlowArticleListDateStickyHeaderPreference> { FlowArticleListDateStickyHeaderPreference.default }
val LocalFlowArticleListTonalElevation = val LocalFlowArticleListTonalElevation =
compositionLocalOf<FlowArticleListTonalElevationPreference> { FlowArticleListTonalElevationPreference.default } compositionLocalOf<FlowArticleListTonalElevationPreference> { FlowArticleListTonalElevationPreference.default }
val LocalLanguages =
compositionLocalOf<LanguagesPreference> { LanguagesPreference.default }

View File

@ -24,6 +24,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import me.ash.reader.ui.theme.palette.alwaysLight import me.ash.reader.ui.theme.palette.alwaysLight
@ -43,7 +44,7 @@ fun Banner(
Surface( Surface(
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.height(88.dp), .height(if (!desc.isNullOrBlank()) 88.dp else Dp.Unspecified),
color = Color.Unspecified, color = Color.Unspecified,
) { ) {
Row( Row(

View File

@ -39,6 +39,9 @@ val Context.initialPage: Int
val Context.initialFilter: Int val Context.initialFilter: Int
get() = this.dataStore.get(DataStoreKeys.InitialFilter) ?: 2 get() = this.dataStore.get(DataStoreKeys.InitialFilter) ?: 2
val Context.languages: Int
get() = this.dataStore.get(DataStoreKeys.Languages) ?: 0
suspend fun <T> DataStore<Preferences>.put(dataStoreKeys: DataStoreKeys<T>, value: T) { suspend fun <T> DataStore<Preferences>.put(dataStoreKeys: DataStoreKeys<T>, value: T) {
this.edit { this.edit {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
@ -244,4 +247,9 @@ sealed class DataStoreKeys<T> {
override val key: Preferences.Key<Int> override val key: Preferences.Key<Int>
get() = intPreferencesKey("initialFilter") get() = intPreferencesKey("initialFilter")
} }
object Languages : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("languages")
}
} }

View File

@ -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.feeds.FeedsPageStyle
import me.ash.reader.ui.page.settings.color.flow.FlowPageStyle 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.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.settings.tips.TipsAndSupport
import me.ash.reader.ui.page.startup.StartupPage import me.ash.reader.ui.page.startup.StartupPage
import me.ash.reader.ui.theme.AppTheme import me.ash.reader.ui.theme.AppTheme
@ -146,6 +147,11 @@ fun HomeEntry(
Interaction(navController) Interaction(navController)
} }
// Languages
animatedComposable(route = RouteName.LANGUAGES) {
Languages(navController = navController)
}
// Tips & Support // Tips & Support
animatedComposable(route = RouteName.TIPS_AND_SUPPORT) { animatedComposable(route = RouteName.TIPS_AND_SUPPORT) {
TipsAndSupport(navController) TipsAndSupport(navController)

View File

@ -21,6 +21,9 @@ object RouteName {
// Interaction // Interaction
const val INTERACTION = "interaction" const val INTERACTION = "interaction"
// Languages
const val LANGUAGES = "languages"
// Tips & Support // Tips & Support
const val TIPS_AND_SUPPORT = "tips_and_support" const val TIPS_AND_SUPPORT = "tips_and_support"
} }

View File

@ -143,8 +143,11 @@ fun SettingsPage(
title = stringResource(R.string.languages), title = stringResource(R.string.languages),
desc = stringResource(R.string.languages_desc), desc = stringResource(R.string.languages_desc),
icon = Icons.Outlined.Language, icon = Icons.Outlined.Language,
enable = false, ) {
) {} navController.navigate(RouteName.LANGUAGES) {
launchSingleTop = true
}
}
} }
item { item {
SelectableSettingGroupItem( SelectableSettingGroupItem(
@ -156,6 +159,7 @@ fun SettingsPage(
launchSingleTop = true launchSingleTop = true
} }
} }
Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
} }
} }
} }

View File

@ -153,7 +153,7 @@ fun FlowPageStyle(
text = stringResource(R.string.article_list) text = stringResource(R.string.article_list)
) )
SettingItem( SettingItem(
title = stringResource(R.string.display_feed_favicon), title = stringResource(R.string.feed_favicons),
onClick = { onClick = {
(!articleListFeedIcon).put(context, scope) (!articleListFeedIcon).put(context, scope)
}, },
@ -163,7 +163,7 @@ fun FlowPageStyle(
} }
} }
SettingItem( SettingItem(
title = stringResource(R.string.display_feed_name), title = stringResource(R.string.feed_names),
onClick = { onClick = {
(!articleListFeedName).put(context, scope) (!articleListFeedName).put(context, scope)
}, },
@ -173,14 +173,14 @@ fun FlowPageStyle(
} }
} }
SettingItem( SettingItem(
title = stringResource(R.string.display_article_image), title = stringResource(R.string.article_images),
enable = false, enable = false,
onClick = {}, onClick = {},
) { ) {
Switch(activated = false, enable = false) Switch(activated = false, enable = false)
} }
SettingItem( SettingItem(
title = stringResource(R.string.display_article_desc), title = stringResource(R.string.article_desc),
onClick = { onClick = {
(!articleListDesc).put(context, scope) (!articleListDesc).put(context, scope)
}, },
@ -190,7 +190,7 @@ fun FlowPageStyle(
} }
} }
SettingItem( SettingItem(
title = stringResource(R.string.display_article_date), title = stringResource(R.string.article_date),
onClick = { onClick = {
(!articleListTime).put(context, scope) (!articleListTime).put(context, scope)
}, },

View File

@ -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)
})
}
}
}
}
}
)
}

View File

@ -1,5 +1,4 @@
<resources> <resources>
<string name="read_you">Read You</string>
<string name="all">全部</string> <string name="all">全部</string>
<string name="all_desc">%1$d 项已归档</string> <string name="all_desc">%1$d 项已归档</string>
<string name="unread">未读</string> <string name="unread">未读</string>
@ -90,7 +89,9 @@
<string name="interaction">交互</string> <string name="interaction">交互</string>
<string name="interaction_desc">启动时、触感反馈</string> <string name="interaction_desc">启动时、触感反馈</string>
<string name="languages">语言</string> <string name="languages">语言</string>
<string name="languages_desc">英语、中文</string> <string name="languages_desc">English、简体中文</string>
<string name="use_device_languages">跟随系统设置</string>
<string name="help_translate">帮助我们翻译</string>
<string name="tips_and_support">提示和支持</string> <string name="tips_and_support">提示和支持</string>
<string name="tips_and_support_desc">关于、开源</string> <string name="tips_and_support_desc">关于、开源</string>
<string name="welcome">欢迎</string> <string name="welcome">欢迎</string>
@ -120,8 +121,6 @@
<string name="reading_page">阅读页面</string> <string name="reading_page">阅读页面</string>
<string name="sponsor">捐赠</string> <string name="sponsor">捐赠</string>
<string name="open_source_licenses">开放源代码许可</string> <string name="open_source_licenses">开放源代码许可</string>
<string name="github_link">https://github.com/Ashinch/ReadYou</string>
<string name="telegram_link">https://t.me/ReadYouApp</string>
<string name="update_link">https://gitee.com/api/v5/repos/Ashinch/ReadYou/releases/latest</string> <string name="update_link">https://gitee.com/api/v5/repos/Ashinch/ReadYou/releases/latest</string>
<string name="change_log">更新日志</string> <string name="change_log">更新日志</string>
<string name="update">更新</string> <string name="update">更新</string>
@ -140,11 +139,11 @@
<string name="preview_feed_name">漩涡书院</string> <string name="preview_feed_name">漩涡书院</string>
<string name="value"></string> <string name="value"></string>
<string name="padding_on_both_ends">两端边距</string> <string name="padding_on_both_ends">两端边距</string>
<string name="display_article_date">显示文章发布时间</string> <string name="article_date">文章发布时间</string>
<string name="display_article_desc">显示文章描述</string> <string name="article_desc">文章描述</string>
<string name="display_article_image">显示文章插图</string> <string name="article_images">文章插图</string>
<string name="display_feed_name">显示订阅源名称</string> <string name="feed_names">订阅源名称</string>
<string name="display_feed_favicon">显示订阅源图标</string> <string name="feed_favicons">订阅源图标</string>
<string name="article_date_sticky_header">文章发布日期粘性标签(实验性)</string> <string name="article_date_sticky_header">文章发布日期粘性标签(实验性)</string>
<string name="article_list">文章列表</string> <string name="article_list">文章列表</string>
<string name="group_list">分组列表</string> <string name="group_list">分组列表</string>

View File

@ -1,5 +1,5 @@
<resources> <resources>
<string name="read_you">Read You</string> <string name="read_you" translatable="false">Read You</string>
<string name="all">All</string> <string name="all">All</string>
<string name="all_desc">%1$d Archived Items</string> <string name="all_desc">%1$d Archived Items</string>
<string name="unread">Unread</string> <string name="unread">Unread</string>
@ -91,7 +91,11 @@
<string name="interaction">Interaction</string> <string name="interaction">Interaction</string>
<string name="interaction_desc">On start, haptic feedback</string> <string name="interaction_desc">On start, haptic feedback</string>
<string name="languages">Languages</string> <string name="languages">Languages</string>
<string name="languages_desc">English, Chinese</string> <string name="languages_desc">English, 简体中文</string>
<string name="help_translate">Help us translate</string>
<string name="use_device_languages">Use Device Languages</string>
<string name="english" translatable="false">English</string>
<string name="chinese_simplified" translatable="false">简体中文</string>
<string name="tips_and_support">Tips &amp; support</string> <string name="tips_and_support">Tips &amp; support</string>
<string name="tips_and_support_desc">About, open source</string> <string name="tips_and_support_desc">About, open source</string>
<string name="welcome">Welcome</string> <string name="welcome">Welcome</string>
@ -121,8 +125,8 @@
<string name="reading_page">Reading Page</string> <string name="reading_page">Reading Page</string>
<string name="sponsor">Sponsor</string> <string name="sponsor">Sponsor</string>
<string name="open_source_licenses">Open Source Licenses</string> <string name="open_source_licenses">Open Source Licenses</string>
<string name="github_link">https://github.com/Ashinch/ReadYou</string> <string name="github_link" translatable="false">https://github.com/Ashinch/ReadYou</string>
<string name="telegram_link">https://t.me/ReadYouApp</string> <string name="telegram_link" translatable="false">https://t.me/ReadYouApp</string>
<string name="update_link">https://api.github.com/repos/Ashinch/ReadYou/releases/latest</string> <string name="update_link">https://api.github.com/repos/Ashinch/ReadYou/releases/latest</string>
<string name="change_log">Change Log</string> <string name="change_log">Change Log</string>
<string name="update">Update</string> <string name="update">Update</string>
@ -141,12 +145,12 @@
<string name="preview_feed_name">Reddit</string> <string name="preview_feed_name">Reddit</string>
<string name="value">value</string> <string name="value">value</string>
<string name="padding_on_both_ends">Padding on Both Ends</string> <string name="padding_on_both_ends">Padding on Both Ends</string>
<string name="display_article_date">Display Article Publish Time</string> <string name="article_date">Article Published Time</string>
<string name="display_article_desc">Display Article Descriptions</string> <string name="article_desc">Article Descriptions</string>
<string name="display_article_image">Display Article Images</string> <string name="article_images">Article Images</string>
<string name="display_feed_name">Display Feed Names</string> <string name="feed_names">Feed Names</string>
<string name="display_feed_favicon">Display Feed Favicons</string> <string name="feed_favicons">Feed Favicons</string>
<string name="article_date_sticky_header">Article Publish Date Sticky Header (Experimental)</string> <string name="article_date_sticky_header">Article Published Date Sticky Header (Experimental)</string>
<string name="article_list">Article List</string> <string name="article_list">Article List</string>
<string name="group_list">Group List</string> <string name="group_list">Group List</string>
<string name="always_expand">Always Expand</string> <string name="always_expand">Always Expand</string>