diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d275ec0..5830478 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -5,9 +5,6 @@
-
-
-
? = null,
- val body: String? = null,
-)
-
-data class AssetsItem(
- val name: String? = null,
- val content_type: String? = null,
- val size: Int? = null,
- val download_count: Int? = null,
- val created_at: String? = null,
- val updated_at: String? = null,
- val browser_download_url: String? = null,
-)
\ No newline at end of file
diff --git a/app/src/main/java/me/ash/reader/data/entity/Filter.kt b/app/src/main/java/me/ash/reader/data/model/Filter.kt
similarity index 74%
rename from app/src/main/java/me/ash/reader/data/entity/Filter.kt
rename to app/src/main/java/me/ash/reader/data/model/Filter.kt
index 738ebca..4d1ada5 100644
--- a/app/src/main/java/me/ash/reader/data/entity/Filter.kt
+++ b/app/src/main/java/me/ash/reader/data/model/Filter.kt
@@ -1,4 +1,4 @@
-package me.ash.reader.data.entity
+package me.ash.reader.data.model
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.FiberManualRecord
@@ -6,7 +6,10 @@ import androidx.compose.material.icons.rounded.FiberManualRecord
import androidx.compose.material.icons.rounded.Star
import androidx.compose.material.icons.rounded.StarOutline
import androidx.compose.material.icons.rounded.Subject
+import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.stringResource
+import me.ash.reader.R
class Filter(
val index: Int,
@@ -33,5 +36,13 @@ class Filter(
iconOutline = Icons.Rounded.Subject,
iconFilled = Icons.Rounded.Subject,
)
+ val values = listOf(Starred, Unread, All)
}
+}
+
+@Composable
+fun Filter.getName(): String = when (this) {
+ Filter.Unread -> stringResource(R.string.unread)
+ Filter.Starred -> stringResource(R.string.starred)
+ else -> stringResource(R.string.all)
}
\ No newline at end of file
diff --git a/app/src/main/java/me/ash/reader/data/entity/ImportantCount.kt b/app/src/main/java/me/ash/reader/data/model/ImportantCount.kt
similarity index 75%
rename from app/src/main/java/me/ash/reader/data/entity/ImportantCount.kt
rename to app/src/main/java/me/ash/reader/data/model/ImportantCount.kt
index c876bb6..5e6ffc0 100644
--- a/app/src/main/java/me/ash/reader/data/entity/ImportantCount.kt
+++ b/app/src/main/java/me/ash/reader/data/model/ImportantCount.kt
@@ -1,4 +1,4 @@
-package me.ash.reader.data.entity
+package me.ash.reader.data.model
data class ImportantCount(
val important: Int,
diff --git a/app/src/main/java/me/ash/reader/data/model/LatestRelease.kt b/app/src/main/java/me/ash/reader/data/model/LatestRelease.kt
new file mode 100644
index 0000000..ff18b3c
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/data/model/LatestRelease.kt
@@ -0,0 +1,2 @@
+package me.ash.reader.data.model
+
diff --git a/app/src/main/java/me/ash/reader/data/entity/Version.kt b/app/src/main/java/me/ash/reader/data/model/Version.kt
similarity index 96%
rename from app/src/main/java/me/ash/reader/data/entity/Version.kt
rename to app/src/main/java/me/ash/reader/data/model/Version.kt
index 1d8243f..50ef322 100644
--- a/app/src/main/java/me/ash/reader/data/entity/Version.kt
+++ b/app/src/main/java/me/ash/reader/data/model/Version.kt
@@ -1,4 +1,4 @@
-package me.ash.reader.data.entity
+package me.ash.reader.data.model
class Version(identifiers: List) {
private var major: Int = 0
diff --git a/app/src/main/java/me/ash/reader/data/module/OkHttpClientModule.kt b/app/src/main/java/me/ash/reader/data/module/OkHttpClientModule.kt
index dcd9224..279248b 100644
--- a/app/src/main/java/me/ash/reader/data/module/OkHttpClientModule.kt
+++ b/app/src/main/java/me/ash/reader/data/module/OkHttpClientModule.kt
@@ -1,3 +1,23 @@
+/*
+ * Feeder: Android RSS reader app
+ * https://gitlab.com/spacecowboy/Feeder
+ *
+ * Copyright (C) 2022 Jonas Kalderstam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
package me.ash.reader.data.module
import android.content.Context
@@ -7,11 +27,20 @@ import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import me.ash.reader.BuildConfig
-import me.ash.reader.cachingHttpClient
+import okhttp3.Cache
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Response
+import java.io.File
+import java.security.KeyManagementException
+import java.security.NoSuchAlgorithmException
+import java.security.cert.X509Certificate
+import java.util.concurrent.TimeUnit
import javax.inject.Singleton
+import javax.net.ssl.HostnameVerifier
+import javax.net.ssl.SSLContext
+import javax.net.ssl.TrustManager
+import javax.net.ssl.X509TrustManager
@Module
@InstallIn(SingletonComponent::class)
@@ -28,6 +57,56 @@ object OkHttpClientModule {
.build()
}
+fun cachingHttpClient(
+ cacheDirectory: File? = null,
+ cacheSize: Long = 10L * 1024L * 1024L,
+ trustAllCerts: Boolean = true,
+ connectTimeoutSecs: Long = 30L,
+ readTimeoutSecs: Long = 30L
+): OkHttpClient {
+ val builder: OkHttpClient.Builder = OkHttpClient.Builder()
+
+ if (cacheDirectory != null) {
+ builder.cache(Cache(cacheDirectory, cacheSize))
+ }
+
+ builder
+ .connectTimeout(connectTimeoutSecs, TimeUnit.SECONDS)
+ .readTimeout(readTimeoutSecs, TimeUnit.SECONDS)
+ .followRedirects(true)
+
+ if (trustAllCerts) {
+ builder.trustAllCerts()
+ }
+
+ return builder.build()
+}
+
+fun OkHttpClient.Builder.trustAllCerts() {
+ try {
+ val trustManager = object : X509TrustManager {
+ override fun checkClientTrusted(chain: Array?, authType: String?) {
+ }
+
+ override fun checkServerTrusted(chain: Array?, authType: String?) {
+ }
+
+ override fun getAcceptedIssuers(): Array = emptyArray()
+ }
+
+ val sslContext = SSLContext.getInstance("TLS")
+ sslContext.init(null, arrayOf(trustManager), null)
+ val sslSocketFactory = sslContext.socketFactory
+
+ sslSocketFactory(sslSocketFactory, trustManager)
+ .hostnameVerifier(HostnameVerifier { _, _ -> true })
+ } catch (e: NoSuchAlgorithmException) {
+ // ignore
+ } catch (e: KeyManagementException) {
+ // ignore
+ }
+}
+
object UserAgentInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
return chain.proceed(
diff --git a/app/src/main/java/me/ash/reader/data/repository/AbstractRssRepository.kt b/app/src/main/java/me/ash/reader/data/repository/AbstractRssRepository.kt
index 71eb82d..75f42f8 100644
--- a/app/src/main/java/me/ash/reader/data/repository/AbstractRssRepository.kt
+++ b/app/src/main/java/me/ash/reader/data/repository/AbstractRssRepository.kt
@@ -2,11 +2,11 @@ package me.ash.reader.data.repository
import android.content.Context
import android.util.Log
-import androidx.hilt.work.HiltWorker
import androidx.paging.PagingSource
-import androidx.work.*
-import dagger.assisted.Assisted
-import dagger.assisted.AssistedInject
+import androidx.work.CoroutineWorker
+import androidx.work.ExistingPeriodicWorkPolicy
+import androidx.work.ListenableWorker
+import androidx.work.WorkManager
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOn
@@ -15,9 +15,9 @@ import me.ash.reader.data.dao.ArticleDao
import me.ash.reader.data.dao.FeedDao
import me.ash.reader.data.dao.GroupDao
import me.ash.reader.data.entity.*
+import me.ash.reader.data.model.ImportantCount
import me.ash.reader.ui.ext.currentAccountId
import java.util.*
-import java.util.concurrent.TimeUnit
abstract class AbstractRssRepository constructor(
private val context: Context,
@@ -130,10 +130,6 @@ abstract class AbstractRssRepository constructor(
return feedDao.queryByLink(context.currentAccountId, url).isNotEmpty()
}
- fun peekWork(): String {
- return workManager.getWorkInfosByTag("sync").get().size.toString()
- }
-
suspend fun updateGroup(group: Group) {
groupDao.update(group)
}
@@ -207,34 +203,3 @@ abstract class AbstractRssRepository constructor(
}
}
}
-
-@HiltWorker
-class SyncWorker @AssistedInject constructor(
- @Assisted context: Context,
- @Assisted workerParams: WorkerParameters,
- private val rssRepository: RssRepository,
-) : CoroutineWorker(context, workerParams) {
-
- override suspend fun doWork(): Result {
- Log.i("RLog", "doWork: ")
- return rssRepository.get().sync(this)
- }
-
- companion object {
- const val WORK_NAME = "article.sync"
-
- val UUID: UUID
-
- val repeatingRequest = PeriodicWorkRequestBuilder(
- 15, TimeUnit.MINUTES
- ).setConstraints(
- Constraints.Builder()
- .build()
- ).addTag(WORK_NAME).build().also {
- UUID = it.id
- }
-
- fun setIsSyncing(boolean: Boolean) = workDataOf("isSyncing" to boolean)
- fun Data.getIsSyncing(): Boolean = getBoolean("isSyncing", false)
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/me/ash/reader/data/repository/AppRepository.kt b/app/src/main/java/me/ash/reader/data/repository/AppRepository.kt
index 5d67c56..bfa0216 100644
--- a/app/src/main/java/me/ash/reader/data/repository/AppRepository.kt
+++ b/app/src/main/java/me/ash/reader/data/repository/AppRepository.kt
@@ -4,13 +4,11 @@ import android.content.Context
import android.util.Log
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.withContext
import me.ash.reader.R
-import me.ash.reader.data.entity.toVersion
-import me.ash.reader.data.module.ApplicationScope
+import me.ash.reader.data.model.toVersion
import me.ash.reader.data.module.DispatcherIO
import me.ash.reader.data.module.DispatcherMain
import me.ash.reader.data.source.AppNetworkDataSource
@@ -23,8 +21,6 @@ class AppRepository @Inject constructor(
@ApplicationContext
private val context: Context,
private val appNetworkDataSource: AppNetworkDataSource,
- @ApplicationScope
- private val applicationScope: CoroutineScope,
@DispatcherIO
private val dispatcherIO: CoroutineDispatcher,
@DispatcherMain
@@ -55,14 +51,8 @@ class AppRepository @Inject constructor(
val currentVersion = context.getCurrentVersion()
val latestLog = latest.body ?: ""
val latestPublishDate = latest.published_at ?: latest.created_at ?: ""
- val latestSize = latest.assets
- ?.first()
- ?.size
- ?: 0
- val latestDownloadUrl = latest.assets
- ?.first()
- ?.browser_download_url
- ?: ""
+ val latestSize = latest.assets?.first()?.size ?: 0
+ val latestDownloadUrl = latest.assets?.first()?.browser_download_url ?: ""
Log.i("RLog", "current version $currentVersion")
if (latestVersion.whetherNeedUpdate(currentVersion, skipVersion)) {
diff --git a/app/src/main/java/me/ash/reader/data/repository/FeverRssRepository.kt b/app/src/main/java/me/ash/reader/data/repository/FeverRssRepository.kt
deleted file mode 100644
index a7b8600..0000000
--- a/app/src/main/java/me/ash/reader/data/repository/FeverRssRepository.kt
+++ /dev/null
@@ -1,163 +0,0 @@
-//package me.ash.reader.data.repository
-//
-//import android.content.Context
-//import android.util.Log
-//import androidx.work.WorkManager
-//import dagger.hilt.android.qualifiers.ApplicationContext
-//import kotlinx.coroutines.CoroutineDispatcher
-//import kotlinx.coroutines.CoroutineScope
-//import kotlinx.coroutines.launch
-//import kotlinx.coroutines.sync.withLock
-//import me.ash.reader.data.dao.AccountDao
-//import me.ash.reader.data.dao.ArticleDao
-//import me.ash.reader.data.dao.FeedDao
-//import me.ash.reader.data.dao.GroupDao
-//import me.ash.reader.data.entity.Article
-//import me.ash.reader.data.entity.Feed
-//import me.ash.reader.data.entity.Group
-//import me.ash.reader.data.module.ApplicationScope
-//import me.ash.reader.data.module.DispatcherDefault
-//import me.ash.reader.data.module.DispatcherIO
-//import me.ash.reader.data.source.FeverApiDataSource
-//import me.ash.reader.data.source.RssNetworkDataSource
-//import me.ash.reader.ui.ext.currentAccountId
-//import me.ash.reader.ui.ext.spacerDollar
-//import net.dankito.readability4j.extended.Readability4JExtended
-//import java.util.*
-//import javax.inject.Inject
-//import kotlin.collections.set
-//
-//class FeverRssRepository @Inject constructor(
-// @ApplicationContext
-// private val context: Context,
-// private val articleDao: ArticleDao,
-// private val feedDao: FeedDao,
-// private val groupDao: GroupDao,
-// private val rssHelper: RssHelper,
-// private val feverApiDataSource: FeverApiDataSource,
-// private val accountDao: AccountDao,
-// rssNetworkDataSource: RssNetworkDataSource,
-// @ApplicationScope
-// private val applicationScope: CoroutineScope,
-// @DispatcherDefault
-// private val dispatcherDefault: CoroutineDispatcher,
-// @DispatcherIO
-// private val dispatcherIO: CoroutineDispatcher,
-// workManager: WorkManager,
-//) : AbstractRssRepository(
-// context, accountDao, articleDao, groupDao,
-// feedDao, rssNetworkDataSource, workManager,
-// dispatcherIO
-//) {
-// override suspend fun updateArticleInfo(article: Article) {
-// articleDao.update(article)
-// }
-//
-// override suspend fun subscribe(feed: Feed, articles: List) {
-// feedDao.insert(feed)
-// articleDao.insertList(articles.map {
-// it.copy(feedId = feed.id)
-// })
-// }
-//
-// override suspend fun addGroup(name: String): String {
-// return UUID.randomUUID().toString().also {
-// groupDao.insert(
-// Group(
-// id = it,
-// name = name,
-// accountId = context.currentAccountId
-// )
-// )
-// }
-// }
-//
-// override suspend fun sync() {
-// applicationScope.launch(dispatcherDefault) {
-// mutex.withLock {
-// val accountId = context.currentAccountId
-//
-// updateSyncState {
-// it.copy(
-// feedCount = 1,
-// syncedCount = 1,
-// currentFeedName = "Fever"
-// )
-// }
-//
-// if (feedDao.queryAll(accountId).isNullOrEmpty()) {
-// // Temporary add feeds
-// val feverFeeds = feverApiDataSource.feeds().execute().body()!!.feeds
-// val feverGroupsBody = feverApiDataSource.groups().execute().body()!!
-// Log.i("RLog", "Fever groups: $feverGroupsBody")
-// feverGroupsBody.groups.forEach {
-// groupDao.insert(
-// Group(
-// id = accountId.spacerDollar(it.id),
-// name = it.title,
-// accountId = accountId,
-// )
-// )
-// }
-// val feverFeedsGroupsMap = mutableMapOf()
-// feverGroupsBody.feeds_groups.forEach { item ->
-// item.feed_ids
-// .split(",")
-// .map { it.toInt() }
-// .forEach { id ->
-// feverFeedsGroupsMap[id] = item.group_id
-// }
-// }
-// val feeds = feverFeeds.map {
-// Feed(
-// id = accountId.spacerDollar(it.id),
-// name = it.title,
-// url = it.url,
-// groupId = feverFeedsGroupsMap[it.id].toString(),
-// accountId = accountId
-// )
-// }
-// feedDao.insertList(feeds)
-// }
-//
-// // Add articles
-// val articles = mutableListOf()
-// feverApiDataSource.itemsBySince(since = 1647444325925621L)
-// .execute().body()!!.items
-// .forEach {
-// articles.add(
-// Article(
-// id = accountId.spacerDollar(it.id),
-// date = Date(it.created_on_time * 1000),
-// title = it.title,
-// author = it.author,
-// rawDescription = it.html,
-// shortDescription = (
-// Readability4JExtended("", it.html)
-// .parse().textContent ?: ""
-// ).take(100).trim(),
-// link = it.url,
-// accountId = accountId,
-// feedId = it.feed_id.toString(),
-// isUnread = it.is_read == 0,
-// isStarred = it.is_saved == 1,
-// )
-// )
-// }
-// articleDao.insertList(articles)
-//
-// // Complete sync
-// accountDao.update(accountDao.queryById(accountId)!!.apply {
-// updateAt = Date()
-// })
-// updateSyncState {
-// it.copy(
-// feedCount = 0,
-// syncedCount = 0,
-// currentFeedName = ""
-// )
-// }
-// }
-// }
-// }
-//}
\ No newline at end of file
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 d101a19..b09d0ad 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
@@ -1,12 +1,7 @@
package me.ash.reader.data.repository
-import android.app.*
import android.content.Context
-import android.content.Intent
-import android.graphics.BitmapFactory
import android.util.Log
-import androidx.core.app.NotificationCompat
-import androidx.core.app.NotificationManagerCompat
import androidx.work.CoroutineWorker
import androidx.work.ListenableWorker
import androidx.work.WorkManager
@@ -15,8 +10,6 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.withContext
-import me.ash.reader.MainActivity
-import me.ash.reader.R
import me.ash.reader.data.dao.AccountDao
import me.ash.reader.data.dao.ArticleDao
import me.ash.reader.data.dao.FeedDao
@@ -30,8 +23,6 @@ import me.ash.reader.data.module.DispatcherIO
import me.ash.reader.data.repository.SyncWorker.Companion.setIsSyncing
import me.ash.reader.ui.ext.currentAccountId
import me.ash.reader.ui.ext.spacerDollar
-import me.ash.reader.ui.page.common.ExtraName
-import me.ash.reader.ui.page.common.NotificationGroupName
import java.util.*
import javax.inject.Inject
@@ -41,6 +32,7 @@ class LocalRssRepository @Inject constructor(
private val articleDao: ArticleDao,
private val feedDao: FeedDao,
private val rssHelper: RssHelper,
+ private val notificationHelper: NotificationHelper,
private val accountDao: AccountDao,
private val groupDao: GroupDao,
@DispatcherDefault
@@ -52,16 +44,6 @@ class LocalRssRepository @Inject constructor(
context, accountDao, articleDao, groupDao,
feedDao, workManager, dispatcherIO
) {
- private val notificationManager: NotificationManagerCompat =
- NotificationManagerCompat.from(context).apply {
- createNotificationChannel(
- NotificationChannel(
- NotificationGroupName.ARTICLE_UPDATE,
- NotificationGroupName.ARTICLE_UPDATE,
- NotificationManager.IMPORTANCE_DEFAULT
- )
- )
- }
override suspend fun updateArticleInfo(article: Article) {
articleDao.update(article)
@@ -98,7 +80,7 @@ class LocalRssRepository @Inject constructor(
.awaitAll()
.forEach {
if (it.isNotify) {
- notify(
+ notificationHelper.notify(
FeedWithArticle(
it.feedWithArticle.feed,
articleDao.insertIfNotExist(it.feedWithArticle.articles)
@@ -183,75 +165,4 @@ class LocalRssRepository @Inject constructor(
isNotify = articles.isNotEmpty() && feed.isNotification
)
}
-
- private fun notify(
- feedWithArticle: FeedWithArticle,
- ) {
- notificationManager.createNotificationChannelGroup(
- NotificationChannelGroup(
- feedWithArticle.feed.id,
- feedWithArticle.feed.name
- )
- )
- feedWithArticle.articles.forEach { article ->
- val builder = NotificationCompat.Builder(context, NotificationGroupName.ARTICLE_UPDATE)
- .setSmallIcon(R.drawable.ic_notification)
- .setLargeIcon(
- (BitmapFactory.decodeResource(
- context.resources,
- R.drawable.ic_notification
- ))
- )
- .setContentTitle(article.title)
- .setContentIntent(
- PendingIntent.getActivity(
- context,
- Random().nextInt() + article.id.hashCode(),
- Intent(context, MainActivity::class.java).apply {
- flags = Intent.FLAG_ACTIVITY_NEW_TASK or
- Intent.FLAG_ACTIVITY_CLEAR_TASK
- putExtra(
- ExtraName.ARTICLE_ID,
- article.id
- )
- },
- PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
- )
- )
- .setGroup(feedWithArticle.feed.id)
- .setStyle(
- NotificationCompat.BigTextStyle()
- .bigText(article.shortDescription)
- .setSummaryText(feedWithArticle.feed.name)
- )
-
- notificationManager.notify(
- Random().nextInt() + article.id.hashCode(),
- builder.build().apply {
- flags = Notification.FLAG_AUTO_CANCEL
- }
- )
- }
-
- if (feedWithArticle.articles.size > 1) {
- notificationManager.notify(
- Random().nextInt() + feedWithArticle.feed.id.hashCode(),
- NotificationCompat.Builder(context, NotificationGroupName.ARTICLE_UPDATE)
- .setSmallIcon(R.drawable.ic_notification)
- .setLargeIcon(
- (BitmapFactory.decodeResource(
- context.resources,
- R.drawable.ic_notification
- ))
- )
- .setStyle(
- NotificationCompat.InboxStyle()
- .setSummaryText(feedWithArticle.feed.name)
- )
- .setGroup(feedWithArticle.feed.id)
- .setGroupSummary(true)
- .build()
- )
- }
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/me/ash/reader/data/repository/NotificationHelper.kt b/app/src/main/java/me/ash/reader/data/repository/NotificationHelper.kt
new file mode 100644
index 0000000..ee421c8
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/data/repository/NotificationHelper.kt
@@ -0,0 +1,103 @@
+package me.ash.reader.data.repository
+
+import android.app.*
+import android.content.Context
+import android.content.Intent
+import android.graphics.BitmapFactory
+import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationManagerCompat
+import dagger.hilt.android.qualifiers.ApplicationContext
+import me.ash.reader.MainActivity
+import me.ash.reader.R
+import me.ash.reader.data.entity.FeedWithArticle
+import me.ash.reader.ui.page.common.ExtraName
+import me.ash.reader.ui.page.common.NotificationGroupName
+import java.util.*
+import javax.inject.Inject
+
+class NotificationHelper @Inject constructor(
+ @ApplicationContext
+ private val context: Context,
+) {
+ private val notificationManager: NotificationManagerCompat =
+ NotificationManagerCompat.from(context).apply {
+ createNotificationChannel(
+ NotificationChannel(
+ NotificationGroupName.ARTICLE_UPDATE,
+ NotificationGroupName.ARTICLE_UPDATE,
+ NotificationManager.IMPORTANCE_DEFAULT
+ )
+ )
+ }
+
+ fun notify(
+ feedWithArticle: FeedWithArticle,
+ ) {
+ notificationManager.createNotificationChannelGroup(
+ NotificationChannelGroup(
+ feedWithArticle.feed.id,
+ feedWithArticle.feed.name
+ )
+ )
+ feedWithArticle.articles.forEach { article ->
+ val builder = NotificationCompat.Builder(context, NotificationGroupName.ARTICLE_UPDATE)
+ .setSmallIcon(R.drawable.ic_notification)
+ .setLargeIcon(
+ (BitmapFactory.decodeResource(
+ context.resources,
+ R.drawable.ic_notification
+ ))
+ )
+ .setContentTitle(article.title)
+ .setContentIntent(
+ PendingIntent.getActivity(
+ context,
+ Random().nextInt() + article.id.hashCode(),
+ Intent(context, MainActivity::class.java).apply {
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK or
+ Intent.FLAG_ACTIVITY_CLEAR_TASK
+ putExtra(
+ ExtraName.ARTICLE_ID,
+ article.id
+ )
+ },
+ PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+ )
+ )
+ .setGroup(feedWithArticle.feed.id)
+ .setStyle(
+ NotificationCompat.BigTextStyle()
+ .bigText(article.shortDescription)
+ .setSummaryText(feedWithArticle.feed.name)
+ )
+
+ notificationManager.notify(
+ Random().nextInt() + article.id.hashCode(),
+ builder.build().apply {
+ flags = Notification.FLAG_AUTO_CANCEL
+ }
+ )
+ }
+
+ if (feedWithArticle.articles.size > 1) {
+ notificationManager.notify(
+ Random().nextInt() + feedWithArticle.feed.id.hashCode(),
+ NotificationCompat.Builder(context, NotificationGroupName.ARTICLE_UPDATE)
+ .setSmallIcon(R.drawable.ic_notification)
+ .setLargeIcon(
+ (BitmapFactory.decodeResource(
+ context.resources,
+ R.drawable.ic_notification
+ ))
+ )
+ .setStyle(
+ NotificationCompat.InboxStyle()
+ .setSummaryText(feedWithArticle.feed.name)
+ )
+ .setGroup(feedWithArticle.feed.id)
+ .setGroupSummary(true)
+ .build()
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/me/ash/reader/data/repository/OpmlRepository.kt b/app/src/main/java/me/ash/reader/data/repository/OpmlRepository.kt
index 304bed4..7af9c0e 100644
--- a/app/src/main/java/me/ash/reader/data/repository/OpmlRepository.kt
+++ b/app/src/main/java/me/ash/reader/data/repository/OpmlRepository.kt
@@ -43,7 +43,7 @@ class OpmlRepository @Inject constructor(
repeatList.add(it)
}
}
- feedDao.insertList((groupWithFeed.feeds subtract repeatList).toList())
+ feedDao.insertList((groupWithFeed.feeds subtract repeatList.toSet()).toList())
}
}
diff --git a/app/src/main/java/me/ash/reader/data/repository/RssHelper.kt b/app/src/main/java/me/ash/reader/data/repository/RssHelper.kt
index 18df76c..584fa48 100644
--- a/app/src/main/java/me/ash/reader/data/repository/RssHelper.kt
+++ b/app/src/main/java/me/ash/reader/data/repository/RssHelper.kt
@@ -183,27 +183,4 @@ class RssHelper @Inject constructor(
}
)
}
-
- private fun parseDate(
- inputDate: String, patterns: Array = arrayOf(
- "yyyy-MM-dd'T'HH:mm:ss'Z'",
- "yyyy-MM-dd",
- "yyyy-MM-dd HH:mm:ss",
- "yyyyMMdd",
- "yyyy/MM/dd",
- "yyyy年MM月dd日",
- "yyyy MM dd",
- )
- ): Date? {
- val df = SimpleDateFormat()
- for (pattern in patterns) {
- df.applyPattern(pattern)
- df.isLenient = false
- val date = df.parse(inputDate, ParsePosition(0))
- if (date != null) {
- return date
- }
- }
- return null
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/me/ash/reader/data/repository/SyncWorker.kt b/app/src/main/java/me/ash/reader/data/repository/SyncWorker.kt
new file mode 100644
index 0000000..50cdaf2
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/data/repository/SyncWorker.kt
@@ -0,0 +1,41 @@
+package me.ash.reader.data.repository
+
+import android.content.Context
+import android.util.Log
+import androidx.hilt.work.HiltWorker
+import androidx.work.*
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import java.util.*
+import java.util.concurrent.TimeUnit
+
+@HiltWorker
+class SyncWorker @AssistedInject constructor(
+ @Assisted context: Context,
+ @Assisted workerParams: WorkerParameters,
+ private val rssRepository: RssRepository,
+) : CoroutineWorker(context, workerParams) {
+
+ override suspend fun doWork(): Result {
+ Log.i("RLog", "doWork: ")
+ return rssRepository.get().sync(this)
+ }
+
+ companion object {
+ const val WORK_NAME = "article.sync"
+
+ val UUID: UUID
+
+ val repeatingRequest = PeriodicWorkRequestBuilder(
+ 15, TimeUnit.MINUTES
+ ).setConstraints(
+ Constraints.Builder()
+ .build()
+ ).addTag(WORK_NAME).build().also {
+ UUID = it.id
+ }
+
+ fun setIsSyncing(boolean: Boolean) = workDataOf("isSyncing" to boolean)
+ fun Data.getIsSyncing(): Boolean = getBoolean("isSyncing", false)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/me/ash/reader/data/source/AppNetworkDataSource.kt b/app/src/main/java/me/ash/reader/data/source/AppNetworkDataSource.kt
index a37d951..df0709e 100644
--- a/app/src/main/java/me/ash/reader/data/source/AppNetworkDataSource.kt
+++ b/app/src/main/java/me/ash/reader/data/source/AppNetworkDataSource.kt
@@ -5,7 +5,6 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
-import me.ash.reader.data.entity.LatestRelease
import okhttp3.ResponseBody
import retrofit2.Response
import retrofit2.Retrofit
@@ -15,12 +14,6 @@ import retrofit2.http.Streaming
import retrofit2.http.Url
import java.io.File
-sealed class Download {
- object NotYet : Download()
- data class Progress(val percent: Int) : Download()
- data class Finished(val file: File) : Download()
-}
-
interface AppNetworkDataSource {
@GET
suspend fun getReleaseLatest(@Url url: String): Response
@@ -92,4 +85,32 @@ fun ResponseBody.downloadToFileWithProgress(saveFile: File): Flow =
saveFile.delete()
}
}
- }.flowOn(Dispatchers.IO).distinctUntilChanged()
\ No newline at end of file
+ }.flowOn(Dispatchers.IO).distinctUntilChanged()
+
+data class LatestRelease(
+ val html_url: String? = null,
+ val tag_name: String? = null,
+ val name: String? = null,
+ val draft: Boolean? = null,
+ val prerelease: Boolean? = null,
+ val created_at: String? = null,
+ val published_at: String? = null,
+ val assets: List? = null,
+ val body: String? = null,
+)
+
+data class AssetsItem(
+ val name: String? = null,
+ val content_type: String? = null,
+ val size: Int? = null,
+ val download_count: Int? = null,
+ val created_at: String? = null,
+ val updated_at: String? = null,
+ val browser_download_url: String? = null,
+)
+
+sealed class Download {
+ object NotYet : Download()
+ data class Progress(val percent: Int) : Download()
+ data class Finished(val file: File) : Download()
+}
\ No newline at end of file
diff --git a/app/src/main/java/me/ash/reader/ui/ext/ContextExt.kt b/app/src/main/java/me/ash/reader/ui/ext/ContextExt.kt
index 203e575..2db93f0 100644
--- a/app/src/main/java/me/ash/reader/ui/ext/ContextExt.kt
+++ b/app/src/main/java/me/ash/reader/ui/ext/ContextExt.kt
@@ -7,8 +7,8 @@ import android.content.Intent
import android.util.Log
import android.widget.Toast
import androidx.core.content.FileProvider
-import me.ash.reader.data.entity.Version
-import me.ash.reader.data.entity.toVersion
+import me.ash.reader.data.model.Version
+import me.ash.reader.data.model.toVersion
import java.io.File
fun Context.findActivity(): Activity? = when (this) {
diff --git a/app/src/main/java/me/ash/reader/ui/ext/DateExt.kt b/app/src/main/java/me/ash/reader/ui/ext/DateExt.kt
index 887b285..e609595 100644
--- a/app/src/main/java/me/ash/reader/ui/ext/DateExt.kt
+++ b/app/src/main/java/me/ash/reader/ui/ext/DateExt.kt
@@ -4,6 +4,7 @@ import android.content.Context
import androidx.core.os.ConfigurationCompat
import me.ash.reader.R
import java.text.DateFormat
+import java.text.ParsePosition
import java.text.SimpleDateFormat
import java.util.*
@@ -40,4 +41,27 @@ fun Date.formatAsString(
}
}
}
+}
+
+private fun String.parseToDate(
+ patterns: Array = arrayOf(
+ "yyyy-MM-dd'T'HH:mm:ss'Z'",
+ "yyyy-MM-dd",
+ "yyyy-MM-dd HH:mm:ss",
+ "yyyyMMdd",
+ "yyyy/MM/dd",
+ "yyyy年MM月dd日",
+ "yyyy MM dd",
+ )
+): Date? {
+ val df = SimpleDateFormat()
+ for (pattern in patterns) {
+ df.applyPattern(pattern)
+ df.isLenient = false
+ val date = df.parse(this, ParsePosition(0))
+ if (date != null) {
+ return date
+ }
+ }
+ return null
}
\ No newline at end of file
diff --git a/app/src/main/java/me/ash/reader/ui/ext/FilterExt.kt b/app/src/main/java/me/ash/reader/ui/ext/FilterExt.kt
deleted file mode 100644
index 8a2327a..0000000
--- a/app/src/main/java/me/ash/reader/ui/ext/FilterExt.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package me.ash.reader.ui.ext
-
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.res.stringResource
-import me.ash.reader.R
-import me.ash.reader.data.entity.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)
-}
diff --git a/app/src/main/java/me/ash/reader/ui/ext/FlavorExt.kt b/app/src/main/java/me/ash/reader/ui/ext/FlavorExt.kt
new file mode 100644
index 0000000..2a3e8b1
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/ui/ext/FlavorExt.kt
@@ -0,0 +1,11 @@
+@file:Suppress("SpellCheckingInspection")
+
+package me.ash.reader.ui.ext
+
+import me.ash.reader.BuildConfig
+
+const val GITHUB = "github"
+const val FDROID = "fdroid"
+
+const val isFdroid = BuildConfig.FLAVOR == FDROID
+const val notFdroid = !isFdroid
\ No newline at end of file
diff --git a/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt b/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt
index 87b5015..3855951 100644
--- a/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt
@@ -12,7 +12,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import com.google.accompanist.navigation.animation.AnimatedNavHost
import com.google.accompanist.navigation.animation.rememberAnimatedNavController
import com.google.accompanist.systemuicontroller.rememberSystemUiController
-import me.ash.reader.data.entity.Filter
+import me.ash.reader.data.model.Filter
import me.ash.reader.data.preference.LocalDarkTheme
import me.ash.reader.ui.ext.*
import me.ash.reader.ui.page.home.HomeViewAction
diff --git a/app/src/main/java/me/ash/reader/ui/page/home/FilterBar.kt b/app/src/main/java/me/ash/reader/ui/page/home/FilterBar.kt
index a9307e7..cb7dad0 100644
--- a/app/src/main/java/me/ash/reader/ui/page/home/FilterBar.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/home/FilterBar.kt
@@ -12,10 +12,10 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
-import me.ash.reader.data.entity.Filter
+import me.ash.reader.data.model.Filter
+import me.ash.reader.data.model.getName
import me.ash.reader.data.preference.FlowFilterBarStylePreference
import me.ash.reader.data.preference.LocalThemeIndex
-import me.ash.reader.ui.ext.getName
import me.ash.reader.ui.ext.surfaceColorAtElevation
import me.ash.reader.ui.theme.palette.onDark
@@ -39,11 +39,7 @@ fun FilterBar(
tonalElevation = filterBarTonalElevation,
) {
Spacer(modifier = Modifier.width(filterBarPadding))
- listOf(
- Filter.Starred,
- Filter.Unread,
- Filter.All,
- ).forEach { item ->
+ Filter.values.forEach { item ->
NavigationBarItem(
// modifier = Modifier.height(60.dp),
alwaysShowLabel = when (filterBarStyle) {
diff --git a/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt
index 9149e23..3f7f258 100644
--- a/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt
@@ -7,7 +7,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
import me.ash.reader.data.entity.Feed
-import me.ash.reader.data.entity.Filter
+import me.ash.reader.data.model.Filter
import me.ash.reader.data.entity.Group
import me.ash.reader.data.module.ApplicationScope
import me.ash.reader.data.repository.RssRepository
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 4b22327..d522d5c 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
@@ -28,7 +28,8 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import kotlinx.coroutines.flow.map
import me.ash.reader.R
-import me.ash.reader.data.entity.toVersion
+import me.ash.reader.data.model.getName
+import me.ash.reader.data.model.toVersion
import me.ash.reader.data.preference.*
import me.ash.reader.data.repository.SyncWorker.Companion.getIsSyncing
import me.ash.reader.ui.component.Banner
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 d6a1abd..81dd07f 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
@@ -25,13 +25,13 @@ import androidx.paging.compose.collectAsLazyPagingItems
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import me.ash.reader.R
+import me.ash.reader.data.model.getName
import me.ash.reader.data.preference.*
import me.ash.reader.data.repository.SyncWorker.Companion.getIsSyncing
import me.ash.reader.ui.component.DisplayText
import me.ash.reader.ui.component.FeedbackIconButton
import me.ash.reader.ui.component.SwipeRefresh
import me.ash.reader.ui.ext.collectAsStateValue
-import me.ash.reader.ui.ext.getName
import me.ash.reader.ui.ext.surfaceColorAtElevation
import me.ash.reader.ui.page.common.RouteName
import me.ash.reader.ui.page.home.FilterBar
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 5a88950..620bc52 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
@@ -19,7 +19,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import kotlinx.coroutines.flow.map
import me.ash.reader.R
-import me.ash.reader.data.entity.toVersion
+import me.ash.reader.data.model.toVersion
import me.ash.reader.ui.component.Banner
import me.ash.reader.ui.component.DisplayText
import me.ash.reader.ui.component.FeedbackIconButton
diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStyle.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStyle.kt
index a7fce97..2f7a95c 100644
--- a/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStyle.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStyle.kt
@@ -22,7 +22,7 @@ import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import me.ash.reader.R
import me.ash.reader.data.entity.Feed
-import me.ash.reader.data.entity.Filter
+import me.ash.reader.data.model.Filter
import me.ash.reader.data.entity.Group
import me.ash.reader.data.preference.*
import me.ash.reader.ui.component.*
diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStyle.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStyle.kt
index a07e097..9471ece 100644
--- a/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStyle.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStyle.kt
@@ -25,7 +25,7 @@ import me.ash.reader.R
import me.ash.reader.data.entity.Article
import me.ash.reader.data.entity.ArticleWithFeed
import me.ash.reader.data.entity.Feed
-import me.ash.reader.data.entity.Filter
+import me.ash.reader.data.model.Filter
import me.ash.reader.data.preference.*
import me.ash.reader.ui.component.*
import me.ash.reader.ui.ext.surfaceColorAtElevation
diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateViewModel.kt
index 03739ca..1a4e54a 100644
--- a/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateViewModel.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateViewModel.kt
@@ -5,9 +5,9 @@ import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
-import me.ash.reader.BuildConfig
import me.ash.reader.data.repository.AppRepository
import me.ash.reader.data.source.Download
+import me.ash.reader.ui.ext.notFdroid
import javax.inject.Inject
@HiltViewModel
@@ -33,7 +33,7 @@ class UpdateViewModel @Inject constructor(
preProcessor: suspend () -> Unit = {},
postProcessor: suspend (Boolean) -> Unit = {}
) {
- if (BuildConfig.FLAVOR != "fdroid") {
+ if (notFdroid) {
viewModelScope.launch {
preProcessor()
appRepository.checkUpdate().let {