Skip to content

Commit

Permalink
[feature|optimize] Support import and export data via OPML (#14); opt…
Browse files Browse the repository at this point in the history
…imize code
  • Loading branch information
SkyD666 committed May 27, 2024
1 parent ce976c9 commit 9094194
Show file tree
Hide file tree
Showing 32 changed files with 1,293 additions and 67 deletions.
3 changes: 2 additions & 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 = 16
versionName = "1.1-beta35"
versionName = "1.1-beta36"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

Expand Down Expand Up @@ -201,6 +201,7 @@ dependencies {
implementation("io.coil-kt:coil-compose:2.6.0")
implementation("io.coil-kt:coil-gif:2.6.0")
implementation("com.rometools:rome:2.1.0")
implementation("be.ceau:opml-parser:3.1.0")
implementation("net.dankito.readability4j:readability4j:1.0.8")

implementation("org.libtorrent4j:libtorrent4j-android-arm64:2.1.0-31")
Expand Down
32 changes: 32 additions & 0 deletions app/src/main/java/com/skyd/anivu/ext/ActivityResultLauncherExt.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.skyd.anivu.ext

import androidx.activity.result.ActivityResultLauncher
import androidx.core.app.ActivityOptionsCompat
import com.skyd.anivu.ui.component.showToast

fun <I> ActivityResultLauncher<I>.safeLaunch(
input: I,
onError: (Throwable) -> Unit = { it.message?.showToast() },
) {
runCatching {
launch(input)
}.onFailure {
// e.g. No Activity found to handle Intent
it.printStackTrace()
onError.invoke(it)
}
}

fun <I> ActivityResultLauncher<I>.safeLaunch(
input: I,
options: ActivityOptionsCompat,
onError: (Throwable) -> Unit = { it.message?.showToast() },
) {
runCatching {
launch(input, options)
}.onFailure {
// e.g. No Activity found to handle Intent
it.printStackTrace()
onError.invoke(it)
}
}
52 changes: 32 additions & 20 deletions app/src/main/java/com/skyd/anivu/ext/DateExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,43 @@ import java.util.Locale

fun Long.toDateTimeString(
context: Context,
dateStyle: Int = SimpleDateFormat.MEDIUM,
timeStyle: Int = SimpleDateFormat.MEDIUM,
locale: Locale = Locale.getDefault()
): String = Date(this).toDateTimeString(context, dateStyle, timeStyle, locale)
): String = Date(this).toDateTimeString(context)

fun Date.toDateTimeString(
context: Context,
dateStyle: Int = SimpleDateFormat.MEDIUM,
timeStyle: Int = SimpleDateFormat.MEDIUM,
locale: Locale = Locale.getDefault()
): String {
return if (context.dataStore.getOrDefault(DateStylePreference) == DateStylePreference.RELATIVE) {
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()
toRelativeDateTimeString()
} else {
SimpleDateFormat
.getDateTimeInstance(dateStyle, timeStyle, locale)
.format(this)
toAbsoluteDateTimeString()
}
}

fun Long.toAbsoluteDateTimeString(
dateStyle: Int = SimpleDateFormat.MEDIUM,
timeStyle: Int = SimpleDateFormat.MEDIUM,
locale: Locale = Locale.getDefault()
): String = Date(this).toAbsoluteDateTimeString(dateStyle, timeStyle, locale)

fun Date.toAbsoluteDateTimeString(
dateStyle: Int = SimpleDateFormat.MEDIUM,
timeStyle: Int = SimpleDateFormat.MEDIUM,
locale: Locale = Locale.getDefault()
): String = SimpleDateFormat
.getDateTimeInstance(dateStyle, timeStyle, locale)
.format(this)

fun Long.toRelativeDateTimeString(): String = Date(this).toRelativeDateTimeString()

fun Date.toRelativeDateTimeString(): String {
val current = System.currentTimeMillis()
val delta = current - this.time
return 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()
}
15 changes: 14 additions & 1 deletion app/src/main/java/com/skyd/anivu/model/db/dao/FeedDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.RawQuery
import androidx.room.Transaction
import androidx.room.Update
import androidx.sqlite.db.SupportSQLiteQuery
import com.skyd.anivu.appContext
import com.skyd.anivu.model.bean.ArticleBean
Expand Down Expand Up @@ -35,9 +36,17 @@ interface FeedDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun setFeed(feedBean: FeedBean)

@Transaction
@Update
suspend fun updateFeed(feedBean: FeedBean)

@Transaction
suspend fun setFeedWithArticle(feedWithArticleBean: FeedWithArticleBean) {
setFeed(feedWithArticleBean.feed)
if (containsByUrl(feedWithArticleBean.feed.url) == 0) {
setFeed(feedWithArticleBean.feed)
} else {
updateFeed(feedWithArticleBean.feed)
}
val hiltEntryPoint =
EntryPointAccessors.fromApplication(appContext, FeedDaoEntryPoint::class.java)
val feedUrl = feedWithArticleBean.feed.url
Expand Down Expand Up @@ -135,4 +144,8 @@ interface FeedDao {
@Transaction
@Query("SELECT ${FeedBean.URL_COLUMN} FROM $FEED_TABLE_NAME")
fun getAllFeedUrl(): List<String>

@Transaction
@Query("SELECT COUNT(*) FROM $FEED_TABLE_NAME WHERE ${FeedBean.URL_COLUMN} LIKE :url")
fun containsByUrl(url: String): Int
}
12 changes: 12 additions & 0 deletions app/src/main/java/com/skyd/anivu/model/db/dao/GroupDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,16 @@ interface GroupDao {
@Transaction
@Query("SELECT DISTINCT ${GroupBean.GROUP_ID_COLUMN} FROM `$GROUP_TABLE_NAME`")
fun getGroupIds(): Flow<List<String>>

@Transaction
@Query("SELECT COUNT(*) FROM `$GROUP_TABLE_NAME` WHERE ${GroupBean.NAME_COLUMN} LIKE :name")
fun containsByName(name: String): Int

@Transaction
@Query(
"SELECT ${GroupBean.GROUP_ID_COLUMN} FROM `$GROUP_TABLE_NAME` " +
"WHERE ${GroupBean.NAME_COLUMN} LIKE :name " +
"LIMIT 1"
)
fun queryGroupIdByName(name: String): String
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,12 @@ class ArticleRepository @Inject constructor(
rssHelper.queryRssXml(
feed = feedDao.getFeed(feedUrl),
latestLink = articleDao.queryLatestByFeedUrl(feedUrl)?.link
)
)?.also { feedWithArticle ->
feedDao.updateFeed(feedWithArticle.feed)
}?.articles
}
val iconAsync = async { rssHelper.getRssIcon(feedUrl) }
val articleBeanList = articleBeanListAsync.await()
val articleBeanList = articleBeanListAsync.await() ?: return@async

if (articleBeanList.isEmpty()) return@async

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ class FeedRepository @Inject constructor(
) { groupList, groupIds ->
groupList to feedDao.getFeedsNotIn(groupIds)
}.map { (groupList, defaultFeeds) ->
val dataList = mutableListOf<Any>()
dataList.add(GroupBean.DefaultGroup)
dataList.addAll(defaultFeeds)
groupList.forEach { group ->
dataList.add(group.group)
dataList.addAll(group.feeds)
mutableListOf<Any>().apply {
add(GroupBean.DefaultGroup)
addAll(defaultFeeds)
groupList.forEach { group ->
add(group.group)
addAll(group.feeds)
}
}
dataList
}.flowOn(Dispatchers.IO)
}

Expand Down
26 changes: 19 additions & 7 deletions app/src/main/java/com/skyd/anivu/model/repository/RssHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,22 +51,34 @@ class RssHelper @Inject constructor(
suspend fun queryRssXml(
feed: FeedBean,
latestLink: String?, // 日期最新的文章链接,更新时不会take比这个文章更旧的文章
): List<ArticleWithEnclosureBean> =
): FeedWithArticleBean? = withContext(Dispatchers.IO) {
runCatching {
val iconAsync = async { getRssIcon(feed.url) }
inputStream(okHttpClient, feed.url).use { inputStream ->
SyndFeedInput().apply { isPreserveWireFeed = true }
.build(XmlReader(inputStream))
.entries
.asSequence()
.takeWhile { latestLink == null || latestLink != it.link }
.map { article(feed, it) }
.toList()
.let { syndFeed ->
FeedWithArticleBean(
feed = feed.copy(
title = syndFeed.title,
description = syndFeed.description,
link = syndFeed.link,
icon = syndFeed.icon?.link ?: iconAsync.await(),
),
articles = syndFeed.entries
.asSequence()
.takeWhile { latestLink == null || latestLink != it.link }
.map { article(feed, it) }
.toList(),
)
}
}
}.onFailure { e ->
e.printStackTrace()
Log.e("RLog", "queryRssXml[${feed.title}]: ${e.message}")
throw e
}.getOrDefault(emptyList())
}.getOrNull()
}

private fun article(
feed: FeedBean,
Expand Down
Loading

0 comments on commit 9094194

Please sign in to comment.