Add click notification feature
This commit is contained in:
parent
bfe1307b27
commit
ee9e0a4553
|
@ -1,6 +1,10 @@
|
||||||
package me.ash.reader
|
package me.ash.reader
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
import dagger.hilt.android.HiltAndroidApp
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
@ -14,6 +18,10 @@ import me.ash.reader.data.source.ReaderDatabase
|
||||||
import me.ash.reader.data.source.RssNetworkDataSource
|
import me.ash.reader.data.source.RssNetworkDataSource
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ExperimentalAnimationApi
|
||||||
|
@ExperimentalMaterial3Api
|
||||||
|
@ExperimentalPagerApi
|
||||||
|
@ExperimentalFoundationApi
|
||||||
@DelicateCoroutinesApi
|
@DelicateCoroutinesApi
|
||||||
@HiltAndroidApp
|
@HiltAndroidApp
|
||||||
class App : Application() {
|
class App : Application() {
|
||||||
|
|
|
@ -271,6 +271,15 @@ interface ArticleDao {
|
||||||
)
|
)
|
||||||
suspend fun queryLatestByFeedId(accountId: Int, feedId: Int): Article?
|
suspend fun queryLatestByFeedId(accountId: Int, feedId: Int): Article?
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
SELECT * FROM article
|
||||||
|
WHERE id = :id
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun queryById(id: Int): ArticleWithFeed?
|
||||||
|
|
||||||
@Insert
|
@Insert
|
||||||
suspend fun insert(article: Article): Long
|
suspend fun insert(article: Article): Long
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package me.ash.reader.ui.data
|
package me.ash.reader.data.constant
|
||||||
|
|
||||||
class Filter(
|
class Filter(
|
||||||
var index: Int,
|
var index: Int,
|
|
@ -1,7 +1,9 @@
|
||||||
package me.ash.reader.ui.data
|
package me.ash.reader.data.constant
|
||||||
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.rounded.*
|
import androidx.compose.material.icons.rounded.FiberManualRecord
|
||||||
|
import androidx.compose.material.icons.rounded.Star
|
||||||
|
import androidx.compose.material.icons.rounded.Subject
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
|
||||||
class NavigationBarItem(
|
class NavigationBarItem(
|
7
app/src/main/java/me/ash/reader/data/constant/Symbol.kt
Normal file
7
app/src/main/java/me/ash/reader/data/constant/Symbol.kt
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package me.ash.reader.data.constant
|
||||||
|
|
||||||
|
object Symbol {
|
||||||
|
const val NOTHING: String = "null"
|
||||||
|
const val NOTIFICATION_CHANNEL_GROUP_ARTICLE_UPDATE: String = "article.update"
|
||||||
|
const val EXTRA_ARTICLE_ID: String = "article.id"
|
||||||
|
}
|
|
@ -86,4 +86,8 @@ class ArticleRepository @Inject constructor(
|
||||||
suspend fun updateArticleInfo(article: Article) {
|
suspend fun updateArticleInfo(article: Article) {
|
||||||
articleDao.update(article)
|
articleDao.update(article)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun findArticleById(id: Int): ArticleWithFeed? {
|
||||||
|
return articleDao.queryById(id)
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -3,29 +3,34 @@ package me.ash.reader.data.repository
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
import android.app.NotificationChannel
|
import android.app.NotificationChannel
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.content.ContextCompat.getSystemService
|
import androidx.core.content.ContextCompat.getSystemService
|
||||||
import androidx.work.*
|
import androidx.work.*
|
||||||
import com.github.muhrifqii.parserss.ParseRSS
|
import com.github.muhrifqii.parserss.ParseRSS
|
||||||
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import me.ash.reader.DataStoreKeys
|
import me.ash.reader.*
|
||||||
import me.ash.reader.R
|
import me.ash.reader.R
|
||||||
import me.ash.reader.data.account.AccountDao
|
import me.ash.reader.data.account.AccountDao
|
||||||
import me.ash.reader.data.article.Article
|
import me.ash.reader.data.article.Article
|
||||||
import me.ash.reader.data.article.ArticleDao
|
import me.ash.reader.data.article.ArticleDao
|
||||||
|
import me.ash.reader.data.constant.Symbol
|
||||||
import me.ash.reader.data.feed.Feed
|
import me.ash.reader.data.feed.Feed
|
||||||
import me.ash.reader.data.feed.FeedDao
|
import me.ash.reader.data.feed.FeedDao
|
||||||
import me.ash.reader.data.source.ReaderDatabase
|
import me.ash.reader.data.source.ReaderDatabase
|
||||||
import me.ash.reader.data.source.RssNetworkDataSource
|
import me.ash.reader.data.source.RssNetworkDataSource
|
||||||
import me.ash.reader.dataStore
|
|
||||||
import me.ash.reader.get
|
|
||||||
import net.dankito.readability4j.Readability4J
|
import net.dankito.readability4j.Readability4J
|
||||||
import net.dankito.readability4j.extended.Readability4JExtended
|
import net.dankito.readability4j.extended.Readability4JExtended
|
||||||
import okhttp3.*
|
import okhttp3.*
|
||||||
|
@ -34,7 +39,6 @@ import java.io.IOException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.random.Random
|
|
||||||
|
|
||||||
|
|
||||||
class RssRepository @Inject constructor(
|
class RssRepository @Inject constructor(
|
||||||
|
@ -83,6 +87,10 @@ class RssRepository @Inject constructor(
|
||||||
return workManager.getWorkInfosByTag("sync").get().size.toString()
|
return workManager.getWorkInfosByTag("sync").get().size.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExperimentalAnimationApi
|
||||||
|
@ExperimentalMaterial3Api
|
||||||
|
@ExperimentalPagerApi
|
||||||
|
@ExperimentalFoundationApi
|
||||||
suspend fun sync(isWork: Boolean? = false) {
|
suspend fun sync(isWork: Boolean? = false) {
|
||||||
if (isWork == true) {
|
if (isWork == true) {
|
||||||
workManager.cancelAllWork()
|
workManager.cancelAllWork()
|
||||||
|
@ -92,7 +100,6 @@ class RssRepository @Inject constructor(
|
||||||
).setConstraints(
|
).setConstraints(
|
||||||
Constraints.Builder()
|
Constraints.Builder()
|
||||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||||
.setRequiresCharging(true)
|
|
||||||
.build()
|
.build()
|
||||||
).addTag("sync").build()
|
).addTag("sync").build()
|
||||||
workManager.enqueue(syncWorkerRequest)
|
workManager.enqueue(syncWorkerRequest)
|
||||||
|
@ -101,6 +108,10 @@ class RssRepository @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExperimentalAnimationApi
|
||||||
|
@ExperimentalMaterial3Api
|
||||||
|
@ExperimentalPagerApi
|
||||||
|
@ExperimentalFoundationApi
|
||||||
@DelicateCoroutinesApi
|
@DelicateCoroutinesApi
|
||||||
companion object {
|
companion object {
|
||||||
data class SyncState(
|
data class SyncState(
|
||||||
|
@ -194,26 +205,38 @@ class RssRepository @Inject constructor(
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
notificationManager.createNotificationChannel(
|
notificationManager.createNotificationChannel(
|
||||||
NotificationChannel(
|
NotificationChannel(
|
||||||
"ARTICLE_UPDATE",
|
Symbol.NOTIFICATION_CHANNEL_GROUP_ARTICLE_UPDATE,
|
||||||
"文章更新",
|
"文章更新",
|
||||||
NotificationManager.IMPORTANCE_DEFAULT
|
NotificationManager.IMPORTANCE_DEFAULT
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
it.reversed().forEachIndexed { index, articleList ->
|
it.reversed().forEach { articleList ->
|
||||||
articleList.forEach { article ->
|
val ids = articleDao.insertList(articleList)
|
||||||
Log.i("RlOG", "combine $index ${article.feedId}: ${article.title}")
|
articleList.forEachIndexed { index, article ->
|
||||||
val builder = NotificationCompat.Builder(context, "ARTICLE_UPDATE")
|
Log.i("RlOG", "combine ${article.feedId}: ${article.title}")
|
||||||
|
val builder = NotificationCompat.Builder(context, Symbol.NOTIFICATION_CHANNEL_GROUP_ARTICLE_UPDATE)
|
||||||
.setSmallIcon(R.drawable.ic_launcher_foreground)
|
.setSmallIcon(R.drawable.ic_launcher_foreground)
|
||||||
.setGroup("ARTICLE_UPDATE")
|
.setGroup(Symbol.NOTIFICATION_CHANNEL_GROUP_ARTICLE_UPDATE)
|
||||||
.setContentTitle(article.title)
|
.setContentTitle(article.title)
|
||||||
.setContentText(article.shortDescription)
|
.setContentText(article.shortDescription)
|
||||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||||
notificationManager.notify(Random.nextInt(), builder.build().apply {
|
.setContentIntent(
|
||||||
|
PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
ids[index].toInt(),
|
||||||
|
Intent(context, MainActivity::class.java).apply {
|
||||||
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK or
|
||||||
|
Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||||
|
putExtra(Symbol.EXTRA_ARTICLE_ID, ids[index].toInt())
|
||||||
|
},
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
)
|
||||||
|
)
|
||||||
|
notificationManager.notify(ids[index].toInt(), builder.build().apply {
|
||||||
flags = Notification.FLAG_AUTO_CANCEL
|
flags = Notification.FLAG_AUTO_CANCEL
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
articleDao.insertList(articleList)
|
|
||||||
}
|
}
|
||||||
}.buffer().onCompletion {
|
}.buffer().onCompletion {
|
||||||
val afterTime = System.currentTimeMillis()
|
val afterTime = System.currentTimeMillis()
|
||||||
|
@ -323,6 +346,10 @@ class RssRepository @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExperimentalAnimationApi
|
||||||
|
@ExperimentalMaterial3Api
|
||||||
|
@ExperimentalPagerApi
|
||||||
|
@ExperimentalFoundationApi
|
||||||
@DelicateCoroutinesApi
|
@DelicateCoroutinesApi
|
||||||
class SyncWorker(
|
class SyncWorker(
|
||||||
context: Context,
|
context: Context,
|
||||||
|
|
|
@ -12,12 +12,16 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.navigation.compose.NavHost
|
||||||
|
import androidx.navigation.compose.composable
|
||||||
|
import androidx.navigation.compose.rememberNavController
|
||||||
import com.google.accompanist.insets.ProvideWindowInsets
|
import com.google.accompanist.insets.ProvideWindowInsets
|
||||||
import com.google.accompanist.insets.navigationBarsHeight
|
import com.google.accompanist.insets.navigationBarsHeight
|
||||||
import com.google.accompanist.insets.statusBarsPadding
|
import com.google.accompanist.insets.statusBarsPadding
|
||||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||||
import me.ash.reader.ui.page.home.HomePage
|
import me.ash.reader.ui.page.home.HomePage
|
||||||
|
import me.ash.reader.ui.page.settings.SettingsPage
|
||||||
import me.ash.reader.ui.theme.AppTheme
|
import me.ash.reader.ui.theme.AppTheme
|
||||||
|
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
|
@ -26,6 +30,8 @@ import me.ash.reader.ui.theme.AppTheme
|
||||||
@ExperimentalFoundationApi
|
@ExperimentalFoundationApi
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeEntry() {
|
fun HomeEntry() {
|
||||||
|
val navController = rememberNavController()
|
||||||
|
|
||||||
AppTheme {
|
AppTheme {
|
||||||
ProvideWindowInsets {
|
ProvideWindowInsets {
|
||||||
rememberSystemUiController().run {
|
rememberSystemUiController().run {
|
||||||
|
@ -33,13 +39,24 @@ fun HomeEntry() {
|
||||||
setSystemBarsColor(MaterialTheme.colorScheme.surface, !isSystemInDarkTheme())
|
setSystemBarsColor(MaterialTheme.colorScheme.surface, !isSystemInDarkTheme())
|
||||||
setNavigationBarColor(MaterialTheme.colorScheme.surface, !isSystemInDarkTheme())
|
setNavigationBarColor(MaterialTheme.colorScheme.surface, !isSystemInDarkTheme())
|
||||||
}
|
}
|
||||||
Column (modifier = Modifier.background(MaterialTheme.colorScheme.surface)){
|
Column(modifier = Modifier.background(MaterialTheme.colorScheme.surface)) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
.statusBarsPadding()
|
.statusBarsPadding()
|
||||||
) {
|
) {
|
||||||
HomePage()
|
NavHost(
|
||||||
|
modifier = Modifier.background(MaterialTheme.colorScheme.surface),
|
||||||
|
navController = navController,
|
||||||
|
startDestination = RouteName.HOME,
|
||||||
|
) {
|
||||||
|
composable(route = RouteName.HOME) {
|
||||||
|
HomePage(navController)
|
||||||
|
}
|
||||||
|
composable(route = RouteName.SETTINGS) {
|
||||||
|
SettingsPage(navController)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Spacer(
|
Spacer(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package me.ash.reader.ui.page.common
|
package me.ash.reader.ui.page.common
|
||||||
|
|
||||||
object RouteName {
|
object RouteName {
|
||||||
|
const val HOME = "home"
|
||||||
const val FEED = "feed"
|
const val FEED = "feed"
|
||||||
const val ARTICLE = "article"
|
const val ARTICLE = "article"
|
||||||
const val READ = "read"
|
const val READ = "read"
|
||||||
|
const val SETTINGS = "settings"
|
||||||
}
|
}
|
|
@ -4,28 +4,31 @@ import android.util.Log
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.NavHostController
|
||||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
import com.google.accompanist.pager.HorizontalPager
|
import com.google.accompanist.pager.HorizontalPager
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import me.ash.reader.data.constant.Symbol
|
||||||
import me.ash.reader.ui.page.home.article.ArticlePage
|
import me.ash.reader.ui.page.home.article.ArticlePage
|
||||||
import me.ash.reader.ui.page.home.feed.FeedPage
|
import me.ash.reader.ui.page.home.feed.FeedPage
|
||||||
import me.ash.reader.ui.page.home.read.ReadPage
|
import me.ash.reader.ui.page.home.read.ReadPage
|
||||||
import me.ash.reader.ui.page.home.read.ReadViewAction
|
import me.ash.reader.ui.page.home.read.ReadViewAction
|
||||||
import me.ash.reader.ui.page.home.read.ReadViewModel
|
import me.ash.reader.ui.page.home.read.ReadViewModel
|
||||||
import me.ash.reader.ui.util.collectAsStateValue
|
import me.ash.reader.ui.util.collectAsStateValue
|
||||||
|
import me.ash.reader.ui.util.findActivity
|
||||||
import me.ash.reader.ui.util.pagerAnimate
|
import me.ash.reader.ui.util.pagerAnimate
|
||||||
import me.ash.reader.ui.widget.AppNavigationBar
|
import me.ash.reader.ui.widget.AppNavigationBar
|
||||||
|
|
||||||
|
@ -35,15 +38,44 @@ import me.ash.reader.ui.widget.AppNavigationBar
|
||||||
@ExperimentalFoundationApi
|
@ExperimentalFoundationApi
|
||||||
@Composable
|
@Composable
|
||||||
fun HomePage(
|
fun HomePage(
|
||||||
|
navController: NavHostController,
|
||||||
viewModel: HomeViewModel = hiltViewModel(),
|
viewModel: HomeViewModel = hiltViewModel(),
|
||||||
readViewModel: ReadViewModel = hiltViewModel(),
|
readViewModel: ReadViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
val viewState = viewModel.viewState.collectAsStateValue()
|
val viewState = viewModel.viewState.collectAsStateValue()
|
||||||
val filterState = viewModel.filterState.collectAsStateValue()
|
val filterState = viewModel.filterState.collectAsStateValue()
|
||||||
val readState = readViewModel.viewState.collectAsStateValue()
|
val readState = readViewModel.viewState.collectAsStateValue()
|
||||||
val navController = rememberNavController()
|
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
DisposableEffect(Unit) {
|
||||||
|
context.findActivity()?.let { activity ->
|
||||||
|
activity.intent?.let { intent ->
|
||||||
|
intent.extras?.get(Symbol.EXTRA_ARTICLE_ID)?.let {
|
||||||
|
readViewModel.dispatch(ReadViewAction.ScrollToItem(2))
|
||||||
|
scope.launch {
|
||||||
|
val article =
|
||||||
|
readViewModel.articleRepository.findArticleById(it.toString().toInt())
|
||||||
|
?: return@launch
|
||||||
|
readViewModel.dispatch(ReadViewAction.InitData(article))
|
||||||
|
if (article.feed.isFullContent) readViewModel.dispatch(ReadViewAction.RenderFullContent)
|
||||||
|
else readViewModel.dispatch(ReadViewAction.RenderDescriptionContent)
|
||||||
|
readViewModel.dispatch(ReadViewAction.RenderDescriptionContent)
|
||||||
|
viewModel.dispatch(
|
||||||
|
HomeViewAction.ScrollToPage(
|
||||||
|
scope = scope,
|
||||||
|
targetPage = 2,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intent.extras?.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDispose { }
|
||||||
|
}
|
||||||
|
|
||||||
BackHandler(true) {
|
BackHandler(true) {
|
||||||
val currentPage = viewState.pagerState.currentPage
|
val currentPage = viewState.pagerState.currentPage
|
||||||
viewModel.dispatch(
|
viewModel.dispatch(
|
||||||
|
@ -115,12 +147,11 @@ fun HomePage(
|
||||||
HorizontalPager(
|
HorizontalPager(
|
||||||
count = 3,
|
count = 3,
|
||||||
state = viewState.pagerState,
|
state = viewState.pagerState,
|
||||||
modifier = Modifier
|
modifier = Modifier.weight(1f)
|
||||||
.weight(1f)
|
|
||||||
.background(MaterialTheme.colorScheme.surface),
|
|
||||||
) { page ->
|
) { page ->
|
||||||
when (page) {
|
when (page) {
|
||||||
0 -> FeedPage(
|
0 -> FeedPage(
|
||||||
|
navController = navController,
|
||||||
modifier = Modifier.pagerAnimate(this, page),
|
modifier = Modifier.pagerAnimate(this, page),
|
||||||
filter = filterState.filter,
|
filter = filterState.filter,
|
||||||
groupAndFeedOnClick = { currentGroup, currentFeed ->
|
groupAndFeedOnClick = { currentGroup, currentFeed ->
|
||||||
|
@ -141,6 +172,7 @@ fun HomePage(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
1 -> ArticlePage(
|
1 -> ArticlePage(
|
||||||
|
navController = navController,
|
||||||
modifier = Modifier.pagerAnimate(this, page),
|
modifier = Modifier.pagerAnimate(this, page),
|
||||||
BackOnClick = {
|
BackOnClick = {
|
||||||
viewModel.dispatch(
|
viewModel.dispatch(
|
||||||
|
@ -165,6 +197,7 @@ fun HomePage(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
2 -> ReadPage(
|
2 -> ReadPage(
|
||||||
|
navController = navController,
|
||||||
modifier = Modifier.pagerAnimate(this, page),
|
modifier = Modifier.pagerAnimate(this, page),
|
||||||
btnBackOnClickListener = {
|
btnBackOnClickListener = {
|
||||||
viewModel.dispatch(
|
viewModel.dispatch(
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
package me.ash.reader.ui.page.home
|
package me.ash.reader.ui.page.home
|
||||||
|
|
||||||
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
|
@ -15,10 +18,13 @@ import kotlinx.coroutines.launch
|
||||||
import me.ash.reader.data.feed.Feed
|
import me.ash.reader.data.feed.Feed
|
||||||
import me.ash.reader.data.group.Group
|
import me.ash.reader.data.group.Group
|
||||||
import me.ash.reader.data.repository.RssRepository
|
import me.ash.reader.data.repository.RssRepository
|
||||||
import me.ash.reader.ui.data.Filter
|
import me.ash.reader.data.constant.Filter
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ExperimentalAnimationApi
|
||||||
|
@ExperimentalMaterial3Api
|
||||||
@ExperimentalPagerApi
|
@ExperimentalPagerApi
|
||||||
|
@ExperimentalFoundationApi
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class HomeViewModel @Inject constructor(
|
class HomeViewModel @Inject constructor(
|
||||||
private val rssRepository: RssRepository,
|
private val rssRepository: RssRepository,
|
||||||
|
|
|
@ -30,6 +30,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
import androidx.paging.compose.collectAsLazyPagingItems
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
import com.google.accompanist.swiperefresh.SwipeRefresh
|
import com.google.accompanist.swiperefresh.SwipeRefresh
|
||||||
|
@ -40,8 +41,8 @@ import me.ash.reader.DateTimeExt
|
||||||
import me.ash.reader.DateTimeExt.toString
|
import me.ash.reader.DateTimeExt.toString
|
||||||
import me.ash.reader.R
|
import me.ash.reader.R
|
||||||
import me.ash.reader.data.article.ArticleWithFeed
|
import me.ash.reader.data.article.ArticleWithFeed
|
||||||
|
import me.ash.reader.data.constant.Filter
|
||||||
import me.ash.reader.data.repository.RssRepository
|
import me.ash.reader.data.repository.RssRepository
|
||||||
import me.ash.reader.ui.data.Filter
|
|
||||||
import me.ash.reader.ui.page.home.HomeViewAction
|
import me.ash.reader.ui.page.home.HomeViewAction
|
||||||
import me.ash.reader.ui.page.home.HomeViewModel
|
import me.ash.reader.ui.page.home.HomeViewModel
|
||||||
import me.ash.reader.ui.util.collectAsStateValue
|
import me.ash.reader.ui.util.collectAsStateValue
|
||||||
|
@ -57,6 +58,7 @@ import me.ash.reader.ui.widget.TopTitleBox
|
||||||
@ExperimentalFoundationApi
|
@ExperimentalFoundationApi
|
||||||
@Composable
|
@Composable
|
||||||
fun ArticlePage(
|
fun ArticlePage(
|
||||||
|
navController: NavHostController,
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
homeViewModel: HomeViewModel = hiltViewModel(),
|
homeViewModel: HomeViewModel = hiltViewModel(),
|
||||||
viewModel: ArticleViewModel = hiltViewModel(),
|
viewModel: ArticleViewModel = hiltViewModel(),
|
||||||
|
@ -92,7 +94,7 @@ fun ArticlePage(
|
||||||
homeViewModel.dispatch(HomeViewAction.Sync())
|
homeViewModel.dispatch(HomeViewAction.Sync())
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Box(modifier.background(MaterialTheme.colorScheme.surface)) {
|
Box {
|
||||||
TopTitleBox(
|
TopTitleBox(
|
||||||
title = when {
|
title = when {
|
||||||
filterState.group != null -> filterState.group.name
|
filterState.group != null -> filterState.group.name
|
||||||
|
@ -241,6 +243,7 @@ private fun ArticleItem(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Spacer(modifier = modifier.height(1.dp))
|
Spacer(modifier = modifier.height(1.dp))
|
||||||
|
Row {
|
||||||
if (true) {
|
if (true) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
@ -303,6 +306,7 @@ private fun ArticleItem(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
@ -6,7 +6,6 @@ import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.animation.*
|
import androidx.compose.animation.*
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
|
@ -26,15 +25,17 @@ import androidx.compose.ui.graphics.painter.BitmapPainter
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
import me.ash.reader.DateTimeExt
|
import me.ash.reader.DateTimeExt
|
||||||
import me.ash.reader.DateTimeExt.toString
|
import me.ash.reader.DateTimeExt.toString
|
||||||
|
import me.ash.reader.data.constant.Filter
|
||||||
import me.ash.reader.data.feed.Feed
|
import me.ash.reader.data.feed.Feed
|
||||||
import me.ash.reader.data.group.Group
|
import me.ash.reader.data.group.Group
|
||||||
import me.ash.reader.data.group.GroupWithFeed
|
import me.ash.reader.data.group.GroupWithFeed
|
||||||
import me.ash.reader.data.repository.RssRepository
|
import me.ash.reader.data.repository.RssRepository
|
||||||
import me.ash.reader.ui.data.Filter
|
import me.ash.reader.ui.page.common.RouteName
|
||||||
import me.ash.reader.ui.page.home.HomeViewAction
|
import me.ash.reader.ui.page.home.HomeViewAction
|
||||||
import me.ash.reader.ui.page.home.HomeViewModel
|
import me.ash.reader.ui.page.home.HomeViewModel
|
||||||
import me.ash.reader.ui.util.collectAsStateValue
|
import me.ash.reader.ui.util.collectAsStateValue
|
||||||
|
@ -47,6 +48,7 @@ import me.ash.reader.ui.widget.*
|
||||||
@ExperimentalFoundationApi
|
@ExperimentalFoundationApi
|
||||||
@Composable
|
@Composable
|
||||||
fun FeedPage(
|
fun FeedPage(
|
||||||
|
navController: NavHostController,
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
viewModel: FeedViewModel = hiltViewModel(),
|
viewModel: FeedViewModel = hiltViewModel(),
|
||||||
homeViewModel: HomeViewModel = hiltViewModel(),
|
homeViewModel: HomeViewModel = hiltViewModel(),
|
||||||
|
@ -84,9 +86,7 @@ fun FeedPage(
|
||||||
}
|
}
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier
|
modifier.fillMaxSize()
|
||||||
.fillMaxSize()
|
|
||||||
.background(MaterialTheme.colorScheme.surface)
|
|
||||||
) {
|
) {
|
||||||
TopTitleBox(
|
TopTitleBox(
|
||||||
title = viewState.account?.name ?: "未知账户",
|
title = viewState.account?.name ?: "未知账户",
|
||||||
|
@ -109,7 +109,7 @@ fun FeedPage(
|
||||||
title = {},
|
title = {},
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
IconButton(onClick = {
|
IconButton(onClick = {
|
||||||
|
navController.navigate(route = RouteName.SETTINGS)
|
||||||
}) {
|
}) {
|
||||||
Icon(
|
Icon(
|
||||||
modifier = Modifier.size(22.dp),
|
modifier = Modifier.size(22.dp),
|
||||||
|
|
|
@ -5,7 +5,6 @@ import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.compose.animation.*
|
import androidx.compose.animation.*
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||||
|
@ -24,6 +23,7 @@ import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
import com.airbnb.lottie.compose.LottieAnimation
|
import com.airbnb.lottie.compose.LottieAnimation
|
||||||
import com.airbnb.lottie.compose.LottieCompositionSpec
|
import com.airbnb.lottie.compose.LottieCompositionSpec
|
||||||
import com.airbnb.lottie.compose.rememberLottieComposition
|
import com.airbnb.lottie.compose.rememberLottieComposition
|
||||||
|
@ -44,6 +44,7 @@ import me.ash.reader.ui.widget.WebView
|
||||||
@ExperimentalFoundationApi
|
@ExperimentalFoundationApi
|
||||||
@Composable
|
@Composable
|
||||||
fun ReadPage(
|
fun ReadPage(
|
||||||
|
navController: NavHostController,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: ReadViewModel = hiltViewModel(),
|
viewModel: ReadViewModel = hiltViewModel(),
|
||||||
btnBackOnClickListener: () -> Unit,
|
btnBackOnClickListener: () -> Unit,
|
||||||
|
@ -66,9 +67,7 @@ fun ReadPage(
|
||||||
|
|
||||||
Box {
|
Box {
|
||||||
Column(
|
Column(
|
||||||
modifier
|
modifier.fillMaxSize()
|
||||||
.fillMaxSize()
|
|
||||||
.background(MaterialTheme.colorScheme.surface)
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
SmallTopAppBar(
|
SmallTopAppBar(
|
||||||
|
|
|
@ -16,7 +16,7 @@ import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class ReadViewModel @Inject constructor(
|
class ReadViewModel @Inject constructor(
|
||||||
private val articleRepository: ArticleRepository,
|
val articleRepository: ArticleRepository,
|
||||||
private val rssRepository: RssRepository,
|
private val rssRepository: RssRepository,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val _viewState = MutableStateFlow(ReadViewState())
|
private val _viewState = MutableStateFlow(ReadViewState())
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
package me.ash.reader.ui.page.settings
|
||||||
|
|
||||||
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
|
|
||||||
|
@ExperimentalAnimationApi
|
||||||
|
@ExperimentalMaterial3Api
|
||||||
|
@ExperimentalPagerApi
|
||||||
|
@ExperimentalFoundationApi
|
||||||
|
@Composable
|
||||||
|
fun SettingsPage(
|
||||||
|
navController: NavHostController,
|
||||||
|
) {
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
LargeTopAppBar(
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
text = "Settings",
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton(onClick = {
|
||||||
|
navController.popBackStack()
|
||||||
|
}) {
|
||||||
|
IconButton(onClick = { /*TODO*/ }) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.ArrowBack,
|
||||||
|
contentDescription = "Back",
|
||||||
|
tint = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
},
|
||||||
|
content = {
|
||||||
|
Text(text = "af")
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,5 +1,8 @@
|
||||||
package me.ash.reader.ui.util
|
package me.ash.reader.ui.util
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.ContextWrapper
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
@ -70,3 +73,9 @@ fun Modifier.roundClick(onClick: () -> Unit = {}) = this
|
||||||
fun Modifier.paddingFixedHorizontal(top: Dp = 0.dp, bottom: Dp = 0.dp) = this
|
fun Modifier.paddingFixedHorizontal(top: Dp = 0.dp, bottom: Dp = 0.dp) = this
|
||||||
.padding(horizontal = 10.dp)
|
.padding(horizontal = 10.dp)
|
||||||
.padding(top = top, bottom = bottom)
|
.padding(top = top, bottom = bottom)
|
||||||
|
|
||||||
|
fun Context.findActivity(): Activity? = when (this) {
|
||||||
|
is Activity -> this
|
||||||
|
is ContextWrapper -> baseContext.findActivity()
|
||||||
|
else -> null
|
||||||
|
}
|
|
@ -1,5 +0,0 @@
|
||||||
package me.ash.reader.ui.util
|
|
||||||
|
|
||||||
object Symbol {
|
|
||||||
const val nothing: String = "null"
|
|
||||||
}
|
|
|
@ -34,8 +34,8 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
import com.google.accompanist.pager.PagerState
|
import com.google.accompanist.pager.PagerState
|
||||||
import me.ash.reader.ui.data.Filter
|
import me.ash.reader.data.constant.Filter
|
||||||
import me.ash.reader.ui.data.NavigationBarItem
|
import me.ash.reader.data.constant.NavigationBarItem
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
@ExperimentalPagerApi
|
@ExperimentalPagerApi
|
||||||
|
|
Loading…
Reference in New Issue
Block a user