Skip to content

Commit

Permalink
[feature] Support auto deletion of articles that exceed the limit in …
Browse files Browse the repository at this point in the history
…each feed (#26)
  • Loading branch information
SkyD666 committed Feb 15, 2025
1 parent 6a5545e commit e2ea8da
Show file tree
Hide file tree
Showing 14 changed files with 376 additions and 27 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ android {
minSdk = 24
targetSdk = 35
versionCode = 26
versionName = "3.1-alpha04"
versionName = "3.1-alpha05"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

Expand Down
8 changes: 7 additions & 1 deletion app/src/main/java/com/skyd/anivu/ext/PreferenceExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import com.skyd.anivu.model.preference.appearance.feed.FeedDefaultGroupExpandPre
import com.skyd.anivu.model.preference.appearance.feed.FeedListTonalElevationPreference
import com.skyd.anivu.model.preference.appearance.feed.FeedNumberBadgePreference
import com.skyd.anivu.model.preference.appearance.feed.FeedTopBarTonalElevationPreference
import com.skyd.anivu.model.preference.behavior.feed.HideMutedFeedPreference
import com.skyd.anivu.model.preference.appearance.media.MediaFileFilterPreference
import com.skyd.anivu.model.preference.appearance.media.MediaShowGroupTabPreference
import com.skyd.anivu.model.preference.appearance.media.MediaShowThumbnailPreference
Expand All @@ -35,11 +34,15 @@ import com.skyd.anivu.model.preference.behavior.article.ArticleSwipeRightActionP
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.model.preference.behavior.feed.HideMutedFeedPreference
import com.skyd.anivu.model.preference.data.OpmlExportDirPreference
import com.skyd.anivu.model.preference.data.autodelete.AutoDeleteArticleBeforePreference
import com.skyd.anivu.model.preference.data.autodelete.AutoDeleteArticleFrequencyPreference
import com.skyd.anivu.model.preference.data.autodelete.AutoDeleteArticleKeepFavoritePreference
import com.skyd.anivu.model.preference.data.autodelete.AutoDeleteArticleKeepUnreadPreference
import com.skyd.anivu.model.preference.data.autodelete.AutoDeleteArticleMaxCountPreference
import com.skyd.anivu.model.preference.data.autodelete.AutoDeleteArticleUseBeforePreference
import com.skyd.anivu.model.preference.data.autodelete.AutoDeleteArticleUseMaxCountPreference
import com.skyd.anivu.model.preference.data.autodelete.UseAutoDeletePreference
import com.skyd.anivu.model.preference.data.medialib.MediaLibLocationPreference
import com.skyd.anivu.model.preference.player.BackgroundPlayPreference
Expand Down Expand Up @@ -132,10 +135,13 @@ fun Preferences.toSettings(): Settings {

// Data
useAutoDelete = UseAutoDeletePreference.fromPreferences(this),
autoDeleteArticleUseBefore = AutoDeleteArticleUseBeforePreference.fromPreferences(this),
autoDeleteArticleFrequency = AutoDeleteArticleFrequencyPreference.fromPreferences(this),
autoDeleteArticleBefore = AutoDeleteArticleBeforePreference.fromPreferences(this),
autoDeleteArticleKeepUnread = AutoDeleteArticleKeepUnreadPreference.fromPreferences(this),
autoDeleteArticleKeepFavorite = AutoDeleteArticleKeepFavoritePreference.fromPreferences(this),
autoDeleteArticleUseMaxCount = AutoDeleteArticleUseMaxCountPreference.fromPreferences(this),
autoDeleteArticleMaxCount = AutoDeleteArticleMaxCountPreference.fromPreferences(this),
opmlExportDir = OpmlExportDirPreference.fromPreferences(this),
mediaLibLocation = MediaLibLocationPreference.fromPreferences(this),

Expand Down
21 changes: 21 additions & 0 deletions app/src/main/java/com/skyd/anivu/model/db/dao/ArticleDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,27 @@ interface ArticleDao {
keepFavorite: Boolean = true,
): Int

@Transaction
@Query(
"DELETE FROM $ARTICLE_TABLE_NAME WHERE " +
"(:keepUnread = 0 OR ${ArticleBean.IS_READ_COLUMN} = 1) AND " +
"(:keepFavorite = 0 OR ${ArticleBean.IS_FAVORITE_COLUMN} = 0) AND " +
"(" +
" ${ArticleBean.UPDATE_AT_COLUMN} IS NULL OR (" +
" SELECT COUNT(*) " +
" FROM $ARTICLE_TABLE_NAME AS a2 " +
" WHERE " +
" a2.${ArticleBean.FEED_URL_COLUMN} = $ARTICLE_TABLE_NAME.${ArticleBean.FEED_URL_COLUMN} AND " +
" a2.${ArticleBean.UPDATE_AT_COLUMN} > $ARTICLE_TABLE_NAME.${ArticleBean.UPDATE_AT_COLUMN}" +
" ) >= :count" +
")"
)
suspend fun deleteArticleExceed(
count: Int,
keepUnread: Boolean = true,
keepFavorite: Boolean = true,
): Int

@Transaction
@RawQuery(observedEntities = [FeedBean::class, ArticleBean::class, EnclosureBean::class])
fun getArticlePagingSource(sql: SupportSQLiteQuery): PagingSource<Int, ArticleWithFeed>
Expand Down
14 changes: 13 additions & 1 deletion app/src/main/java/com/skyd/anivu/model/preference/Settings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ import com.skyd.anivu.model.preference.data.autodelete.AutoDeleteArticleBeforePr
import com.skyd.anivu.model.preference.data.autodelete.AutoDeleteArticleFrequencyPreference
import com.skyd.anivu.model.preference.data.autodelete.AutoDeleteArticleKeepFavoritePreference
import com.skyd.anivu.model.preference.data.autodelete.AutoDeleteArticleKeepUnreadPreference
import com.skyd.anivu.model.preference.data.autodelete.AutoDeleteArticleMaxCountPreference
import com.skyd.anivu.model.preference.data.autodelete.AutoDeleteArticleUseBeforePreference
import com.skyd.anivu.model.preference.data.autodelete.AutoDeleteArticleUseMaxCountPreference
import com.skyd.anivu.model.preference.data.autodelete.UseAutoDeletePreference
import com.skyd.anivu.model.preference.data.medialib.MediaLibLocationPreference
import com.skyd.anivu.model.preference.player.BackgroundPlayPreference
Expand Down Expand Up @@ -84,6 +87,9 @@ import com.skyd.anivu.ui.local.LocalAutoDeleteArticleBefore
import com.skyd.anivu.ui.local.LocalAutoDeleteArticleFrequency
import com.skyd.anivu.ui.local.LocalAutoDeleteArticleKeepFavorite
import com.skyd.anivu.ui.local.LocalAutoDeleteArticleKeepUnread
import com.skyd.anivu.ui.local.LocalAutoDeleteArticleMaxCount
import com.skyd.anivu.ui.local.LocalAutoDeleteArticleUseBefore
import com.skyd.anivu.ui.local.LocalAutoDeleteArticleUseMaxCount
import com.skyd.anivu.ui.local.LocalBackgroundPlay
import com.skyd.anivu.ui.local.LocalDarkMode
import com.skyd.anivu.ui.local.LocalDateStyle
Expand Down Expand Up @@ -196,10 +202,13 @@ data class Settings(
val backgroundPlay: Boolean = BackgroundPlayPreference.default,
// Data
val useAutoDelete: Boolean = UseAutoDeletePreference.default,
val autoDeleteArticleUseBefore: Boolean = AutoDeleteArticleUseBeforePreference.default,
val autoDeleteArticleFrequency: Long = AutoDeleteArticleFrequencyPreference.default,
val autoDeleteArticleBefore: Long = AutoDeleteArticleBeforePreference.default,
val autoDeleteArticleKeepUnread: Boolean = AutoDeleteArticleKeepUnreadPreference.default,
val autoDeleteArticleKeepFavorite: Boolean = AutoDeleteArticleKeepFavoritePreference.default,
val autoDeleteArticleUseMaxCount: Boolean = AutoDeleteArticleUseMaxCountPreference.default,
val autoDeleteArticleMaxCount: Int = AutoDeleteArticleMaxCountPreference.default,
val opmlExportDir: String = OpmlExportDirPreference.default,
val mediaLibLocation: String = MediaLibLocationPreference.default,
// Transmission
Expand Down Expand Up @@ -260,7 +269,7 @@ fun SettingsProvider(
LocalHideMutedFeed provides settings.hideMutedFeed,
LocalPickImageMethod provides settings.pickImageMethod,
LocalMediaFileFilter provides settings.mediaFileFilter,
// rss
// Rss
LocalRssSyncFrequency provides settings.rssSyncFrequency,
LocalRssSyncWifiConstraint provides settings.rssSyncWifiConstraint,
LocalRssSyncChargingConstraint provides settings.rssSyncChargingConstraint,
Expand All @@ -279,10 +288,13 @@ fun SettingsProvider(
LocalBackgroundPlay provides settings.backgroundPlay,
// Data
LocalUseAutoDelete provides settings.useAutoDelete,
LocalAutoDeleteArticleUseBefore provides settings.autoDeleteArticleUseBefore,
LocalAutoDeleteArticleFrequency provides settings.autoDeleteArticleFrequency,
LocalAutoDeleteArticleBefore provides settings.autoDeleteArticleBefore,
LocalAutoDeleteArticleKeepUnread provides settings.autoDeleteArticleKeepUnread,
LocalAutoDeleteArticleKeepFavorite provides settings.autoDeleteArticleKeepFavorite,
LocalAutoDeleteArticleUseMaxCount provides settings.autoDeleteArticleUseMaxCount,
LocalAutoDeleteArticleMaxCount provides settings.autoDeleteArticleMaxCount,
LocalOpmlExportDir provides settings.opmlExportDir,
LocalMediaLibLocation provides settings.mediaLibLocation,
// Transmission
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.skyd.anivu.model.preference.data.autodelete

import android.content.Context
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.intPreferencesKey
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 AutoDeleteArticleMaxCountPreference : BasePreference<Int> {
private const val AUTO_DELETE_ARTICLE_MAX_COUNT = "autoDeleteArticleMaxCount"

override val default = 500

val key = intPreferencesKey(AUTO_DELETE_ARTICLE_MAX_COUNT)

fun put(context: Context, scope: CoroutineScope, value: Int) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(key, value)
}
}

override fun fromPreferences(preferences: Preferences): Int = preferences[key] ?: default
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.skyd.anivu.model.preference.data.autodelete

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 AutoDeleteArticleUseBeforePreference : BasePreference<Boolean> {
private const val AUTO_DELETE_ARTICLE_USE_BEFORE = "autoDeleteArticleUseBefore"

override val default = true

val key = booleanPreferencesKey(AUTO_DELETE_ARTICLE_USE_BEFORE)

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
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.skyd.anivu.model.preference.data.autodelete

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 AutoDeleteArticleUseMaxCountPreference : BasePreference<Boolean> {
private const val AUTO_DELETE_ARTICLE_USE_MAX_COUNT = "autoDeleteArticleUseMaxCount"

override val default = false

val key = booleanPreferencesKey(AUTO_DELETE_ARTICLE_USE_MAX_COUNT)

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
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import com.skyd.anivu.model.db.dao.ArticleDao
import com.skyd.anivu.model.preference.data.autodelete.AutoDeleteArticleBeforePreference
import com.skyd.anivu.model.preference.data.autodelete.AutoDeleteArticleKeepFavoritePreference
import com.skyd.anivu.model.preference.data.autodelete.AutoDeleteArticleKeepUnreadPreference
import com.skyd.anivu.model.preference.data.autodelete.AutoDeleteArticleMaxCountPreference
import com.skyd.anivu.model.preference.data.autodelete.AutoDeleteArticleUseBeforePreference
import com.skyd.anivu.model.preference.data.autodelete.AutoDeleteArticleUseMaxCountPreference
import dagger.hilt.EntryPoint
import dagger.hilt.InstallIn
import dagger.hilt.android.EntryPointAccessors
Expand All @@ -29,17 +32,28 @@ class DeleteArticleWorker(context: Context, parameters: WorkerParameters) :

override suspend fun doWork(): Result {
runCatching {
hiltEntryPoint.articleDao.deleteArticleBefore(
timestamp = System.currentTimeMillis() - applicationContext.dataStore.getOrDefault(
AutoDeleteArticleBeforePreference
),
keepUnread = applicationContext.dataStore.getOrDefault(
AutoDeleteArticleKeepUnreadPreference
),
keepFavorite = applicationContext.dataStore.getOrDefault(
AutoDeleteArticleKeepFavoritePreference
),
)
val dataStore = applicationContext.dataStore
val keepUnread = dataStore.getOrDefault(AutoDeleteArticleKeepUnreadPreference)
val keepFavorite = dataStore.getOrDefault(AutoDeleteArticleKeepFavoritePreference)
val useBefore = dataStore.getOrDefault(AutoDeleteArticleUseBeforePreference)
if (useBefore) {
hiltEntryPoint.articleDao.deleteArticleBefore(
timestamp = System.currentTimeMillis() - dataStore.getOrDefault(
AutoDeleteArticleBeforePreference
),
keepUnread = keepUnread,
keepFavorite = keepFavorite,
)
}
val useMaxCount = dataStore.getOrDefault(AutoDeleteArticleUseMaxCountPreference)
val maxCount = dataStore.getOrDefault(AutoDeleteArticleMaxCountPreference)
if (useMaxCount && maxCount > 1) {
hiltEntryPoint.articleDao.deleteArticleExceed(
count = maxCount,
keepUnread = keepUnread,
keepFavorite = keepFavorite,
)
}
}.onFailure { return Result.failure() }
return Result.success()
}
Expand Down
72 changes: 68 additions & 4 deletions app/src/main/java/com/skyd/anivu/ui/component/SettingsItem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import androidx.compose.material3.RadioButton
import androidx.compose.material3.Slider
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.VerticalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.compositionLocalOf
Expand Down Expand Up @@ -137,11 +138,11 @@ fun SliderSettingsItem(

@Composable
fun SwitchSettingsItem(
imageVector: ImageVector?,
checked: Boolean,
text: String,
imageVector: ImageVector?,
modifier: Modifier = Modifier,
description: String? = null,
checked: Boolean = false,
enabled: Boolean = true,
onCheckedChange: ((Boolean) -> Unit)?,
) {
Expand All @@ -158,11 +159,11 @@ fun SwitchSettingsItem(

@Composable
fun SwitchSettingsItem(
painter: Painter?,
checked: Boolean,
text: String,
painter: Painter?,
modifier: Modifier = Modifier,
description: String? = null,
checked: Boolean = false,
enabled: Boolean = true,
onCheckedChange: ((Boolean) -> Unit)?,
) {
Expand Down Expand Up @@ -190,6 +191,69 @@ fun SwitchSettingsItem(
}
}


@Composable
fun SwitchBaseSettingsItem(
checked: Boolean,
text: String,
imageVector: ImageVector?,
modifier: Modifier = Modifier,
description: String? = null,
enabled: Boolean = true,
onClick: (() -> Unit)? = null,
onLongClick: (() -> Unit)? = null,
extraContent: (@Composable () -> Unit)? = null,
onCheckedChange: ((Boolean) -> Unit)?,
) {
SwitchBaseSettingsItem(
checked = checked,
text = text,
painter = imageVector?.let { rememberVectorPainter(image = it) },
modifier = modifier,
description = description,
enabled = enabled,
onClick = onClick,
onLongClick = onLongClick,
extraContent = extraContent,
onCheckedChange = onCheckedChange,
)
}

@Composable
fun SwitchBaseSettingsItem(
checked: Boolean,
text: String,
painter: Painter?,
modifier: Modifier = Modifier,
description: String? = null,
enabled: Boolean = true,
onClick: (() -> Unit)? = null,
onLongClick: (() -> Unit)? = null,
extraContent: (@Composable () -> Unit)? = null,
onCheckedChange: ((Boolean) -> Unit)?,
) {
BaseSettingsItem(
modifier = modifier,
icon = painter,
text = text,
descriptionText = description,
enabled = enabled,
onClick = onClick,
onLongClick = onLongClick,
extraContent = extraContent,
) {
Row(verticalAlignment = Alignment.CenterVertically) {
VerticalDivider(modifier = Modifier.height(32.dp))
Spacer(modifier = Modifier.width(16.dp))
Switch(
checked = checked,
enabled = enabled,
onCheckedChange = onCheckedChange,
)
}
}
}

@Composable
fun RadioSettingsItem(
imageVector: ImageVector?,
Expand Down
Loading

0 comments on commit e2ea8da

Please sign in to comment.