From 72ce2e490205800904be2e49dc3336022b88f519 Mon Sep 17 00:00:00 2001 From: SkyD666 Date: Mon, 29 Apr 2024 00:02:06 +0800 Subject: [PATCH] [feature|optimize] Support hide empty Default feed group; relative time to "3 weeks ago" --- app/build.gradle.kts | 2 +- .../main/java/com/skyd/anivu/config/Const.kt | 1 + .../main/java/com/skyd/anivu/ext/DateExt.kt | 11 +++++- .../java/com/skyd/anivu/ext/PreferenceExt.kt | 2 + .../skyd/anivu/model/preference/Settings.kt | 4 ++ .../feed/HideEmptyDefaultPreference.kt | 26 +++++++++++++ .../skyd/anivu/model/repository/RssHelper.kt | 3 +- .../skyd/anivu/model/service/HttpService.kt | 3 +- .../adapter/proxy/DefaultGroup1Proxy.kt | 19 ++++++---- .../adapter/proxy/Feed1Proxy.kt | 4 +- .../skyd/anivu/ui/fragment/feed/FeedScreen.kt | 37 ++++++++++++------- .../settings/behavior/BehaviorFragment.kt | 21 +++++++++++ .../com/skyd/anivu/ui/local/LocalValue.kt | 4 +- app/src/main/res/values-zh-rCN/strings.xml | 13 +++++-- app/src/main/res/values/strings.xml | 9 +++-- 15 files changed, 124 insertions(+), 35 deletions(-) create mode 100644 app/src/main/java/com/skyd/anivu/model/preference/behavior/feed/HideEmptyDefaultPreference.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8f56bb89..0dfe32b8 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -21,7 +21,7 @@ android { minSdk = 24 targetSdk = 34 versionCode = 16 - versionName = "1.1-beta17" + versionName = "1.1-beta18" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/com/skyd/anivu/config/Const.kt b/app/src/main/java/com/skyd/anivu/config/Const.kt index 1b6a1d16..ca103c35 100644 --- a/app/src/main/java/com/skyd/anivu/config/Const.kt +++ b/app/src/main/java/com/skyd/anivu/config/Const.kt @@ -20,6 +20,7 @@ object Const { const val NIGHT_SCREEN_URL = "https://github.com/SkyD666/NightScreen" const val BASE_URL = "https://github.com/SkyD666/" + const val FAVICON_FETCH_URL = "https://besticon-demo.herokuapp.com/allicons.json" val TEMP_TORRENT_DIR = File(appContext.cacheDir.path, "Torrent").apply { if (!exists()) mkdirs() diff --git a/app/src/main/java/com/skyd/anivu/ext/DateExt.kt b/app/src/main/java/com/skyd/anivu/ext/DateExt.kt index aa9f0a97..36c0bbcb 100644 --- a/app/src/main/java/com/skyd/anivu/ext/DateExt.kt +++ b/app/src/main/java/com/skyd/anivu/ext/DateExt.kt @@ -21,7 +21,16 @@ fun Date.toDateTimeString( locale: Locale = Locale.getDefault() ): String { return if (context.dataStore.getOrDefault(DateStylePreference) == DateStylePreference.RELATIVE) { - DateUtils.getRelativeTimeSpanString(this.time, System.currentTimeMillis(), 0).toString() + val current = System.currentTimeMillis() + val delta = current - this.time + DateUtils.getRelativeTimeSpanString( + this.time, + current, + // "DateUtils.WEEK_IN_MILLIS <= .. <= DateUtils.WEEK_IN_MILLIS * 4" is 1~3 weeks ago + if (delta in DateUtils.WEEK_IN_MILLIS..DateUtils.WEEK_IN_MILLIS * 4) { + DateUtils.WEEK_IN_MILLIS + } else 0 + ).toString() } else { SimpleDateFormat .getDateTimeInstance(dateStyle, timeStyle, locale) diff --git a/app/src/main/java/com/skyd/anivu/ext/PreferenceExt.kt b/app/src/main/java/com/skyd/anivu/ext/PreferenceExt.kt index 56f551a2..1c66b623 100644 --- a/app/src/main/java/com/skyd/anivu/ext/PreferenceExt.kt +++ b/app/src/main/java/com/skyd/anivu/ext/PreferenceExt.kt @@ -11,6 +11,7 @@ import com.skyd.anivu.model.preference.appearance.feed.FeedGroupExpandPreference import com.skyd.anivu.model.preference.behavior.article.ArticleSwipeLeftActionPreference import com.skyd.anivu.model.preference.behavior.article.ArticleTapActionPreference import com.skyd.anivu.model.preference.behavior.article.DeduplicateTitleInDescPreference +import com.skyd.anivu.model.preference.behavior.feed.HideEmptyDefaultPreference fun Preferences.toSettings(): Settings { return Settings( @@ -28,5 +29,6 @@ fun Preferences.toSettings(): Settings { deduplicateTitleInDesc = DeduplicateTitleInDescPreference.fromPreferences(this), articleTapAction = ArticleTapActionPreference.fromPreferences(this), articleSwipeLeftAction = ArticleSwipeLeftActionPreference.fromPreferences(this), + hideEmptyDefault = HideEmptyDefaultPreference.fromPreferences(this), ) } diff --git a/app/src/main/java/com/skyd/anivu/model/preference/Settings.kt b/app/src/main/java/com/skyd/anivu/model/preference/Settings.kt index 65b6909c..f5537039 100644 --- a/app/src/main/java/com/skyd/anivu/model/preference/Settings.kt +++ b/app/src/main/java/com/skyd/anivu/model/preference/Settings.kt @@ -16,12 +16,14 @@ import com.skyd.anivu.model.preference.appearance.feed.FeedGroupExpandPreference import com.skyd.anivu.model.preference.behavior.article.ArticleSwipeLeftActionPreference import com.skyd.anivu.model.preference.behavior.article.ArticleTapActionPreference import com.skyd.anivu.model.preference.behavior.article.DeduplicateTitleInDescPreference +import com.skyd.anivu.model.preference.behavior.feed.HideEmptyDefaultPreference import com.skyd.anivu.ui.local.LocalArticleSwipeLeftAction import com.skyd.anivu.ui.local.LocalArticleTapAction import com.skyd.anivu.ui.local.LocalDarkMode import com.skyd.anivu.ui.local.LocalDateStyle import com.skyd.anivu.ui.local.LocalDeduplicateTitleInDesc import com.skyd.anivu.ui.local.LocalFeedGroupExpand +import com.skyd.anivu.ui.local.LocalHideEmptyDefault import com.skyd.anivu.ui.local.LocalIgnoreUpdateVersion import com.skyd.anivu.ui.local.LocalTextFieldStyle import com.skyd.anivu.ui.local.LocalTheme @@ -41,6 +43,7 @@ data class Settings( val deduplicateTitleInDesc: Boolean = DeduplicateTitleInDescPreference.default, val articleTapAction: String = ArticleTapActionPreference.default, val articleSwipeLeftAction: String = ArticleSwipeLeftActionPreference.default, + val hideEmptyDefault: Boolean = HideEmptyDefaultPreference.default, ) @Composable @@ -64,6 +67,7 @@ fun SettingsProvider( LocalDeduplicateTitleInDesc provides settings.deduplicateTitleInDesc, LocalArticleTapAction provides settings.articleTapAction, LocalArticleSwipeLeftAction provides settings.articleSwipeLeftAction, + LocalHideEmptyDefault provides settings.hideEmptyDefault, ) { content() } diff --git a/app/src/main/java/com/skyd/anivu/model/preference/behavior/feed/HideEmptyDefaultPreference.kt b/app/src/main/java/com/skyd/anivu/model/preference/behavior/feed/HideEmptyDefaultPreference.kt new file mode 100644 index 00000000..12e861d1 --- /dev/null +++ b/app/src/main/java/com/skyd/anivu/model/preference/behavior/feed/HideEmptyDefaultPreference.kt @@ -0,0 +1,26 @@ +package com.skyd.anivu.model.preference.behavior.feed + +import android.content.Context +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.booleanPreferencesKey +import com.skyd.anivu.base.BasePreference +import com.skyd.anivu.ext.dataStore +import com.skyd.anivu.ext.put +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +object HideEmptyDefaultPreference : BasePreference { + private const val HIDE_EMPTY_DEFAULT = "hideEmptyDefault" + override val default = true + + val key = booleanPreferencesKey(HIDE_EMPTY_DEFAULT) + + fun put(context: Context, scope: CoroutineScope, value: Boolean) { + scope.launch(Dispatchers.IO) { + context.dataStore.put(key, value) + } + } + + override fun fromPreferences(preferences: Preferences): Boolean = preferences[key] ?: default +} \ No newline at end of file diff --git a/app/src/main/java/com/skyd/anivu/model/repository/RssHelper.kt b/app/src/main/java/com/skyd/anivu/model/repository/RssHelper.kt index f2d5ac9b..e970171d 100644 --- a/app/src/main/java/com/skyd/anivu/model/repository/RssHelper.kt +++ b/app/src/main/java/com/skyd/anivu/model/repository/RssHelper.kt @@ -1,6 +1,7 @@ package com.skyd.anivu.model.repository import android.util.Log +import androidx.compose.ui.util.fastMaxBy import com.rometools.rome.feed.synd.SyndEntry import com.rometools.rome.io.SyndFeedInput import com.rometools.rome.io.XmlReader @@ -114,7 +115,7 @@ class RssHelper @Inject constructor( return runCatching { retrofit.create(HttpService::class.java) .requestFavicon(url) - .icons?.firstOrNull { it.width != null && it.width >= 20 }?.url + .icons?.fastMaxBy { it.width ?: 0 }?.url }.onFailure { it.printStackTrace() }.getOrNull() } diff --git a/app/src/main/java/com/skyd/anivu/model/service/HttpService.kt b/app/src/main/java/com/skyd/anivu/model/service/HttpService.kt index 0f6118d6..380c8a15 100644 --- a/app/src/main/java/com/skyd/anivu/model/service/HttpService.kt +++ b/app/src/main/java/com/skyd/anivu/model/service/HttpService.kt @@ -1,5 +1,6 @@ package com.skyd.anivu.model.service +import com.skyd.anivu.config.Const import com.skyd.anivu.model.bean.FaviconBean import okhttp3.ResponseBody import retrofit2.Call @@ -11,6 +12,6 @@ interface HttpService { @GET fun requestGetResponseBody(@Url url: String): Call - @GET("https://besticon-demo.herokuapp.com/allicons.json") + @GET(Const.FAVICON_FETCH_URL) suspend fun requestFavicon(@Query("url") url: String): FaviconBean } \ No newline at end of file diff --git a/app/src/main/java/com/skyd/anivu/ui/component/lazyverticalgrid/adapter/proxy/DefaultGroup1Proxy.kt b/app/src/main/java/com/skyd/anivu/ui/component/lazyverticalgrid/adapter/proxy/DefaultGroup1Proxy.kt index 851622bd..2014c1c9 100644 --- a/app/src/main/java/com/skyd/anivu/ui/component/lazyverticalgrid/adapter/proxy/DefaultGroup1Proxy.kt +++ b/app/src/main/java/com/skyd/anivu/ui/component/lazyverticalgrid/adapter/proxy/DefaultGroup1Proxy.kt @@ -6,16 +6,19 @@ import com.skyd.anivu.ui.component.lazyverticalgrid.adapter.LazyGridAdapter class DefaultGroup1Proxy( private val group1Proxy: Group1Proxy, + private val hide: (index: Int) -> Boolean, ) : LazyGridAdapter.Proxy() { @Composable override fun Draw(index: Int, data: GroupBean.DefaultGroup) { - Group1Item( - data = data, - initExpand = group1Proxy.isExpand, - onExpandChange = group1Proxy.onExpandChange, - onShowAllArticles = group1Proxy.onShowAllArticles, - onDelete = group1Proxy.onDelete, - onFeedsMoveTo = group1Proxy.onMoveFeedsTo, - ) + if (!hide(index)) { + Group1Item( + data = data, + initExpand = group1Proxy.isExpand, + onExpandChange = group1Proxy.onExpandChange, + onShowAllArticles = group1Proxy.onShowAllArticles, + onDelete = group1Proxy.onDelete, + onFeedsMoveTo = group1Proxy.onMoveFeedsTo, + ) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/skyd/anivu/ui/component/lazyverticalgrid/adapter/proxy/Feed1Proxy.kt b/app/src/main/java/com/skyd/anivu/ui/component/lazyverticalgrid/adapter/proxy/Feed1Proxy.kt index ca472d07..395d077f 100644 --- a/app/src/main/java/com/skyd/anivu/ui/component/lazyverticalgrid/adapter/proxy/Feed1Proxy.kt +++ b/app/src/main/java/com/skyd/anivu/ui/component/lazyverticalgrid/adapter/proxy/Feed1Proxy.kt @@ -83,11 +83,11 @@ fun Feed1Item( .padding(horizontal = 16.dp, vertical = 12.dp) ) { Row(verticalAlignment = Alignment.CenterVertically) { - FeedIcon(modifier = Modifier.padding(3.dp), data = feed, size = 23.dp) + FeedIcon(modifier = Modifier.padding(3.dp), data = feed, size = 24.dp) val title = rememberSaveable(feed.title, feed.nickname) { feed.nickname.orEmpty().ifBlank { feed.title?.readable().orEmpty() } } - Spacer(modifier = Modifier.width(10.dp)) + Spacer(modifier = Modifier.width(8.dp)) Text( modifier = Modifier.weight(1f), text = title, diff --git a/app/src/main/java/com/skyd/anivu/ui/fragment/feed/FeedScreen.kt b/app/src/main/java/com/skyd/anivu/ui/fragment/feed/FeedScreen.kt index 02a8acc1..f823bfac 100644 --- a/app/src/main/java/com/skyd/anivu/ui/fragment/feed/FeedScreen.kt +++ b/app/src/main/java/com/skyd/anivu/ui/fragment/feed/FeedScreen.kt @@ -89,6 +89,7 @@ import com.skyd.anivu.ui.component.lazyverticalgrid.adapter.proxy.Group1Proxy import com.skyd.anivu.ui.fragment.article.ArticleFragment import com.skyd.anivu.ui.fragment.search.SearchFragment import com.skyd.anivu.ui.local.LocalFeedGroupExpand +import com.skyd.anivu.ui.local.LocalHideEmptyDefault import com.skyd.anivu.ui.local.LocalNavController import com.skyd.anivu.ui.local.LocalTextFieldStyle import com.skyd.anivu.ui.local.LocalWindowSizeClass @@ -200,19 +201,19 @@ fun FeedScreen(viewModel: FeedViewModel = hiltViewModel()) { contentPadding = innerPadding + PaddingValues(bottom = fabHeight + 16.dp), onRemoveFeed = { feed -> dispatch(FeedIntent.RemoveFeed(feed.url)) }, onShowAllArticles = { group -> - navController.navigate(R.id.action_to_article_fragment, Bundle().apply { - putStringArrayList( - ArticleFragment.FEED_URLS_KEY, - ArrayList( - (uiState.groupListState as? GroupListState.Success) - ?.dataList - ?.filterIsInstance(FeedViewBean::class.java) - ?.filter { it.feed.groupId == group.groupId || group.isDefaultGroup() && it.feed.isDefaultGroup() } - ?.map { it.feed.url } - .orEmpty() + val feedUrls = (uiState.groupListState as? GroupListState.Success) + ?.dataList + ?.filterIsInstance(FeedViewBean::class.java) + ?.filter { it.feed.groupId == group.groupId || group.isDefaultGroup() && it.feed.isDefaultGroup() } + ?.map { it.feed.url } + .orEmpty() + if (feedUrls.isNotEmpty()) { + navController.navigate(R.id.action_to_article_fragment, Bundle().apply { + putStringArrayList( + ArticleFragment.FEED_URLS_KEY, ArrayList(feedUrls) ) - ) - }) + }) + } }, onEditFeed = { feed -> openEditDialog = feed @@ -524,6 +525,7 @@ private fun FeedList( onMoveToGroup: (from: GroupBean, to: GroupBean) -> Unit, openCreateGroupDialog: () -> Unit, ) { + val hideEmptyDefault = LocalHideEmptyDefault.current val feedGroupExpand = LocalFeedGroupExpand.current val groups = rememberSaveable(result) { result.filterIsInstance() } val feedVisible = rememberSaveable(saver = snapshotStateMapSaver()) { @@ -549,7 +551,11 @@ private fun FeedList( } var openSelectGroupDialog by rememberSaveable { mutableStateOf(null) } var selectGroupDialogCurrentGroup by rememberSaveable { mutableStateOf(null) } - val adapter = remember { + + val shouldHideEmptyDefault: (index: Int) -> Boolean = remember(hideEmptyDefault, result) { + { hideEmptyDefault && result.getOrNull(it + 1) !is FeedViewBean } + } + val adapter = remember(shouldHideEmptyDefault) { val group1Proxy = Group1Proxy( isExpand = { feedVisible[it.groupId] ?: false }, onExpandChange = { data, expand -> feedVisible[data.groupId] = expand }, @@ -562,7 +568,10 @@ private fun FeedList( ) LazyGridAdapter( mutableListOf( - DefaultGroup1Proxy(group1Proxy), + DefaultGroup1Proxy( + group1Proxy = group1Proxy, + hide = shouldHideEmptyDefault, + ), group1Proxy, Feed1Proxy( visible = { feedVisible[it] ?: false }, diff --git a/app/src/main/java/com/skyd/anivu/ui/fragment/settings/behavior/BehaviorFragment.kt b/app/src/main/java/com/skyd/anivu/ui/fragment/settings/behavior/BehaviorFragment.kt index de0fc4b5..6f71ad0e 100644 --- a/app/src/main/java/com/skyd/anivu/ui/fragment/settings/behavior/BehaviorFragment.kt +++ b/app/src/main/java/com/skyd/anivu/ui/fragment/settings/behavior/BehaviorFragment.kt @@ -10,6 +10,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.outlined.Article import androidx.compose.material.icons.outlined.Done import androidx.compose.material.icons.outlined.SwipeLeft +import androidx.compose.material.icons.outlined.VisibilityOff import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon @@ -33,6 +34,7 @@ import com.skyd.anivu.base.BaseComposeFragment import com.skyd.anivu.model.preference.behavior.article.ArticleSwipeLeftActionPreference import com.skyd.anivu.model.preference.behavior.article.ArticleTapActionPreference import com.skyd.anivu.model.preference.behavior.article.DeduplicateTitleInDescPreference +import com.skyd.anivu.model.preference.behavior.feed.HideEmptyDefaultPreference import com.skyd.anivu.ui.component.AniVuTopBar import com.skyd.anivu.ui.component.AniVuTopBarStyle import com.skyd.anivu.ui.component.BaseSettingsItem @@ -41,6 +43,7 @@ import com.skyd.anivu.ui.component.SwitchSettingsItem import com.skyd.anivu.ui.local.LocalArticleSwipeLeftAction import com.skyd.anivu.ui.local.LocalArticleTapAction import com.skyd.anivu.ui.local.LocalDeduplicateTitleInDesc +import com.skyd.anivu.ui.local.LocalHideEmptyDefault import dagger.hilt.android.AndroidEntryPoint @@ -76,6 +79,24 @@ fun BehaviorScreen() { .nestedScroll(scrollBehavior.nestedScrollConnection), contentPadding = paddingValues, ) { + item { + CategorySettingsItem(text = stringResource(id = R.string.behavior_screen_feed_screen_category)) + } + item { + SwitchSettingsItem( + imageVector = Icons.Outlined.VisibilityOff, + text = stringResource(id = R.string.behavior_screen_feed_screen_hide_empty_default), + description = stringResource(id = R.string.behavior_screen_feed_screen_hide_empty_default_description), + checked = LocalHideEmptyDefault.current, + onCheckedChange = { + HideEmptyDefaultPreference.put( + context = context, + scope = scope, + value = it, + ) + } + ) + } item { CategorySettingsItem(text = stringResource(id = R.string.behavior_screen_article_screen_category)) } diff --git a/app/src/main/java/com/skyd/anivu/ui/local/LocalValue.kt b/app/src/main/java/com/skyd/anivu/ui/local/LocalValue.kt index a3521d94..47cdaf64 100644 --- a/app/src/main/java/com/skyd/anivu/ui/local/LocalValue.kt +++ b/app/src/main/java/com/skyd/anivu/ui/local/LocalValue.kt @@ -12,6 +12,7 @@ import com.skyd.anivu.model.preference.appearance.feed.FeedGroupExpandPreference import com.skyd.anivu.model.preference.behavior.article.ArticleSwipeLeftActionPreference import com.skyd.anivu.model.preference.behavior.article.ArticleTapActionPreference import com.skyd.anivu.model.preference.behavior.article.DeduplicateTitleInDescPreference +import com.skyd.anivu.model.preference.behavior.feed.HideEmptyDefaultPreference val LocalNavController = compositionLocalOf { error("LocalNavController not initialized!") @@ -34,4 +35,5 @@ val LocalIgnoreUpdateVersion = compositionLocalOf { IgnoreUpdateVersionPreferenc // Behavior val LocalDeduplicateTitleInDesc = compositionLocalOf { DeduplicateTitleInDescPreference.default } val LocalArticleTapAction = compositionLocalOf { ArticleTapActionPreference.default } -val LocalArticleSwipeLeftAction = compositionLocalOf { ArticleSwipeLeftActionPreference.default } \ No newline at end of file +val LocalArticleSwipeLeftAction = compositionLocalOf { ArticleSwipeLeftActionPreference.default } +val LocalHideEmptyDefault = compositionLocalOf { HideEmptyDefaultPreference.default } \ No newline at end of file diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index de2977db..db4155fb 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -75,7 +75,7 @@ 确定要删除这些缓存吗? 清除 RSS - 配置 RSS 等 + 配置 RSS 同步、解析规则等 解析 Link 为附件 解析 Link 标签,若符合磁力链或 Torrent 格式,则显示为附件 解析 @@ -128,7 +128,7 @@ 播放器 行为 双击 - 配置播放器行为等 + 配置播放器行为、外观等 配置双击行为 外观和样式 主题 @@ -160,7 +160,7 @@ 检查更新失败:%s 详情 行为 - 配置对操作的响应行为 + 配置订阅、文章列表行为等 文章列表 标题去重 删除描述文本中与标题相同的子字符串 @@ -188,6 +188,13 @@ 所有文章 新建下载 磁力链 / 种子链接 + 不支持的链接 + 相对日期 + 完整日期 + 日期样式 + 订阅列表 + 隐藏空默认分组 + 当默认分组为空时,隐藏它 每 %d 分钟 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d8a5525a..53ffaaa0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -80,7 +80,7 @@ Sure to clear the caches? Clear RSS - Configure RSS + Configure RSS sync, parsing rules Parse link as enclosure Parses and displays the link tag as an enclosure if it\'s a magnet or torrent link Parse @@ -134,7 +134,7 @@ Player Behavior Double tap - Configure player behavior + Configure player behavior, appearance Configure double tap behavior Appearance & Style Theme @@ -168,7 +168,7 @@ Check for update failed: %s Read Behavior - Configure behavior in response to user operations + Configure Feed, Article list behavior Article list Deduplicate title Remove substrings in the description text that are the same as the title @@ -200,6 +200,9 @@ Relative Full Date style + Feed list + Hide empty Default + When the Default grouping is empty, hide it Every %d minute Every %d minutes