diff --git a/app/build.gradle b/app/build.gradle
index 14b037c..710c85c 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -54,6 +54,7 @@ dependencies {
implementation("com.google.accompanist:accompanist-navigation-animation:0.24.3-alpha")
implementation "androidx.datastore:datastore-preferences:1.0.0"
implementation "com.airbnb.android:lottie-compose:4.2.2"
+ implementation "androidx.hilt:hilt-work:1.0.0"
implementation "androidx.work:work-runtime-ktx:2.8.0-alpha01"
implementation "net.dankito.readability4j:readability4j:1.0.8"
implementation "androidx.navigation:navigation-compose:2.5.0-alpha01"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d61d9fa..c3c90ef 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,5 +1,6 @@
@@ -23,6 +24,11 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/java/me/ash/reader/App.kt b/app/src/main/java/me/ash/reader/App.kt
index f5d3855..a433564 100644
--- a/app/src/main/java/me/ash/reader/App.kt
+++ b/app/src/main/java/me/ash/reader/App.kt
@@ -1,22 +1,32 @@
package me.ash.reader
import android.app.Application
+import androidx.hilt.work.HiltWorkerFactory
+import androidx.work.*
import dagger.hilt.android.HiltAndroidApp
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
-import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import me.ash.reader.data.repository.*
import me.ash.reader.data.source.OpmlLocalDataSource
import me.ash.reader.data.source.ReaderDatabase
import me.ash.reader.data.source.RssNetworkDataSource
+import java.util.concurrent.TimeUnit
import javax.inject.Inject
@DelicateCoroutinesApi
@HiltAndroidApp
-class App : Application() {
+class App : Application(), Configuration.Provider {
@Inject
lateinit var readerDatabase: ReaderDatabase
+ @Inject
+ lateinit var workerFactory: HiltWorkerFactory
+
+ @Inject
+ lateinit var workManager: WorkManager
+
@Inject
lateinit var opmlLocalDataSource: OpmlLocalDataSource
@@ -44,15 +54,43 @@ class App : Application() {
@Inject
lateinit var rssRepository: RssRepository
+ private val applicationScope = CoroutineScope(Dispatchers.IO)
+
override fun onCreate() {
super.onCreate()
- GlobalScope.launch {
- if (accountRepository.isNoAccount()) {
- val account = accountRepository.addDefaultAccount()
- applicationContext.dataStore.put(DataStoreKeys.CurrentAccountId, account.id!!)
- applicationContext.dataStore.put(DataStoreKeys.CurrentAccountType, account.type)
- }
- rssRepository.get().doSync(true)
+ applicationScope.launch {
+ accountInit()
+ workerInit()
}
}
+
+ private suspend fun accountInit() {
+ if (accountRepository.isNoAccount()) {
+ val account = accountRepository.addDefaultAccount()
+ applicationContext.dataStore.put(DataStoreKeys.CurrentAccountId, account.id!!)
+ applicationContext.dataStore.put(DataStoreKeys.CurrentAccountType, account.type)
+ }
+ }
+
+ private fun workerInit() {
+ val repeatingRequest = PeriodicWorkRequestBuilder(
+ 15, TimeUnit.MINUTES
+ ).setConstraints(
+ Constraints.Builder()
+ .setRequiredNetworkType(NetworkType.CONNECTED)
+ .build()
+ ).addTag(SyncWorker.WORK_NAME).build()
+
+ workManager.enqueueUniquePeriodicWork(
+ SyncWorker.WORK_NAME,
+ ExistingPeriodicWorkPolicy.REPLACE,
+ repeatingRequest
+ )
+ }
+
+ override fun getWorkManagerConfiguration(): Configuration =
+ Configuration.Builder()
+ .setWorkerFactory(workerFactory)
+ .setMinimumLoggingLevel(android.util.Log.DEBUG)
+ .build()
}
\ No newline at end of file
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 91b0751..0149284 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,18 @@ 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 kotlinx.coroutines.DelicateCoroutinesApi
+import androidx.work.CoroutineWorker
+import androidx.work.WorkManager
+import androidx.work.WorkerParameters
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.sync.Mutex
import me.ash.reader.DataStoreKeys
import me.ash.reader.data.account.AccountDao
import me.ash.reader.data.article.Article
@@ -18,12 +25,9 @@ import me.ash.reader.data.feed.FeedDao
import me.ash.reader.data.group.Group
import me.ash.reader.data.group.GroupDao
import me.ash.reader.data.group.GroupWithFeed
-import me.ash.reader.data.source.ReaderDatabase
import me.ash.reader.data.source.RssNetworkDataSource
import me.ash.reader.dataStore
import me.ash.reader.get
-import java.util.concurrent.TimeUnit
-import javax.inject.Inject
abstract class AbstractRssRepository constructor(
private val context: Context,
@@ -43,19 +47,11 @@ abstract class AbstractRssRepository constructor(
val isNotSyncing: Boolean = !isSyncing
}
- abstract fun getSyncState(): StateFlow
-
abstract suspend fun updateArticleInfo(article: Article)
abstract suspend fun subscribe(feed: Feed, articles: List)
- abstract suspend fun sync(
- context: Context,
- accountDao: AccountDao,
- articleDao: ArticleDao,
- feedDao: FeedDao,
- rssNetworkDataSource: RssNetworkDataSource
- )
+ abstract suspend fun sync()
fun pullGroups(): Flow> {
val accountId = context.dataStore.get(DataStoreKeys.CurrentAccountId) ?: 0
@@ -135,46 +131,32 @@ abstract class AbstractRssRepository constructor(
return workManager.getWorkInfosByTag("sync").get().size.toString()
}
- suspend fun doSync(isWork: Boolean? = false) {
- if (isWork == true) {
- workManager.cancelAllWork()
- val syncWorkerRequest: WorkRequest =
- PeriodicWorkRequestBuilder(
- 15, TimeUnit.MINUTES
- ).setConstraints(
- Constraints.Builder()
- .setRequiredNetworkType(NetworkType.CONNECTED)
- .build()
- ).addTag("sync").build()
- workManager.enqueue(syncWorkerRequest)
- } else {
- sync(context, accountDao, articleDao, feedDao, rssNetworkDataSource)
+ companion object {
+ val mutex = Mutex()
+
+ private val _syncState = MutableStateFlow(SyncState())
+ val syncState = _syncState.asStateFlow()
+
+ fun updateSyncState(function: (SyncState) -> SyncState) {
+ _syncState.update(function)
}
}
}
-@DelicateCoroutinesApi
-class SyncWorker(
- context: Context,
- workerParams: WorkerParameters,
+@HiltWorker
+class SyncWorker @AssistedInject constructor(
+ @Assisted context: Context,
+ @Assisted workerParams: WorkerParameters,
+ private val rssRepository: RssRepository,
) : CoroutineWorker(context, workerParams) {
- @Inject
- lateinit var rssRepository: RssRepository
-
- @Inject
- lateinit var rssNetworkDataSource: RssNetworkDataSource
-
override suspend fun doWork(): Result {
Log.i("RLog", "doWork: ")
- val db = ReaderDatabase.getInstance(applicationContext)
- rssRepository.get().sync(
- applicationContext,
- db.accountDao(),
- db.articleDao(),
- db.feedDao(),
- rssNetworkDataSource
- )
+ rssRepository.get().sync()
return Result.success()
}
+
+ companion object {
+ const val WORK_NAME = "article.sync"
+ }
}
\ No newline at end of file
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
index d0c505e..ff7745d 100644
--- a/app/src/main/java/me/ash/reader/data/repository/FeverRssRepository.kt
+++ b/app/src/main/java/me/ash/reader/data/repository/FeverRssRepository.kt
@@ -4,9 +4,6 @@ import android.content.Context
import android.util.Log
import androidx.work.WorkManager
import dagger.hilt.android.qualifiers.ApplicationContext
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.update
-import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import me.ash.reader.DataStoreKeys
import me.ash.reader.data.account.AccountDao
@@ -33,18 +30,13 @@ class FeverRssRepository @Inject constructor(
private val groupDao: GroupDao,
private val rssHelper: RssHelper,
private val feverApiDataSource: FeverApiDataSource,
+ private val accountDao: AccountDao,
rssNetworkDataSource: RssNetworkDataSource,
- accountDao: AccountDao,
workManager: WorkManager,
) : AbstractRssRepository(
context, accountDao, articleDao, groupDao,
feedDao, rssNetworkDataSource, workManager,
) {
- private val mutex = Mutex()
- private val syncState = MutableStateFlow(SyncState())
-
- override fun getSyncState() = syncState
-
override suspend fun updateArticleInfo(article: Article) {
articleDao.update(article)
}
@@ -56,18 +48,12 @@ class FeverRssRepository @Inject constructor(
})
}
- override suspend fun sync(
- context: Context,
- accountDao: AccountDao,
- articleDao: ArticleDao,
- feedDao: FeedDao,
- rssNetworkDataSource: RssNetworkDataSource
- ) {
+ override suspend fun sync() {
mutex.withLock {
val accountId = context.dataStore.get(DataStoreKeys.CurrentAccountId)
?: return
- syncState.update {
+ updateSyncState {
it.copy(
feedCount = 1,
syncedCount = 1,
@@ -140,7 +126,7 @@ class FeverRssRepository @Inject constructor(
accountDao.update(accountDao.queryById(accountId)!!.apply {
updateAt = Date()
})
- syncState.update {
+ updateSyncState {
it.copy(
feedCount = 0,
syncedCount = 0,
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 8988612..a9d420e 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
@@ -13,7 +13,6 @@ import androidx.core.content.ContextCompat.getSystemService
import androidx.work.WorkManager
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.*
-import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import me.ash.reader.*
import me.ash.reader.data.account.AccountDao
@@ -34,19 +33,14 @@ class LocalRssRepository @Inject constructor(
private val articleDao: ArticleDao,
private val feedDao: FeedDao,
private val rssHelper: RssHelper,
- rssNetworkDataSource: RssNetworkDataSource,
+ private val rssNetworkDataSource: RssNetworkDataSource,
+ private val accountDao: AccountDao,
groupDao: GroupDao,
- accountDao: AccountDao,
workManager: WorkManager,
) : AbstractRssRepository(
context, accountDao, articleDao, groupDao,
feedDao, rssNetworkDataSource, workManager,
) {
- private val mutex = Mutex()
- private val syncState = MutableStateFlow(SyncState())
-
- override fun getSyncState() = syncState
-
override suspend fun updateArticleInfo(article: Article) {
articleDao.update(article)
}
@@ -58,13 +52,7 @@ class LocalRssRepository @Inject constructor(
})
}
- override suspend fun sync(
- context: Context,
- accountDao: AccountDao,
- articleDao: ArticleDao,
- feedDao: FeedDao,
- rssNetworkDataSource: RssNetworkDataSource
- ) {
+ override suspend fun sync() {
mutex.withLock {
val accountId = context.dataStore.get(DataStoreKeys.CurrentAccountId)
?: return
@@ -98,10 +86,10 @@ class LocalRssRepository @Inject constructor(
}
}
)
- syncState.update {
+ updateSyncState {
it.copy(
feedCount = feeds.size,
- syncedCount = syncState.value.syncedCount + 1,
+ syncedCount = it.syncedCount + 1,
currentFeedName = feed.name
)
}
@@ -173,7 +161,7 @@ class LocalRssRepository @Inject constructor(
}
)
}
- syncState.update {
+ updateSyncState {
it.copy(
feedCount = 0,
syncedCount = 0,
diff --git a/app/src/main/java/me/ash/reader/ui/page/home/HomePage.kt b/app/src/main/java/me/ash/reader/ui/page/home/HomePage.kt
index 67723d7..007bec8 100644
--- a/app/src/main/java/me/ash/reader/ui/page/home/HomePage.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/home/HomePage.kt
@@ -16,7 +16,6 @@ import androidx.navigation.NavHostController
import com.google.accompanist.pager.ExperimentalPagerApi
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
-import me.ash.reader.ui.page.common.NotificationGroupName
import me.ash.reader.ui.extension.collectAsStateValue
import me.ash.reader.ui.extension.findActivity
import me.ash.reader.ui.page.common.ExtraName
@@ -46,9 +45,9 @@ fun HomePage(
intent.extras?.get(ExtraName.ARTICLE_ID)?.let {
readViewModel.dispatch(ReadViewAction.ScrollToItem(2))
scope.launch {
- val article =
- readViewModel.rssRepository.get().findArticleById(it.toString().toInt())
- ?: return@launch
+ val article = readViewModel
+ .rssRepository.get()
+ .findArticleById(it.toString().toInt()) ?: return@launch
readViewModel.dispatch(ReadViewAction.InitData(article))
if (article.feed.isFullContent) readViewModel.dispatch(ReadViewAction.RenderFullContent)
else readViewModel.dispatch(ReadViewAction.RenderDescriptionContent)
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 3a5a3a8..ef7e6e0 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
@@ -15,6 +15,7 @@ import kotlinx.coroutines.launch
import me.ash.reader.data.constant.Filter
import me.ash.reader.data.feed.Feed
import me.ash.reader.data.group.Group
+import me.ash.reader.data.repository.AbstractRssRepository
import me.ash.reader.data.repository.RssRepository
import me.ash.reader.ui.extension.animateScrollToPage
import javax.inject.Inject
@@ -31,7 +32,7 @@ class HomeViewModel @Inject constructor(
private val _filterState = MutableStateFlow(FilterState())
val filterState = _filterState.asStateFlow()
- val syncState = rssRepository.get().getSyncState()
+ val syncState = AbstractRssRepository.syncState
fun dispatch(action: HomeViewAction) {
when (action) {
@@ -47,7 +48,7 @@ class HomeViewModel @Inject constructor(
private fun sync(callback: () -> Unit = {}) {
viewModelScope.launch(Dispatchers.IO) {
- rssRepository.get().doSync()
+ rssRepository.get().sync()
callback()
}
}
diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsViewModel.kt
index 892006a..89c8d07 100644
--- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsViewModel.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsViewModel.kt
@@ -50,7 +50,7 @@ class FeedsViewModel @Inject constructor(
private fun addFromFile(inputStream: InputStream) {
viewModelScope.launch(Dispatchers.IO) {
opmlRepository.saveToDatabase(inputStream)
- pullFeeds(isStarred = false, isUnread = false)
+ rssRepository.get().sync()
}
}
@@ -60,7 +60,6 @@ class FeedsViewModel @Inject constructor(
isStarred = filterState.filter.isStarred(),
isUnread = filterState.filter.isUnread(),
)
- _viewState
}
}
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 27b2ae0..0a2511d 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
@@ -1,5 +1,6 @@
package me.ash.reader.ui.page.home.flow
+import android.widget.Toast
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxWidth
@@ -77,7 +78,11 @@ fun FlowPage(
}
},
actions = {
- IconButton(onClick = {}) {
+ IconButton(onClick = {
+ viewModel.dispatch(FlowViewAction.PeekSyncWork)
+ Toast.makeText(context, viewState.syncWorkInfo.length.toString(), Toast.LENGTH_SHORT)
+ .show()
+ }) {
Icon(
imageVector = Icons.Rounded.DoneAll,
contentDescription = stringResource(R.string.mark_all_as_read),
diff --git a/gradle.properties b/gradle.properties
index 75833fb..3c7a8bd 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -21,9 +21,3 @@ kotlin.code.style=official
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
-
-# Custom
-org.gradle.daemon=true
-org.gradle.parallel=true
-org.gradle.configureondemand=true
-org.gradle.caching = true
\ No newline at end of file