Skip to content

Commit

Permalink
[feature|optimize] Support favorite articles; support mark articles a…
Browse files Browse the repository at this point in the history
…s read; support record OPML export directory; optimize player touch intercept logic; optimize search screen
  • Loading branch information
SkyD666 committed Jun 3, 2024
1 parent df36159 commit 6e59f4d
Show file tree
Hide file tree
Showing 39 changed files with 899 additions and 314 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ android {
minSdk = 24
targetSdk = 34
versionCode = 17
versionName = "1.1-beta44"
versionName = "1.1-beta45"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

Expand Down
3 changes: 3 additions & 0 deletions app/src/main/java/com/skyd/anivu/ext/FileExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import androidx.core.content.FileProvider
import com.skyd.anivu.R
import com.skyd.anivu.ui.component.showToast
import java.io.File

fun File.toUri(context: Context): Uri = FileProvider.getUriForFile(
Expand Down Expand Up @@ -43,5 +45,6 @@ fun File.savePictureToMediaStore(context: Context, autoDelete: Boolean = true) {
if (!dir.exists()) dir.mkdirs()
this.copyTo(File(dir, name))
}
context.getString(R.string.save_picture_to_media_store_saved).showToast()
if (autoDelete) delete()
}
8 changes: 5 additions & 3 deletions app/src/main/java/com/skyd/anivu/ext/PreferenceExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import com.skyd.anivu.model.preference.appearance.NavigationBarLabelPreference
import com.skyd.anivu.model.preference.appearance.TextFieldStylePreference
import com.skyd.anivu.model.preference.appearance.ThemePreference
import com.skyd.anivu.model.preference.appearance.feed.FeedGroupExpandPreference
import com.skyd.anivu.model.preference.autodelete.AutoDeleteArticleBeforePreference
import com.skyd.anivu.model.preference.autodelete.AutoDeleteArticleFrequencyPreference
import com.skyd.anivu.model.preference.autodelete.UseAutoDeletePreference
import com.skyd.anivu.model.preference.behavior.PickImageMethodPreference
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.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.UseAutoDeletePreference
import com.skyd.anivu.model.preference.player.HardwareDecodePreference
import com.skyd.anivu.model.preference.player.PlayerDoubleTapPreference
import com.skyd.anivu.model.preference.player.PlayerShow85sButtonPreference
Expand Down Expand Up @@ -52,5 +53,6 @@ fun Preferences.toSettings(): Settings {
useAutoDelete = UseAutoDeletePreference.fromPreferences(this),
autoDeleteArticleFrequency = AutoDeleteArticleFrequencyPreference.fromPreferences(this),
autoDeleteArticleBefore = AutoDeleteArticleBeforePreference.fromPreferences(this),
opmlExportDir = OpmlExportDirPreference.fromPreferences(this),
)
}
28 changes: 26 additions & 2 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 @@ -98,11 +98,17 @@ interface ArticleDao {
@Query(
"""
SELECT * FROM $ARTICLE_TABLE_NAME
WHERE ${ArticleBean.FEED_URL_COLUMN} IN (:feedUrls)
WHERE ${ArticleBean.FEED_URL_COLUMN} IN (:feedUrls) AND
(:isFavorite IS NULL OR ${ArticleBean.IS_FAVORITE_COLUMN} = :isFavorite) AND
(:isRead IS NULL OR ${ArticleBean.IS_READ_COLUMN} = :isRead)
ORDER BY ${ArticleBean.DATE_COLUMN} DESC
"""
)
fun getArticlePagingSource(feedUrls: List<String>): PagingSource<Int, ArticleWithFeed>
fun getArticlePagingSource(
feedUrls: List<String>,
isFavorite: Boolean?,
isRead: Boolean?,
): PagingSource<Int, ArticleWithFeed>

@Transaction
@RawQuery(observedEntities = [ArticleBean::class])
Expand Down Expand Up @@ -132,4 +138,22 @@ interface ArticleDao {
"""
)
suspend fun queryLatestByFeedUrl(feedUrl: String): ArticleBean?

@Transaction
@Query(
"""
UPDATE $ARTICLE_TABLE_NAME SET ${ArticleBean.IS_FAVORITE_COLUMN} = :favorite
WHERE ${ArticleBean.ARTICLE_ID_COLUMN} LIKE :articleId
"""
)
fun favoriteArticle(articleId: String, favorite: Boolean)

@Transaction
@Query(
"""
UPDATE $ARTICLE_TABLE_NAME SET ${ArticleBean.IS_READ_COLUMN} = :read
WHERE ${ArticleBean.ARTICLE_ID_COLUMN} LIKE :articleId
"""
)
fun readArticle(articleId: String, read: Boolean)
}
10 changes: 7 additions & 3 deletions app/src/main/java/com/skyd/anivu/model/preference/Settings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ import com.skyd.anivu.model.preference.appearance.NavigationBarLabelPreference
import com.skyd.anivu.model.preference.appearance.TextFieldStylePreference
import com.skyd.anivu.model.preference.appearance.ThemePreference
import com.skyd.anivu.model.preference.appearance.feed.FeedGroupExpandPreference
import com.skyd.anivu.model.preference.autodelete.AutoDeleteArticleBeforePreference
import com.skyd.anivu.model.preference.autodelete.AutoDeleteArticleFrequencyPreference
import com.skyd.anivu.model.preference.autodelete.UseAutoDeletePreference
import com.skyd.anivu.model.preference.behavior.PickImageMethodPreference
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.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.UseAutoDeletePreference
import com.skyd.anivu.model.preference.player.HardwareDecodePreference
import com.skyd.anivu.model.preference.player.PlayerDoubleTapPreference
import com.skyd.anivu.model.preference.player.PlayerShow85sButtonPreference
Expand All @@ -38,6 +39,7 @@ import com.skyd.anivu.ui.local.LocalHardwareDecode
import com.skyd.anivu.ui.local.LocalHideEmptyDefault
import com.skyd.anivu.ui.local.LocalIgnoreUpdateVersion
import com.skyd.anivu.ui.local.LocalNavigationBarLabel
import com.skyd.anivu.ui.local.LocalOpmlExportDir
import com.skyd.anivu.ui.local.LocalPickImageMethod
import com.skyd.anivu.ui.local.LocalPlayerDoubleTap
import com.skyd.anivu.ui.local.LocalPlayerShow85sButton
Expand Down Expand Up @@ -73,6 +75,7 @@ data class Settings(
val useAutoDelete: Boolean = UseAutoDeletePreference.default,
val autoDeleteArticleFrequency: Long = AutoDeleteArticleFrequencyPreference.default,
val autoDeleteArticleBefore: Long = AutoDeleteArticleBeforePreference.default,
val opmlExportDir: String = OpmlExportDirPreference.default,
)

@Composable
Expand Down Expand Up @@ -108,6 +111,7 @@ fun SettingsProvider(
LocalUseAutoDelete provides settings.useAutoDelete,
LocalAutoDeleteArticleFrequency provides settings.autoDeleteArticleFrequency,
LocalAutoDeleteArticleBefore provides settings.autoDeleteArticleBefore,
LocalOpmlExportDir provides settings.opmlExportDir,
) {
content()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.skyd.anivu.model.preference.data

import android.content.Context
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.stringPreferencesKey
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 OpmlExportDirPreference : BasePreference<String> {
private const val OPML_EXPORT_DIR = "opmlExportDir"

override val default = ""

val key = stringPreferencesKey(OPML_EXPORT_DIR)

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

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

import android.content.Context
import androidx.datastore.preferences.core.Preferences
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.skyd.anivu.model.preference.autodelete
package com.skyd.anivu.model.preference.data.autodelete

import android.content.Context
import androidx.datastore.preferences.core.Preferences
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.skyd.anivu.model.preference.autodelete
package com.skyd.anivu.model.preference.data.autodelete

import android.content.Context
import androidx.datastore.preferences.core.Preferences
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
Expand All @@ -24,10 +26,29 @@ class ArticleRepository @Inject constructor(
private val rssHelper: RssHelper,
private val pagingConfig: PagingConfig,
) : BaseRepository() {
private val filterFavorite = MutableStateFlow<Boolean?>(null)
private val filterRead = MutableStateFlow<Boolean?>(null)

fun filterFavorite(favorite: Boolean?) {
filterFavorite.value = favorite
}

fun filterRead(read: Boolean?) {
filterRead.value = read
}

fun requestArticleList(feedUrls: List<String>): Flow<PagingData<ArticleWithFeed>> {
return Pager(pagingConfig) {
articleDao.getArticlePagingSource(feedUrls)
}.flow.flowOn(Dispatchers.IO)
return combine(filterFavorite, filterRead) { favorite, read ->
favorite to read
}.flatMapConcat { (favorite, read) ->
Pager(pagingConfig) {
articleDao.getArticlePagingSource(
feedUrls = feedUrls,
isFavorite = favorite,
isRead = read,
)
}.flow
}.flowOn(Dispatchers.IO)
}

fun refreshGroupArticles(groupId: String?): Flow<Unit> {
Expand Down Expand Up @@ -70,4 +91,16 @@ class ArticleRepository @Inject constructor(
})
}.flowOn(Dispatchers.IO)
}

fun favoriteArticle(articleId: String, favorite: Boolean): Flow<Unit> {
return flow {
emit(articleDao.favoriteArticle(articleId, favorite))
}.flowOn(Dispatchers.IO)
}

fun readArticle(articleId: String, read: Boolean): Flow<Unit> {
return flow {
emit(articleDao.readArticle(articleId, read))
}.flowOn(Dispatchers.IO)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import android.database.DatabaseUtils
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.PagingSource
import androidx.paging.PagingState
import androidx.sqlite.db.SimpleSQLiteQuery
import com.skyd.anivu.appContext
import com.skyd.anivu.base.BaseRepository
Expand All @@ -28,85 +26,42 @@ import dagger.hilt.android.EntryPointAccessors
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.withContext
import javax.inject.Inject

class SearchRepository @Inject constructor(
private val feedDao: FeedDao,
private val articleDao: ArticleDao,
private val pagingConfig: PagingConfig,
) : BaseRepository() {
fun requestSearchAll(query: String): Flow<PagingData<Any>> {
return Pager(pagingConfig) {
object : PagingSource<Int, Any>() {
override fun getRefreshKey(state: PagingState<Int, Any>): Int? = null

override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Any> {
val resultList = mutableListOf<Any>()
val key = params.key ?: 0
withContext(Dispatchers.IO) {
val feedSql = genSql(
tableName = FEED_VIEW_NAME,
k = query,
limit = { key to params.loadSize },
)
resultList.addAll(feedDao.getFeedList(feedSql))
val articleSql by lazy {
genSql(
tableName = ARTICLE_TABLE_NAME,
k = query,
leadingFilter = "1",
limit = {
key to if (resultList.isEmpty()) {
params.loadSize
} else {
params.loadSize - resultList.size
}
},
)
}
if (resultList.size < params.loadSize) {
resultList.addAll(articleDao.getArticleList(articleSql))
}
}
return LoadResult.Page(
data = resultList,
prevKey = null,
nextKey = key + resultList.size
)
}
}
}.flow.flowOn(Dispatchers.IO)
private val searchQuery = MutableStateFlow("")

fun updateQuery(query: String) {
searchQuery.value = query
}

fun requestSearchFeed(
query: String,
): Flow<PagingData<FeedViewBean>> {
return flow { emit(genSql(tableName = FEED_VIEW_NAME, k = query)) }.flatMapConcat { sql ->
Pager(pagingConfig) { feedDao.getFeedPagingSource(sql) }.flow
fun listenSearchFeed(): Flow<PagingData<FeedViewBean>> {
return searchQuery.flatMapLatest { query ->
Pager(pagingConfig) {
feedDao.getFeedPagingSource(genSql(tableName = FEED_VIEW_NAME, k = query))
}.flow
}.flowOn(Dispatchers.IO)
}

fun requestSearchArticle(
feedUrls: List<String>,
query: String,
): Flow<PagingData<ArticleWithFeed>> {
return flow {
emit(
genSql(
fun listenSearchArticle(feedUrls: List<String>): Flow<PagingData<ArticleWithFeed>> {
return searchQuery.flatMapLatest { query ->
Pager(pagingConfig) {
articleDao.getArticlePagingSource(genSql(
tableName = ARTICLE_TABLE_NAME,
k = query,
leadingFilter = if (feedUrls.isEmpty()) "1"
else "${ArticleBean.FEED_URL_COLUMN} IN (${
feedUrls.joinToString(", ") { DatabaseUtils.sqlEscapeString(it) }
})",
)
)
}.flatMapConcat { sql ->
Pager(pagingConfig) { articleDao.getArticlePagingSource(sql) }.flow
))
}.flow
}.flowOn(Dispatchers.IO)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import androidx.work.WorkerParameters
import com.skyd.anivu.ext.dataStore
import com.skyd.anivu.ext.getOrDefault
import com.skyd.anivu.model.db.dao.ArticleDao
import com.skyd.anivu.model.preference.autodelete.AutoDeleteArticleBeforePreference
import com.skyd.anivu.model.preference.data.autodelete.AutoDeleteArticleBeforePreference
import dagger.hilt.EntryPoint
import dagger.hilt.InstallIn
import dagger.hilt.android.EntryPointAccessors
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import androidx.work.WorkManager
import com.google.common.util.concurrent.FutureCallback
import com.google.common.util.concurrent.Futures
import com.skyd.anivu.ext.dataStore
import com.skyd.anivu.model.preference.autodelete.AutoDeleteArticleFrequencyPreference
import com.skyd.anivu.model.preference.autodelete.UseAutoDeletePreference
import com.skyd.anivu.model.preference.data.autodelete.AutoDeleteArticleFrequencyPreference
import com.skyd.anivu.model.preference.data.autodelete.UseAutoDeletePreference
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
Expand Down
Loading

0 comments on commit 6e59f4d

Please sign in to comment.