From 582e0f8148739c4ef3adfd4bf575caca10c86428 Mon Sep 17 00:00:00 2001 From: Ash Date: Mon, 21 Mar 2022 00:07:45 +0800 Subject: [PATCH] Use localized language --- README-zh.md | 2 +- README.md | 2 +- app/src/main/AndroidManifest.xml | 2 +- app/src/main/java/me/ash/reader/App.kt | 3 + app/src/main/java/me/ash/reader/DateExt.kt | 42 ++++++++++++++ .../main/java/me/ash/reader/DateTimeExt.kt | 53 ------------------ .../me/ash/reader/data/constant/Filter.kt | 8 --- .../reader/data/constant/NavigationBarItem.kt | 18 ------ .../me/ash/reader/data/constant/Symbol.kt | 8 --- .../data/repository/AccountRepository.kt | 13 ++--- .../data/repository/LocalRssRepository.kt | 11 ++-- .../data/repository/StringsRepository.kt | 12 ++++ .../me/ash/reader/ui/extension/FilterExt.kt | 20 +++++++ .../me/ash/reader/ui/page/common/ExtraName.kt | 5 ++ .../ui/page/common/NotificationGroupName.kt | 5 ++ .../reader/ui/page/home/HomeBottomNavBar.kt | 46 +++++++--------- .../me/ash/reader/ui/page/home/HomePage.kt | 7 ++- .../reader/ui/page/home/feeds/FeedsPage.kt | 21 ++++--- .../reader/ui/page/home/feeds/GroupItem.kt | 8 ++- .../home/feeds/subscribe/ResultViewPage.kt | 48 ++++++++-------- .../home/feeds/subscribe/SearchViewPage.kt | 14 +++-- .../home/feeds/subscribe/SubscribeDialog.kt | 33 ++++++----- .../feeds/subscribe/SubscribeViewModel.kt | 55 ++++++++++--------- .../feeds/subscribe/SubscribeViewPager.kt | 17 +++--- .../reader/ui/page/home/flow/ArticleItem.kt | 7 ++- .../reader/ui/page/home/flow/ArticleList.kt | 8 +-- .../ash/reader/ui/page/home/flow/FlowPage.kt | 15 +++-- .../me/ash/reader/ui/page/home/read/Header.kt | 5 +- .../ui/page/home/read/ReadPageTopBar.kt | 4 +- .../reader/ui/page/settings/SettingsPage.kt | 4 +- .../me/ash/reader/ui/widget/SelectionChip.kt | 6 +- app/src/main/res/values-zh-rCN/strings.xml | 41 ++++++++++++++ app/src/main/res/values/strings.xml | 40 +++++++++++++- 33 files changed, 343 insertions(+), 240 deletions(-) create mode 100644 app/src/main/java/me/ash/reader/DateExt.kt delete mode 100644 app/src/main/java/me/ash/reader/DateTimeExt.kt delete mode 100644 app/src/main/java/me/ash/reader/data/constant/NavigationBarItem.kt delete mode 100644 app/src/main/java/me/ash/reader/data/constant/Symbol.kt create mode 100644 app/src/main/java/me/ash/reader/data/repository/StringsRepository.kt create mode 100644 app/src/main/java/me/ash/reader/ui/extension/FilterExt.kt create mode 100644 app/src/main/java/me/ash/reader/ui/page/common/ExtraName.kt create mode 100644 app/src/main/java/me/ash/reader/ui/page/common/NotificationGroupName.kt create mode 100644 app/src/main/res/values-zh-rCN/strings.xml diff --git a/README-zh.md b/README-zh.md index 1233ab1..81d0950 100644 --- a/README-zh.md +++ b/README-zh.md @@ -21,9 +21,9 @@ - [x] 全文解析 - [x] 过滤未读、星标 - [x] Feed 分组 + - [x] 本地化 - [ ] 文章搜索 - [ ] 偏好设置 - - [ ] 本地化 - [ ] 发布 APK - [ ] 小组件 - [ ] ... diff --git a/README.md b/README.md index 01509e7..cebf719 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,9 @@ The following are the progress made so far and the goals to be worked on in the - [x] Full Content Parsing - [x] Filter unread and starred - [x] Feed Grouping + - [x] Localization - [ ] Search for articles - [ ] Preference settings - - [ ] Localization - [ ] Release APK - [ ] Widget - [ ] ... diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2bc2e99..d61d9fa 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,7 +8,7 @@ android:name=".App" android:allowBackup="true" android:icon="@mipmap/ic_launcher" - android:label="@string/app_name" + android:label="@string/read_you" android:networkSecurityConfig="@xml/network_security_config" android:roundIcon="@mipmap/ic_launcher" android:supportsRtl="true" diff --git a/app/src/main/java/me/ash/reader/App.kt b/app/src/main/java/me/ash/reader/App.kt index 05ae60f..f5d3855 100644 --- a/app/src/main/java/me/ash/reader/App.kt +++ b/app/src/main/java/me/ash/reader/App.kt @@ -26,6 +26,9 @@ class App : Application() { @Inject lateinit var rssHelper: RssHelper + @Inject + lateinit var stringsRepository: StringsRepository + @Inject lateinit var accountRepository: AccountRepository diff --git a/app/src/main/java/me/ash/reader/DateExt.kt b/app/src/main/java/me/ash/reader/DateExt.kt new file mode 100644 index 0000000..55fa401 --- /dev/null +++ b/app/src/main/java/me/ash/reader/DateExt.kt @@ -0,0 +1,42 @@ +package me.ash.reader + +import android.content.Context +import androidx.core.os.ConfigurationCompat +import java.text.DateFormat +import java.text.SimpleDateFormat +import java.util.* + +fun Date.formatToString( + context: Context, + onlyHourMinute: Boolean? = false, + atHourMinute: Boolean? = false, +): String { + val locale = ConfigurationCompat.getLocales(context.resources.configuration)[0] + val df = DateFormat.getDateInstance(DateFormat.FULL, locale) + return when { + onlyHourMinute == true -> { + SimpleDateFormat("HH:mm", locale).format(this) + } + atHourMinute == true -> { + context.getString( + R.string.date_at_time, + df.format(this), + SimpleDateFormat("HH:mm", locale).format(this), + ) + } + else -> { + df.format(this).run { + when (this) { + df.format(Date()) -> context.getString(R.string.today) + df.format( + Calendar.getInstance().apply { + time = Date() + add(Calendar.DAY_OF_MONTH, -1) + }.time + ) -> context.getString(R.string.yesterday) + else -> this + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/DateTimeExt.kt b/app/src/main/java/me/ash/reader/DateTimeExt.kt deleted file mode 100644 index a29e0ce..0000000 --- a/app/src/main/java/me/ash/reader/DateTimeExt.kt +++ /dev/null @@ -1,53 +0,0 @@ -package me.ash.reader - -import java.text.SimpleDateFormat -import java.util.* - -object DateTimeExt { - - const val HH_MM_SS = "HH:mm:ss" - const val HH_MM = "HH:mm" - const val MM_SS = "mm:ss" - const val YYYY_MM_DD_HH_MM_SS = "yyyy年MM月dd日 HH:mm:ss" - const val YYYY_MM_DD_HH_MM = "yyyy年MM月dd日 HH:mm" - const val YYYY_MM_DD = "yyyy年MM月dd日" - const val YYYY_MM = "yyyy年MM月" - const val YYYY = "yyyy年" - const val MM = "MM月" - const val DD = "dd日" - - /** - * Returns a date-time [String] format from a [Date] object. - */ - fun Date.toString(pattern: String, simpleDate: Boolean? = false): String { - return if (simpleDate == true) { - val format = if (pattern == YYYY_MM_DD) { - "" - } else { - SimpleDateFormat( - pattern.replace(YYYY_MM_DD, "") - ).format(this) - } - when (this.toString(YYYY_MM_DD)) { - Date().toString(YYYY_MM_DD) -> { - "今天${format}" - } - Calendar.getInstance().apply { - time = Date() - add(Calendar.DAY_OF_MONTH, -1) - }.time.toString(YYYY_MM_DD) -> { - "昨天${format}" - } - else -> SimpleDateFormat(pattern).format(this) - } - } else { - SimpleDateFormat(pattern).format(this) - } - } - - /** - * Returns a [Date] object parsed from a date-time [String]. - */ - fun String.toDate(pattern: String? = null): Date = - SimpleDateFormat((pattern ?: YYYY_MM_DD_HH_MM_SS)).parse(this) -} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/constant/Filter.kt b/app/src/main/java/me/ash/reader/data/constant/Filter.kt index 96ebbc3..709e77c 100644 --- a/app/src/main/java/me/ash/reader/data/constant/Filter.kt +++ b/app/src/main/java/me/ash/reader/data/constant/Filter.kt @@ -8,8 +8,6 @@ import androidx.compose.ui.graphics.vector.ImageVector class Filter( var index: Int, - var name: String, - var description: String, var important: Int, var icon: ImageVector, ) { @@ -20,22 +18,16 @@ class Filter( companion object { val Starred = Filter( index = 0, - name = "Starred", - description = " Starred Items", important = 13, icon = Icons.Rounded.StarOutline, ) val Unread = Filter( index = 1, - name = "Unread", - description = " Unread Items", important = 666, icon = Icons.Outlined.FiberManualRecord, ) val All = Filter( index = 2, - name = "All", - description = " Unread Items", important = 666, icon = Icons.Rounded.Subject, ) diff --git a/app/src/main/java/me/ash/reader/data/constant/NavigationBarItem.kt b/app/src/main/java/me/ash/reader/data/constant/NavigationBarItem.kt deleted file mode 100644 index 7b9c978..0000000 --- a/app/src/main/java/me/ash/reader/data/constant/NavigationBarItem.kt +++ /dev/null @@ -1,18 +0,0 @@ -package me.ash.reader.data.constant - -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.FiberManualRecord -import androidx.compose.material.icons.rounded.Star -import androidx.compose.material.icons.rounded.Subject -import androidx.compose.ui.graphics.vector.ImageVector - -class NavigationBarItem( - var title: String, - var icon: ImageVector, -) { - companion object { - val Starred = NavigationBarItem("STARRED", Icons.Rounded.Star) - val Unread = NavigationBarItem("UNREAD", Icons.Rounded.FiberManualRecord) - val All = NavigationBarItem("ALL", Icons.Rounded.Subject) - } -} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/constant/Symbol.kt b/app/src/main/java/me/ash/reader/data/constant/Symbol.kt deleted file mode 100644 index e76aa4c..0000000 --- a/app/src/main/java/me/ash/reader/data/constant/Symbol.kt +++ /dev/null @@ -1,8 +0,0 @@ -package me.ash.reader.data.constant - -object Symbol { - const val NOTHING: String = "Null" - const val Unknown: String = "Unknown" - const val NOTIFICATION_CHANNEL_GROUP_ARTICLE_UPDATE: String = "article.update" - const val EXTRA_ARTICLE_ID: String = "article.id" -} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/repository/AccountRepository.kt b/app/src/main/java/me/ash/reader/data/repository/AccountRepository.kt index 25c1bfc..a799ff5 100644 --- a/app/src/main/java/me/ash/reader/data/repository/AccountRepository.kt +++ b/app/src/main/java/me/ash/reader/data/repository/AccountRepository.kt @@ -2,14 +2,11 @@ package me.ash.reader.data.repository import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext -import me.ash.reader.DataStoreKeys +import me.ash.reader.* import me.ash.reader.data.account.Account import me.ash.reader.data.account.AccountDao import me.ash.reader.data.group.Group import me.ash.reader.data.group.GroupDao -import me.ash.reader.dataStore -import me.ash.reader.get -import me.ash.reader.spacerDollar import javax.inject.Inject class AccountRepository @Inject constructor( @@ -29,8 +26,10 @@ class AccountRepository @Inject constructor( } suspend fun addDefaultAccount(): Account { + val readYouString = context.getString(R.string.read_you) + val defaultString = context.getString(R.string.defaults) return Account( - name = "Read You", + name = readYouString, type = Account.Type.LOCAL, ).apply { id = accountDao.insert(this).toInt() @@ -38,8 +37,8 @@ class AccountRepository @Inject constructor( if (groupDao.queryAll(it.id!!).isEmpty()) { groupDao.insert( Group( - id = it.id!!.spacerDollar("0"), - name = "默认", + id = it.id!!.spacerDollar(readYouString + defaultString), + name = defaultString, accountId = it.id!!, ) ) diff --git a/app/src/main/java/me/ash/reader/data/repository/LocalRssRepository.kt b/app/src/main/java/me/ash/reader/data/repository/LocalRssRepository.kt index a7d7dcd..8988612 100644 --- a/app/src/main/java/me/ash/reader/data/repository/LocalRssRepository.kt +++ b/app/src/main/java/me/ash/reader/data/repository/LocalRssRepository.kt @@ -19,11 +19,12 @@ import me.ash.reader.* import me.ash.reader.data.account.AccountDao import me.ash.reader.data.article.Article import me.ash.reader.data.article.ArticleDao -import me.ash.reader.data.constant.Symbol import me.ash.reader.data.feed.Feed import me.ash.reader.data.feed.FeedDao import me.ash.reader.data.group.GroupDao import me.ash.reader.data.source.RssNetworkDataSource +import me.ash.reader.ui.page.common.ExtraName +import me.ash.reader.ui.page.common.NotificationGroupName import java.util.* import javax.inject.Inject @@ -119,7 +120,7 @@ class LocalRssRepository @Inject constructor( if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { notificationManager.createNotificationChannel( NotificationChannel( - Symbol.NOTIFICATION_CHANNEL_GROUP_ARTICLE_UPDATE, + NotificationGroupName.ARTICLE_UPDATE, "文章更新", NotificationManager.IMPORTANCE_DEFAULT ) @@ -132,9 +133,9 @@ class LocalRssRepository @Inject constructor( if (feedNotificationMap[article.feedId] == true) { val builder = NotificationCompat.Builder( context, - Symbol.NOTIFICATION_CHANNEL_GROUP_ARTICLE_UPDATE + NotificationGroupName.ARTICLE_UPDATE ).setSmallIcon(R.drawable.ic_launcher_foreground) - .setGroup(Symbol.NOTIFICATION_CHANNEL_GROUP_ARTICLE_UPDATE) + .setGroup(NotificationGroupName.ARTICLE_UPDATE) .setContentTitle(article.title) .setContentText(article.shortDescription) .setPriority(NotificationCompat.PRIORITY_DEFAULT) @@ -146,7 +147,7 @@ class LocalRssRepository @Inject constructor( flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK putExtra( - Symbol.EXTRA_ARTICLE_ID, + ExtraName.ARTICLE_ID, ids[index].toInt() ) }, diff --git a/app/src/main/java/me/ash/reader/data/repository/StringsRepository.kt b/app/src/main/java/me/ash/reader/data/repository/StringsRepository.kt new file mode 100644 index 0000000..0d64845 --- /dev/null +++ b/app/src/main/java/me/ash/reader/data/repository/StringsRepository.kt @@ -0,0 +1,12 @@ +package me.ash.reader.data.repository + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject + +class StringsRepository @Inject constructor( + @ApplicationContext + private val context: Context, +) { + fun getString(resId: Int) = context.getString(resId) +} diff --git a/app/src/main/java/me/ash/reader/ui/extension/FilterExt.kt b/app/src/main/java/me/ash/reader/ui/extension/FilterExt.kt new file mode 100644 index 0000000..2622425 --- /dev/null +++ b/app/src/main/java/me/ash/reader/ui/extension/FilterExt.kt @@ -0,0 +1,20 @@ +package me.ash.reader.ui.extension + +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import me.ash.reader.R +import me.ash.reader.data.constant.Filter + +@Composable +fun Filter.getName(): String = when (this) { + Filter.Unread -> stringResource(R.string.unread) + Filter.Starred -> stringResource(R.string.starred) + else -> stringResource(R.string.all) +} + +@Composable +fun Filter.getDesc(): String = when (this) { + Filter.Unread -> stringResource(R.string.unread_desc, this.important) + Filter.Starred -> stringResource(R.string.starred_desc, this.important) + else -> stringResource(R.string.unread_desc, this.important) +} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/page/common/ExtraName.kt b/app/src/main/java/me/ash/reader/ui/page/common/ExtraName.kt new file mode 100644 index 0000000..87abe5a --- /dev/null +++ b/app/src/main/java/me/ash/reader/ui/page/common/ExtraName.kt @@ -0,0 +1,5 @@ +package me.ash.reader.ui.page.common + +object ExtraName { + const val ARTICLE_ID: String = "article.id" +} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/page/common/NotificationGroupName.kt b/app/src/main/java/me/ash/reader/ui/page/common/NotificationGroupName.kt new file mode 100644 index 0000000..df5c194 --- /dev/null +++ b/app/src/main/java/me/ash/reader/ui/page/common/NotificationGroupName.kt @@ -0,0 +1,5 @@ +package me.ash.reader.ui.page.common + +object NotificationGroupName { + const val ARTICLE_UPDATE: String = "article.update" +} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/page/home/HomeBottomNavBar.kt b/app/src/main/java/me/ash/reader/ui/page/home/HomeBottomNavBar.kt index e7c1062..f36ebae 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/HomeBottomNavBar.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/HomeBottomNavBar.kt @@ -29,13 +29,15 @@ import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.google.accompanist.pager.ExperimentalPagerApi import com.google.accompanist.pager.PagerState +import me.ash.reader.R import me.ash.reader.data.constant.Filter -import me.ash.reader.data.constant.NavigationBarItem +import me.ash.reader.ui.extension.getName import me.ash.reader.ui.widget.CanBeDisabledIconButton import kotlin.math.absoluteValue @@ -153,10 +155,10 @@ private fun FilterBar( verticalAlignment = Alignment.CenterVertically, ) { listOf( - NavigationBarItem.Starred, - NavigationBarItem.Unread, - NavigationBarItem.All - ).forEachIndexed { index, item -> + Filter.Starred, + Filter.Unread, + Filter.All + ).forEach { item -> Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center, @@ -175,39 +177,33 @@ private fun FilterBar( .clip(CircleShape) .clickable(onClick = { view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) - onSelected( - when (index) { - 0 -> Filter.Starred - 1 -> Filter.Unread - else -> Filter.All - } - ) + onSelected(item) }) .background( - if (filter.index == index) { + if (filter == item) { MaterialTheme.colorScheme.inverseOnSurface } else { Color.Unspecified } ) ) { - if (filter.index == index) { + if (filter == item) { Spacer(modifier = Modifier.width(10.dp)) Icon( modifier = Modifier.size( - if (Filter.Unread.index == index) { - 15 + if (filter == item) { + 15.dp } else { - 19 - }.dp + 19.dp + } ), imageVector = item.icon, - contentDescription = item.title, + contentDescription = item.getName(), tint = MaterialTheme.colorScheme.primary, ) Spacer(modifier = Modifier.width(4.dp)) Text( - text = item.title, + text = item.getName().uppercase(), fontSize = 11.sp, fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.primary, @@ -216,14 +212,14 @@ private fun FilterBar( } else { Icon( modifier = Modifier.size( - if (Filter.Unread.index == index) { + if (item.isUnread()) { 15 } else { 19 }.dp ), imageVector = item.icon, - contentDescription = item.title, + contentDescription = item.getName(), tint = MaterialTheme.colorScheme.outline, ) } @@ -261,7 +257,7 @@ private fun ReaderBar( } else { Icons.Outlined.Circle }, - contentDescription = "Mark Unread", + contentDescription = stringResource(if (isUnread) R.string.mark_as_read else R.string.mark_as_unread), tint = MaterialTheme.colorScheme.primary, ) { view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) @@ -275,7 +271,7 @@ private fun ReaderBar( } else { Icons.Rounded.StarBorder }, - contentDescription = "Starred", + contentDescription = stringResource(if (isStarred) R.string.mark_as_unstar else R.string.mark_as_starred), tint = MaterialTheme.colorScheme.primary, ) { view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) @@ -306,7 +302,7 @@ private fun ReaderBar( } else { Icons.Outlined.Article }, - contentDescription = "Full Content Parsing", + contentDescription = stringResource(R.string.parse_full_content), tint = MaterialTheme.colorScheme.primary, ) { view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) diff --git a/app/src/main/java/me/ash/reader/ui/page/home/HomePage.kt b/app/src/main/java/me/ash/reader/ui/page/home/HomePage.kt index 4cd3a91..67723d7 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/HomePage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/HomePage.kt @@ -16,9 +16,10 @@ import androidx.navigation.NavHostController import com.google.accompanist.pager.ExperimentalPagerApi import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch -import me.ash.reader.data.constant.Symbol +import me.ash.reader.ui.page.common.NotificationGroupName import me.ash.reader.ui.extension.collectAsStateValue import me.ash.reader.ui.extension.findActivity +import me.ash.reader.ui.page.common.ExtraName import me.ash.reader.ui.page.home.feeds.FeedsPage import me.ash.reader.ui.page.home.flow.FlowPage import me.ash.reader.ui.page.home.read.ReadPage @@ -42,7 +43,7 @@ fun HomePage( LaunchedEffect(Unit) { context.findActivity()?.let { activity -> activity.intent?.let { intent -> - intent.extras?.get(Symbol.EXTRA_ARTICLE_ID)?.let { + intent.extras?.get(ExtraName.ARTICLE_ID)?.let { readViewModel.dispatch(ReadViewAction.ScrollToItem(2)) scope.launch { val article = @@ -60,7 +61,7 @@ fun HomePage( ) } } - intent.extras?.remove(Symbol.EXTRA_ARTICLE_ID) + intent.extras?.remove(ExtraName.ARTICLE_ID) } } } diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt index edf6afe..5ee2c3c 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt @@ -19,12 +19,15 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavHostController import kotlinx.coroutines.flow.collect -import me.ash.reader.data.constant.Symbol +import me.ash.reader.R import me.ash.reader.ui.extension.collectAsStateValue +import me.ash.reader.ui.extension.getDesc +import me.ash.reader.ui.extension.getName import me.ash.reader.ui.page.home.HomeViewAction import me.ash.reader.ui.page.home.HomeViewModel import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeDialog @@ -77,7 +80,7 @@ fun FeedsPage( IconButton(onClick = { }) { Icon( imageVector = Icons.Rounded.ArrowBack, - contentDescription = "Back", + contentDescription = stringResource(R.string.back), tint = MaterialTheme.colorScheme.onSurface ) } @@ -90,7 +93,7 @@ fun FeedsPage( Icon( modifier = Modifier.rotate(if (syncState.isSyncing) angle else 0f), imageVector = Icons.Rounded.Refresh, - contentDescription = "Refresh", + contentDescription = stringResource(R.string.refresh), tint = MaterialTheme.colorScheme.onSurface, ) } @@ -99,7 +102,7 @@ fun FeedsPage( }) { Icon( imageVector = Icons.Rounded.Add, - contentDescription = "Subscribe", + contentDescription = stringResource(R.string.subscribe), tint = MaterialTheme.colorScheme.onSurface, ) } @@ -121,20 +124,20 @@ fun FeedsPage( end = 24.dp, bottom = 24.dp ), - text = viewState.account?.name ?: Symbol.Unknown, + text = viewState.account?.name ?: stringResource(R.string.unknown), style = MaterialTheme.typography.displaySmall, color = MaterialTheme.colorScheme.onSurface, ) } item { Banner( - title = viewState.filter.name, - desc = "${viewState.filter.important}${viewState.filter.description}", + title = filterState.filter.getName(), + desc = filterState.filter.getDesc(), icon = viewState.filter.icon, action = { Icon( imageVector = Icons.Outlined.KeyboardArrowRight, - contentDescription = "Goto", + contentDescription = stringResource(R.string.go_to), tint = MaterialTheme.colorScheme.onSurface, ) }, @@ -159,7 +162,7 @@ fun FeedsPage( Spacer(modifier = Modifier.height(24.dp)) Subtitle( modifier = Modifier.padding(start = 28.dp), - text = "Feeds" + text = stringResource(R.string.feeds) ) Spacer(modifier = Modifier.height(8.dp)) } diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/GroupItem.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/GroupItem.kt index 13a0cea..562bce9 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/GroupItem.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/GroupItem.kt @@ -16,7 +16,9 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import me.ash.reader.R import me.ash.reader.data.feed.Feed @Composable @@ -37,7 +39,7 @@ fun GroupItem( .clip(RoundedCornerShape(32.dp)) .background(MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.14f)) .clickable { groupOnClick() } - .padding(vertical = 22.dp) + .padding(top = 22.dp) ) { Row( modifier = modifier.fillMaxWidth(), @@ -64,12 +66,12 @@ fun GroupItem( ) { Icon( imageVector = if (expanded) Icons.Rounded.ExpandLess else Icons.Rounded.ExpandMore, - contentDescription = if (expanded) "Expand Less" else "Expand More", + contentDescription = stringResource(if (expanded) R.string.expand_less else R.string.expand_more), tint = MaterialTheme.colorScheme.onSecondaryContainer, ) } } - + Spacer(modifier = Modifier.height(22.dp)) AnimatedVisibility( visible = expanded, enter = fadeIn() + expandVertically(), diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/ResultViewPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/ResultViewPage.kt index 20777fb..bcafcdc 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/ResultViewPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/ResultViewPage.kt @@ -13,9 +13,11 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.google.accompanist.flowlayout.FlowRow import com.google.accompanist.flowlayout.MainAxisAlignment +import me.ash.reader.R import me.ash.reader.data.group.Group import me.ash.reader.ui.widget.SelectionChip import me.ash.reader.ui.widget.SelectionEditorChip @@ -25,11 +27,11 @@ import me.ash.reader.ui.widget.Subtitle fun ResultViewPage( link: String = "", groups: List = emptyList(), - selectedNotificationPreset: Boolean = false, - selectedFullContentParsePreset: Boolean = false, + selectedAllowNotificationPreset: Boolean = false, + selectedParseFullContentPreset: Boolean = false, selectedGroupId: String = "", - notificationPresetOnClick: () -> Unit = {}, - fullContentParsePresetOnClick: () -> Unit = {}, + allowNotificationPresetOnClick: () -> Unit = {}, + parseFullContentPresetOnClick: () -> Unit = {}, groupOnClick: (groupId: String) -> Unit = {}, onKeyboardAction: () -> Unit = {}, ) { @@ -42,10 +44,10 @@ fun ResultViewPage( Spacer(modifier = Modifier.height(26.dp)) Preset( - selectedNotificationPreset = selectedNotificationPreset, - selectedFullContentParsePreset = selectedFullContentParsePreset, - notificationPresetOnClick = notificationPresetOnClick, - fullContentParsePresetOnClick = fullContentParsePresetOnClick, + selectedAllowNotificationPreset = selectedAllowNotificationPreset, + selectedParseFullContentPreset = selectedParseFullContentPreset, + allowNotificationPresetOnClick = allowNotificationPresetOnClick, + parseFullContentPresetOnClick = parseFullContentPresetOnClick, ) Spacer(modifier = Modifier.height(26.dp)) @@ -78,12 +80,12 @@ private fun Link( @Composable private fun Preset( - selectedNotificationPreset: Boolean = false, - selectedFullContentParsePreset: Boolean = false, - notificationPresetOnClick: () -> Unit = {}, - fullContentParsePresetOnClick: () -> Unit = {}, + selectedAllowNotificationPreset: Boolean = false, + selectedParseFullContentPreset: Boolean = false, + allowNotificationPresetOnClick: () -> Unit = {}, + parseFullContentPresetOnClick: () -> Unit = {}, ) { - Subtitle(text = "预设") + Subtitle(text = stringResource(R.string.preset)) Spacer(modifier = Modifier.height(10.dp)) FlowRow( mainAxisAlignment = MainAxisAlignment.Start, @@ -92,35 +94,35 @@ private fun Preset( ) { SelectionChip( modifier = Modifier.animateContentSize(), - content = "接收通知", - selected = selectedNotificationPreset, + content = stringResource(R.string.allow_notification), + selected = selectedAllowNotificationPreset, selectedIcon = { Icon( imageVector = Icons.Outlined.Notifications, - contentDescription = "Check", + contentDescription = stringResource(R.string.allow_notification), modifier = Modifier .padding(start = 8.dp) .size(18.dp), ) }, ) { - notificationPresetOnClick() + allowNotificationPresetOnClick() } SelectionChip( modifier = Modifier.animateContentSize(), - content = "全文解析", - selected = selectedFullContentParsePreset, + content = stringResource(R.string.parse_full_content), + selected = selectedParseFullContentPreset, selectedIcon = { Icon( imageVector = Icons.Outlined.Article, - contentDescription = "Check", + contentDescription = stringResource(R.string.parse_full_content), modifier = Modifier .padding(start = 8.dp) .size(18.dp), ) }, ) { - fullContentParsePresetOnClick() + parseFullContentPresetOnClick() } } } @@ -132,7 +134,7 @@ private fun AddToGroup( groupOnClick: (groupId: String) -> Unit = {}, onKeyboardAction: () -> Unit = {}, ) { - Subtitle(text = "添加到组") + Subtitle(text = stringResource(R.string.add_to_group)) Spacer(modifier = Modifier.height(10.dp)) FlowRow( mainAxisAlignment = MainAxisAlignment.Start, @@ -151,7 +153,7 @@ private fun AddToGroup( SelectionEditorChip( modifier = Modifier.animateContentSize(), - content = "新建分组", + content = stringResource(R.string.new_group), selected = false, onKeyboardAction = onKeyboardAction, ) { diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SearchViewPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SearchViewPage.kt index bd3fcbb..29bbf77 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SearchViewPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SearchViewPage.kt @@ -22,11 +22,17 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import com.google.accompanist.pager.ExperimentalPagerApi +import com.google.accompanist.pager.PagerState import kotlinx.coroutines.delay +import me.ash.reader.R +@OptIn(ExperimentalPagerApi::class) @Composable fun SearchViewPage( + pagerState: PagerState, inputContent: String = "", errorMessage: String = "", onValueChange: (String) -> Unit = {}, @@ -51,11 +57,11 @@ fun SearchViewPage( ), value = inputContent, onValueChange = { - onValueChange(it) + if (pagerState.currentPage == 0) onValueChange(it) }, placeholder = { Text( - text = "订阅源或站点链接", + text = stringResource(R.string.feed_or_site_url), color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f) ) }, @@ -68,7 +74,7 @@ fun SearchViewPage( }) { Icon( imageVector = Icons.Rounded.Close, - contentDescription = "Clear", + contentDescription = stringResource(R.string.clear), tint = MaterialTheme.colorScheme.outline.copy(alpha = 0.5f), ) } @@ -77,7 +83,7 @@ fun SearchViewPage( }) { Icon( imageVector = Icons.Rounded.ContentPaste, - contentDescription = "Paste", + contentDescription = stringResource(R.string.paste), tint = MaterialTheme.colorScheme.primary ) } diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeDialog.kt index 6b5cb3b..6ddbbda 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeDialog.kt @@ -11,12 +11,11 @@ import androidx.compose.material3.TextButton import androidx.compose.runtime.* import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import com.google.accompanist.pager.ExperimentalPagerApi -import me.ash.reader.DataStoreKeys -import me.ash.reader.dataStore -import me.ash.reader.get -import me.ash.reader.spacerDollar +import me.ash.reader.* +import me.ash.reader.R import me.ash.reader.ui.extension.collectAsStateValue import me.ash.reader.ui.widget.Dialog import java.io.InputStream @@ -61,14 +60,14 @@ fun SubscribeDialog( icon = { Icon( imageVector = Icons.Rounded.RssFeed, - contentDescription = "Subscribe", + contentDescription = stringResource(R.string.subscribe), ) }, title = { Text( when (viewState.pagerState.currentPage) { - 0 -> "订阅" - else -> viewState.feed?.name ?: "未知" + 0 -> stringResource(R.string.subscribe) + else -> viewState.feed?.name ?: stringResource(R.string.unknown) } ) }, @@ -88,15 +87,15 @@ fun SubscribeDialog( }, link = viewState.inputContent, groups = groupsState.value, - selectedNotificationPreset = viewState.notificationPreset, - selectedFullContentParsePreset = viewState.fullContentParsePreset, + selectedAllowNotificationPreset = viewState.allowNotificationPreset, + selectedParseFullContentPreset = viewState.parseFullContentPreset, selectedGroupId = viewState.selectedGroupId, pagerState = viewState.pagerState, - notificationPresetOnClick = { - viewModel.dispatch(SubscribeViewAction.ChangeNotificationPreset) + allowNotificationPresetOnClick = { + viewModel.dispatch(SubscribeViewAction.ChangeAllowNotificationPreset) }, - fullContentParsePresetOnClick = { - viewModel.dispatch(SubscribeViewAction.ChangeFullContentParsePreset) + parseFullContentPresetOnClick = { + viewModel.dispatch(SubscribeViewAction.ChangeParseFullContentPreset) }, groupOnClick = { viewModel.dispatch(SubscribeViewAction.SelectedGroup(it)) @@ -116,7 +115,7 @@ fun SubscribeDialog( } ) { Text( - text = "搜索", + text = stringResource(R.string.search), color = if (viewState.inputContent.isNotEmpty()) { Color.Unspecified } else { @@ -131,7 +130,7 @@ fun SubscribeDialog( viewModel.dispatch(SubscribeViewAction.Subscribe) } ) { - Text("订阅") + Text(stringResource(R.string.subscribe)) } } } @@ -145,7 +144,7 @@ fun SubscribeDialog( viewModel.dispatch(SubscribeViewAction.Hide) } ) { - Text("导入OPML文件") + Text(text = stringResource(R.string.import_from_opml)) } } 1 -> { @@ -154,7 +153,7 @@ fun SubscribeDialog( viewModel.dispatch(SubscribeViewAction.Hide) } ) { - Text("取消") + Text(text = stringResource(R.string.cancel)) } } } diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeViewModel.kt index 7b85cea..fee275f 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeViewModel.kt @@ -9,12 +9,13 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch +import me.ash.reader.R import me.ash.reader.data.article.Article -import me.ash.reader.data.constant.Symbol import me.ash.reader.data.feed.Feed import me.ash.reader.data.group.Group import me.ash.reader.data.repository.RssHelper import me.ash.reader.data.repository.RssRepository +import me.ash.reader.data.repository.StringsRepository import me.ash.reader.formatUrl import me.ash.reader.ui.extension.animateScrollToPage import javax.inject.Inject @@ -24,9 +25,10 @@ import javax.inject.Inject class SubscribeViewModel @Inject constructor( private val rssRepository: RssRepository, private val rssHelper: RssHelper, + private val stringsRepository: StringsRepository, ) : ViewModel() { - private val _viewState = MutableStateFlow(SubScribeViewState()) - val viewState: StateFlow = _viewState.asStateFlow() + private val _viewState = MutableStateFlow(SubscribeViewState()) + val viewState: StateFlow = _viewState.asStateFlow() fun dispatch(action: SubscribeViewAction) { when (action) { @@ -36,10 +38,10 @@ class SubscribeViewModel @Inject constructor( is SubscribeViewAction.Hide -> changeVisible(false) is SubscribeViewAction.Input -> inputLink(action.content) is SubscribeViewAction.Search -> search(action.scope) - is SubscribeViewAction.ChangeNotificationPreset -> - changeNotificationPreset() - is SubscribeViewAction.ChangeFullContentParsePreset -> - changeFullContentParsePreset() + is SubscribeViewAction.ChangeAllowNotificationPreset -> + changeAllowNotificationPreset() + is SubscribeViewAction.ChangeParseFullContentPreset -> + changeParseFullContentPreset() is SubscribeViewAction.SelectedGroup -> selectedGroup(action.groupId) is SubscribeViewAction.Subscribe -> subscribe() } @@ -48,6 +50,7 @@ class SubscribeViewModel @Inject constructor( private fun init() { _viewState.update { it.copy( + title = stringsRepository.getString(R.string.subscribe), groups = rssRepository.get().pullGroups() ) } @@ -57,13 +60,13 @@ class SubscribeViewModel @Inject constructor( _viewState.update { it.copy( visible = false, - title = "订阅", + title = stringsRepository.getString(R.string.subscribe), errorMessage = "", inputContent = "", feed = null, articles = emptyList(), - notificationPreset = false, - fullContentParsePreset = false, + allowNotificationPreset = false, + parseFullContentPreset = false, selectedGroupId = "", groups = emptyFlow(), ) @@ -78,8 +81,8 @@ class SubscribeViewModel @Inject constructor( rssRepository.get().subscribe( feed.copy( groupId = groupId, - isNotification = _viewState.value.notificationPreset, - isFullContent = _viewState.value.fullContentParsePreset, + isNotification = _viewState.value.allowNotificationPreset, + isFullContent = _viewState.value.parseFullContentPreset, ), articles ) changeVisible(false) @@ -94,18 +97,18 @@ class SubscribeViewModel @Inject constructor( } } - private fun changeFullContentParsePreset() { + private fun changeParseFullContentPreset() { _viewState.update { it.copy( - fullContentParsePreset = !_viewState.value.fullContentParsePreset + parseFullContentPreset = !_viewState.value.parseFullContentPreset ) } } - private fun changeNotificationPreset() { + private fun changeAllowNotificationPreset() { _viewState.update { it.copy( - notificationPreset = !_viewState.value.notificationPreset + allowNotificationPreset = !_viewState.value.allowNotificationPreset ) } } @@ -122,7 +125,7 @@ class SubscribeViewModel @Inject constructor( } _viewState.update { it.copy( - title = "搜索中", + title = stringsRepository.getString(R.string.searching), ) } viewModelScope.launch(Dispatchers.IO) { @@ -130,7 +133,7 @@ class SubscribeViewModel @Inject constructor( if (rssRepository.get().isExist(_viewState.value.inputContent)) { _viewState.update { it.copy( - errorMessage = "已订阅", + errorMessage = stringsRepository.getString(R.string.already_subscribed), ) } return@launch @@ -147,8 +150,8 @@ class SubscribeViewModel @Inject constructor( e.printStackTrace() _viewState.update { it.copy( - title = "订阅", - errorMessage = e.message ?: Symbol.Unknown, + title = stringsRepository.getString(R.string.subscribe), + errorMessage = e.message ?: stringsRepository.getString(R.string.unknown), ) } } @@ -173,15 +176,15 @@ class SubscribeViewModel @Inject constructor( } @OptIn(ExperimentalPagerApi::class) -data class SubScribeViewState( +data class SubscribeViewState( val visible: Boolean = false, - val title: String = "订阅", + val title: String = "", val errorMessage: String = "", val inputContent: String = "", val feed: Feed? = null, val articles: List
= emptyList(), - val notificationPreset: Boolean = false, - val fullContentParsePreset: Boolean = false, + val allowNotificationPreset: Boolean = false, + val parseFullContentPreset: Boolean = false, val selectedGroupId: String = "", val groups: Flow> = emptyFlow(), val pagerState: PagerState = PagerState(), @@ -202,8 +205,8 @@ sealed class SubscribeViewAction { val scope: CoroutineScope, ) : SubscribeViewAction() - object ChangeNotificationPreset : SubscribeViewAction() - object ChangeFullContentParsePreset : SubscribeViewAction() + object ChangeAllowNotificationPreset : SubscribeViewAction() + object ChangeParseFullContentPreset : SubscribeViewAction() data class SelectedGroup( val groupId: String diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeViewPager.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeViewPager.kt index eb97173..0da7f18 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeViewPager.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeViewPager.kt @@ -19,12 +19,12 @@ fun SubscribeViewPager( onSearchKeyboardAction: () -> Unit = {}, link: String = "", groups: List = emptyList(), - selectedNotificationPreset: Boolean = false, - selectedFullContentParsePreset: Boolean = false, + selectedAllowNotificationPreset: Boolean = false, + selectedParseFullContentPreset: Boolean = false, selectedGroupId: String = "", pagerState: PagerState = com.google.accompanist.pager.rememberPagerState(), - notificationPresetOnClick: () -> Unit = {}, - fullContentParsePresetOnClick: () -> Unit = {}, + allowNotificationPresetOnClick: () -> Unit = {}, + parseFullContentPresetOnClick: () -> Unit = {}, groupOnClick: (groupId: String) -> Unit = {}, onResultKeyboardAction: () -> Unit = {}, ) { @@ -35,6 +35,7 @@ fun SubscribeViewPager( composableList = listOf( { SearchViewPage( + pagerState = pagerState, inputContent = inputContent, errorMessage = errorMessage, onValueChange = onValueChange, @@ -45,11 +46,11 @@ fun SubscribeViewPager( ResultViewPage( link = link, groups = groups, - selectedNotificationPreset = selectedNotificationPreset, - selectedFullContentParsePreset = selectedFullContentParsePreset, + selectedAllowNotificationPreset = selectedAllowNotificationPreset, + selectedParseFullContentPreset = selectedParseFullContentPreset, selectedGroupId = selectedGroupId, - notificationPresetOnClick = notificationPresetOnClick, - fullContentParsePresetOnClick = fullContentParsePresetOnClick, + allowNotificationPresetOnClick = allowNotificationPresetOnClick, + parseFullContentPresetOnClick = parseFullContentPresetOnClick, groupOnClick = groupOnClick, onKeyboardAction = onResultKeyboardAction, ) diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt index 199640a..447c7b2 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt @@ -12,11 +12,11 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import me.ash.reader.DateTimeExt -import me.ash.reader.DateTimeExt.toString import me.ash.reader.data.article.ArticleWithFeed +import me.ash.reader.formatToString @Composable fun ArticleItem( @@ -24,6 +24,7 @@ fun ArticleItem( articleWithFeed: ArticleWithFeed, onClick: (ArticleWithFeed) -> Unit = {}, ) { + val context = LocalContext.current Column( modifier = Modifier .padding(horizontal = 12.dp, vertical = 8.dp) @@ -44,7 +45,7 @@ fun ArticleItem( style = MaterialTheme.typography.labelMedium, ) Text( - text = articleWithFeed.article.date.toString(DateTimeExt.HH_MM), + text = articleWithFeed.article.date.formatToString(context, onlyHourMinute = true), color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f), style = MaterialTheme.typography.labelMedium, ) diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleList.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleList.kt index 8a4020d..9cc2727 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleList.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleList.kt @@ -1,5 +1,6 @@ package me.ash.reader.ui.page.home.flow +import android.content.Context import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height @@ -8,9 +9,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.paging.compose.LazyPagingItems import kotlinx.coroutines.CoroutineScope -import me.ash.reader.DateTimeExt -import me.ash.reader.DateTimeExt.toString import me.ash.reader.data.article.ArticleWithFeed +import me.ash.reader.formatToString import me.ash.reader.ui.page.home.HomeViewAction import me.ash.reader.ui.page.home.HomeViewModel import me.ash.reader.ui.page.home.read.ReadViewAction @@ -18,6 +18,7 @@ import me.ash.reader.ui.page.home.read.ReadViewModel @OptIn(ExperimentalFoundationApi::class) fun LazyListScope.generateArticleList( + context: Context, pagingItems: LazyPagingItems?, readViewModel: ReadViewModel, homeViewModel: HomeViewModel, @@ -27,8 +28,7 @@ fun LazyListScope.generateArticleList( var lastItemDay: String? = null for (itemIndex in 0 until pagingItems.itemCount) { val currentItem = pagingItems.peek(itemIndex) ?: continue - val currentItemDay = currentItem.article.date - .toString(DateTimeExt.YYYY_MM_DD, true) + val currentItemDay = currentItem.article.date.formatToString(context) if (lastItemDay != currentItemDay) { if (itemIndex != 0) { item { Spacer(modifier = Modifier.height(40.dp)) } diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt index 8b5c4ac..27b2ae0 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt @@ -14,13 +14,17 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect 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.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavHostController import androidx.paging.compose.collectAsLazyPagingItems import kotlinx.coroutines.flow.collect +import me.ash.reader.R import me.ash.reader.ui.extension.collectAsStateValue +import me.ash.reader.ui.extension.getName import me.ash.reader.ui.page.home.HomeViewAction import me.ash.reader.ui.page.home.HomeViewModel import me.ash.reader.ui.page.home.read.ReadViewModel @@ -37,6 +41,7 @@ fun FlowPage( homeViewModel: HomeViewModel = hiltViewModel(), readViewModel: ReadViewModel = hiltViewModel(), ) { + val context = LocalContext.current val scope = rememberCoroutineScope() val viewState = viewModel.viewState.collectAsStateValue() val filterState = homeViewModel.filterState.collectAsStateValue() @@ -66,7 +71,7 @@ fun FlowPage( }) { Icon( imageVector = Icons.Rounded.ArrowBack, - contentDescription = "Back", + contentDescription = stringResource(R.string.back), tint = MaterialTheme.colorScheme.onSurface ) } @@ -75,14 +80,14 @@ fun FlowPage( IconButton(onClick = {}) { Icon( imageVector = Icons.Rounded.DoneAll, - contentDescription = "Read All", + contentDescription = stringResource(R.string.mark_all_as_read), tint = MaterialTheme.colorScheme.onSurface, ) } IconButton(onClick = {}) { Icon( imageVector = Icons.Rounded.Search, - contentDescription = "Search", + contentDescription = stringResource(R.string.search), tint = MaterialTheme.colorScheme.onSurface, ) } @@ -106,7 +111,7 @@ fun FlowPage( text = when { filterState.group != null -> filterState.group.name filterState.feed != null -> filterState.feed.name - else -> filterState.filter.name + else -> filterState.filter.getName() }, style = MaterialTheme.typography.displaySmall, color = MaterialTheme.colorScheme.onSurface, @@ -114,7 +119,7 @@ fun FlowPage( overflow = TextOverflow.Ellipsis, ) } - generateArticleList(pagingItems, readViewModel, homeViewModel, scope) + generateArticleList(context, pagingItems, readViewModel, homeViewModel, scope) } } ) diff --git a/app/src/main/java/me/ash/reader/ui/page/home/read/Header.kt b/app/src/main/java/me/ash/reader/ui/page/home/read/Header.kt index bbcdcae..3587ac4 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/read/Header.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/read/Header.kt @@ -11,10 +11,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import me.ash.reader.DateTimeExt -import me.ash.reader.DateTimeExt.toString import me.ash.reader.data.article.Article import me.ash.reader.data.feed.Feed +import me.ash.reader.formatToString import me.ash.reader.ui.extension.roundClick @Composable @@ -34,7 +33,7 @@ fun Header( ) { Column(modifier = Modifier.padding(10.dp)) { Text( - text = article.date.toString(DateTimeExt.YYYY_MM_DD_HH_MM, true), + text = article.date.formatToString(context, atHourMinute = true), fontSize = 12.sp, color = MaterialTheme.colorScheme.outline, fontWeight = FontWeight.Medium, diff --git a/app/src/main/java/me/ash/reader/ui/page/home/read/ReadPageTopBar.kt b/app/src/main/java/me/ash/reader/ui/page/home/read/ReadPageTopBar.kt index f1d0251..6753012 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/read/ReadPageTopBar.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/read/ReadPageTopBar.kt @@ -13,7 +13,9 @@ import androidx.compose.material3.SmallTopAppBar import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import me.ash.reader.R @Composable fun ReadPageTopBar( @@ -30,7 +32,7 @@ fun ReadPageTopBar( Icon( modifier = Modifier.size(28.dp), imageVector = Icons.Rounded.Close, - contentDescription = "Back", + contentDescription = stringResource(R.string.back), tint = MaterialTheme.colorScheme.primary ) } 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 30dee5b..0da50b3 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 @@ -14,10 +14,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavHostController +import me.ash.reader.R import me.ash.reader.ui.extension.paddingFixedHorizontal import me.ash.reader.ui.extension.roundClick import me.ash.reader.ui.widget.TopTitleBox @@ -49,7 +51,7 @@ fun SettingsPage( IconButton(onClick = { navController.popBackStack() }) { Icon( imageVector = Icons.Rounded.ArrowBackIosNew, - contentDescription = "Back", + contentDescription = stringResource(R.string.back), tint = MaterialTheme.colorScheme.primary ) } diff --git a/app/src/main/java/me/ash/reader/ui/widget/SelectionChip.kt b/app/src/main/java/me/ash/reader/ui/widget/SelectionChip.kt index b65400f..0ba6c4d 100644 --- a/app/src/main/java/me/ash/reader/ui/widget/SelectionChip.kt +++ b/app/src/main/java/me/ash/reader/ui/widget/SelectionChip.kt @@ -19,7 +19,9 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import me.ash.reader.R @OptIn(ExperimentalMaterialApi::class) @Composable @@ -33,7 +35,7 @@ fun SelectionChip( selectedIcon: @Composable () -> Unit = { Icon( imageVector = Icons.Rounded.Check, - contentDescription = "Check", + contentDescription = stringResource(R.string.selected), modifier = Modifier .padding(start = 8.dp) .size(18.dp) @@ -87,7 +89,7 @@ fun SelectionEditorChip( selectedIcon: @Composable () -> Unit = { Icon( imageVector = Icons.Rounded.Check, - contentDescription = "Check", + contentDescription = stringResource(R.string.selected), modifier = Modifier .padding(start = 8.dp) .size(16.dp) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml new file mode 100644 index 0000000..039940d --- /dev/null +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -0,0 +1,41 @@ + + Read You + 全部 + 共 %1$d 项 + 未读 + %1$d 项未读 + 已加星标 + %1$d 项已加星标 + 分组 + 收缩 + 展开 + 确认 + 取消 + 默认 + 未知 + 返回 + 转到 + 刷新 + 搜索 + 搜索中… + 订阅 + 已有订阅 + 清空 + 粘贴 + 订阅源或站点链接 + 导入 OPML 文件 + 预设 + 已选择 + 允许通知 + 全文解析 + 添加到组 + 新建分组 + 今天 + 昨天 + %1$s %2$s + 标记为已读 + 全部标记为已读 + 标记为未读 + 标记为已加星标 + 标记为未加星标 + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 92c30d0..a5c0279 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,41 @@ - Reader + Read You + All + %1$d All Items + Unread + %1$d Unread Items + Starred + %1$d Starred Items + Feeds + Expand Less + Expand More + Confirm + Cancel + Default + Unknown + Back + Goto + Refresh + Search + Searching… + Subscribe + Already subscribed + Clear + Paste + Feed or Site URL + Import from OPML + Preset + Selected + Allow Notification + Parse Full Content + Add to Group + New Group + Today + Yesterday + %1$s At %2$s + Mark as Read + Mark All as Read + Mark as Unread + Mark as Starred + Mark as Unstar \ No newline at end of file