Optimize extensions and components and packages
This commit is contained in:
parent
257a6332ac
commit
622d0e2cfd
|
@ -14,6 +14,9 @@ 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 me.ash.reader.ui.ext.DataStoreKeys
|
||||
import me.ash.reader.ui.ext.dataStore
|
||||
import me.ash.reader.ui.ext.put
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidApp
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
package me.ash.reader
|
||||
|
||||
fun Int.spacerDollar(str: Any): String = "$this$$str"
|
|
@ -1,17 +0,0 @@
|
|||
package me.ash.reader.data
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import java.util.*
|
||||
|
||||
class Converters {
|
||||
|
||||
@TypeConverter
|
||||
fun toDate(dateLong: Long?): Date? {
|
||||
return dateLong?.let { Date(it) }
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromDate(date: Date?): Long? {
|
||||
return date?.time
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package me.ash.reader.data.account
|
||||
package me.ash.reader.data.dao
|
||||
|
||||
import androidx.room.*
|
||||
import me.ash.reader.data.entity.Account
|
||||
|
||||
@Dao
|
||||
interface AccountDao {
|
|
@ -1,8 +1,11 @@
|
|||
package me.ash.reader.data.article
|
||||
package me.ash.reader.data.dao
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.room.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import me.ash.reader.data.entity.Article
|
||||
import me.ash.reader.data.entity.ArticleWithFeed
|
||||
import me.ash.reader.data.entity.ImportantCount
|
||||
|
||||
@Dao
|
||||
interface ArticleDao {
|
|
@ -1,6 +1,7 @@
|
|||
package me.ash.reader.data.feed
|
||||
package me.ash.reader.data.dao
|
||||
|
||||
import androidx.room.*
|
||||
import me.ash.reader.data.entity.Feed
|
||||
|
||||
@Dao
|
||||
interface FeedDao {
|
|
@ -1,7 +1,9 @@
|
|||
package me.ash.reader.data.group
|
||||
package me.ash.reader.data.dao
|
||||
|
||||
import androidx.room.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import me.ash.reader.data.entity.Group
|
||||
import me.ash.reader.data.entity.GroupWithFeed
|
||||
|
||||
@Dao
|
||||
interface GroupDao {
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader.data.account
|
||||
package me.ash.reader.data.entity
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
|
@ -1,10 +1,9 @@
|
|||
package me.ash.reader.data.article
|
||||
package me.ash.reader.data.entity
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.PrimaryKey
|
||||
import me.ash.reader.data.feed.Feed
|
||||
import java.util.*
|
||||
|
||||
@Entity(
|
|
@ -1,8 +1,7 @@
|
|||
package me.ash.reader.data.article
|
||||
package me.ash.reader.data.entity
|
||||
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Relation
|
||||
import me.ash.reader.data.feed.Feed
|
||||
|
||||
data class ArticleWithFeed(
|
||||
@Embedded
|
|
@ -1,7 +1,6 @@
|
|||
package me.ash.reader.data.feed
|
||||
package me.ash.reader.data.entity
|
||||
|
||||
import androidx.room.*
|
||||
import me.ash.reader.data.group.Group
|
||||
|
||||
@Entity(
|
||||
tableName = "feed",
|
|
@ -1,8 +1,7 @@
|
|||
package me.ash.reader.data.feed
|
||||
package me.ash.reader.data.entity
|
||||
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Relation
|
||||
import me.ash.reader.data.article.Article
|
||||
|
||||
data class FeedWithArticle(
|
||||
@Embedded
|
|
@ -1,8 +1,7 @@
|
|||
package me.ash.reader.data.feed
|
||||
package me.ash.reader.data.entity
|
||||
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Relation
|
||||
import me.ash.reader.data.group.Group
|
||||
|
||||
data class FeedWithGroup(
|
||||
@Embedded
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader.data.constant
|
||||
package me.ash.reader.data.entity
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.FiberManualRecord
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader.data.group
|
||||
package me.ash.reader.data.entity
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
|
@ -1,8 +1,7 @@
|
|||
package me.ash.reader.data.group
|
||||
package me.ash.reader.data.entity
|
||||
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Relation
|
||||
import me.ash.reader.data.feed.Feed
|
||||
|
||||
data class GroupWithFeed(
|
||||
@Embedded
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader.data.article
|
||||
package me.ash.reader.data.entity
|
||||
|
||||
data class ImportantCount(
|
||||
val important: Int,
|
|
@ -6,10 +6,10 @@ import dagger.Provides
|
|||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import me.ash.reader.data.account.AccountDao
|
||||
import me.ash.reader.data.article.ArticleDao
|
||||
import me.ash.reader.data.feed.FeedDao
|
||||
import me.ash.reader.data.group.GroupDao
|
||||
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.source.ReaderDatabase
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
|
|
@ -10,18 +10,13 @@ import dagger.assisted.AssistedInject
|
|||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import me.ash.reader.currentAccountId
|
||||
import me.ash.reader.data.account.AccountDao
|
||||
import me.ash.reader.data.article.Article
|
||||
import me.ash.reader.data.article.ArticleDao
|
||||
import me.ash.reader.data.article.ArticleWithFeed
|
||||
import me.ash.reader.data.article.ImportantCount
|
||||
import me.ash.reader.data.feed.Feed
|
||||
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.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.*
|
||||
import me.ash.reader.data.source.RssNetworkDataSource
|
||||
import me.ash.reader.ui.ext.currentAccountId
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
abstract class AbstractRssRepository constructor(
|
||||
|
|
|
@ -3,12 +3,12 @@ package me.ash.reader.data.repository
|
|||
import android.content.Context
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.currentAccountId
|
||||
import me.ash.reader.data.account.Account
|
||||
import me.ash.reader.data.account.AccountDao
|
||||
import me.ash.reader.data.group.Group
|
||||
import me.ash.reader.data.group.GroupDao
|
||||
import me.ash.reader.spacerDollar
|
||||
import me.ash.reader.data.dao.AccountDao
|
||||
import me.ash.reader.data.dao.GroupDao
|
||||
import me.ash.reader.data.entity.Account
|
||||
import me.ash.reader.data.entity.Group
|
||||
import me.ash.reader.ui.ext.currentAccountId
|
||||
import me.ash.reader.ui.ext.spacerDollar
|
||||
import javax.inject.Inject
|
||||
|
||||
class AccountRepository @Inject constructor(
|
||||
|
|
|
@ -8,20 +8,20 @@ import kotlinx.coroutines.CoroutineDispatcher
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import me.ash.reader.currentAccountId
|
||||
import me.ash.reader.data.account.AccountDao
|
||||
import me.ash.reader.data.article.Article
|
||||
import me.ash.reader.data.article.ArticleDao
|
||||
import me.ash.reader.data.feed.Feed
|
||||
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.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.spacerDollar
|
||||
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
|
||||
|
|
|
@ -14,18 +14,18 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
|||
import kotlinx.coroutines.*
|
||||
import me.ash.reader.MainActivity
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.currentAccountId
|
||||
import me.ash.reader.data.account.AccountDao
|
||||
import me.ash.reader.data.article.Article
|
||||
import me.ash.reader.data.article.ArticleDao
|
||||
import me.ash.reader.data.feed.Feed
|
||||
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.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.RssNetworkDataSource
|
||||
import me.ash.reader.ui.ext.currentAccountId
|
||||
import me.ash.reader.ui.page.common.ExtraName
|
||||
import me.ash.reader.ui.page.common.NotificationGroupName
|
||||
import java.util.*
|
||||
|
|
|
@ -8,13 +8,13 @@ import be.ceau.opml.entity.Opml
|
|||
import be.ceau.opml.entity.Outline
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.currentAccountId
|
||||
import me.ash.reader.data.account.AccountDao
|
||||
import me.ash.reader.data.feed.Feed
|
||||
import me.ash.reader.data.feed.FeedDao
|
||||
import me.ash.reader.data.group.GroupDao
|
||||
import me.ash.reader.data.dao.AccountDao
|
||||
import me.ash.reader.data.dao.FeedDao
|
||||
import me.ash.reader.data.dao.GroupDao
|
||||
import me.ash.reader.data.entity.Feed
|
||||
import me.ash.reader.data.source.OpmlLocalDataSource
|
||||
import me.ash.reader.spacerDollar
|
||||
import me.ash.reader.ui.ext.currentAccountId
|
||||
import me.ash.reader.ui.ext.spacerDollar
|
||||
import java.io.InputStream
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
|
|
@ -6,14 +6,14 @@ import android.util.Log
|
|||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.ash.reader.currentAccountId
|
||||
import me.ash.reader.data.article.Article
|
||||
import me.ash.reader.data.feed.Feed
|
||||
import me.ash.reader.data.feed.FeedDao
|
||||
import me.ash.reader.data.feed.FeedWithArticle
|
||||
import me.ash.reader.data.dao.FeedDao
|
||||
import me.ash.reader.data.entity.Article
|
||||
import me.ash.reader.data.entity.Feed
|
||||
import me.ash.reader.data.entity.FeedWithArticle
|
||||
import me.ash.reader.data.module.DispatcherIO
|
||||
import me.ash.reader.data.source.RssNetworkDataSource
|
||||
import me.ash.reader.spacerDollar
|
||||
import me.ash.reader.ui.ext.currentAccountId
|
||||
import me.ash.reader.ui.ext.spacerDollar
|
||||
import net.dankito.readability4j.Readability4J
|
||||
import net.dankito.readability4j.extended.Readability4JExtended
|
||||
import okhttp3.OkHttpClient
|
||||
|
|
|
@ -2,8 +2,8 @@ package me.ash.reader.data.repository
|
|||
|
||||
import android.content.Context
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import me.ash.reader.currentAccountType
|
||||
import me.ash.reader.data.account.Account
|
||||
import me.ash.reader.data.entity.Account
|
||||
import me.ash.reader.ui.ext.currentAccountType
|
||||
import javax.inject.Inject
|
||||
|
||||
class RssRepository @Inject constructor(
|
||||
|
|
|
@ -5,11 +5,11 @@ import be.ceau.opml.OpmlParser
|
|||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.ash.reader.currentAccountId
|
||||
import me.ash.reader.data.feed.Feed
|
||||
import me.ash.reader.data.group.Group
|
||||
import me.ash.reader.data.group.GroupWithFeed
|
||||
import me.ash.reader.data.entity.Feed
|
||||
import me.ash.reader.data.entity.Group
|
||||
import me.ash.reader.data.entity.GroupWithFeed
|
||||
import me.ash.reader.data.module.DispatcherIO
|
||||
import me.ash.reader.ui.ext.currentAccountId
|
||||
import java.io.InputStream
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
|
|
@ -1,26 +1,23 @@
|
|||
package me.ash.reader.data.source
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Database
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import me.ash.reader.data.Converters
|
||||
import me.ash.reader.data.account.Account
|
||||
import me.ash.reader.data.account.AccountDao
|
||||
import me.ash.reader.data.article.Article
|
||||
import me.ash.reader.data.article.ArticleDao
|
||||
import me.ash.reader.data.feed.Feed
|
||||
import me.ash.reader.data.feed.FeedDao
|
||||
import me.ash.reader.data.group.Group
|
||||
import me.ash.reader.data.group.GroupDao
|
||||
import androidx.room.*
|
||||
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.Account
|
||||
import me.ash.reader.data.entity.Article
|
||||
import me.ash.reader.data.entity.Feed
|
||||
import me.ash.reader.data.entity.Group
|
||||
import java.util.*
|
||||
|
||||
@Database(
|
||||
entities = [Account::class, Feed::class, Article::class, Group::class],
|
||||
version = 1,
|
||||
exportSchema = false,
|
||||
)
|
||||
@TypeConverters(Converters::class)
|
||||
@TypeConverters(ReaderDatabase.Converters::class)
|
||||
abstract class ReaderDatabase : RoomDatabase() {
|
||||
abstract fun accountDao(): AccountDao
|
||||
abstract fun feedDao(): FeedDao
|
||||
|
@ -42,4 +39,17 @@ abstract class ReaderDatabase : RoomDatabase() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Converters {
|
||||
|
||||
@TypeConverter
|
||||
fun toDate(dateLong: Long?): Date? {
|
||||
return dateLong?.let { Date(it) }
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromDate(date: Date?): Long? {
|
||||
return date?.time
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader.ui.widget
|
||||
package me.ash.reader.ui.component
|
||||
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.FastOutSlowInEasing
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader.ui.widget
|
||||
package me.ash.reader.ui.component
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.background
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader.ui.widget
|
||||
package me.ash.reader.ui.component
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader.ui.widget
|
||||
package me.ash.reader.ui.component
|
||||
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Icon
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader.ui.widget
|
||||
package me.ash.reader.ui.component
|
||||
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.runtime.Composable
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader.ui.widget
|
||||
package me.ash.reader.ui.component
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader.ui.widget
|
||||
package me.ash.reader.ui.component
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.material3.DropdownMenu
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader.ui.widget
|
||||
package me.ash.reader.ui.component
|
||||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
|
@ -153,7 +153,7 @@ fun SelectionEditorChip(
|
|||
onValueChange = { onValueChange(it) },
|
||||
cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurfaceVariant),
|
||||
textStyle = MaterialTheme.typography.titleSmall.copy(
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
decorationBox = { innerTextField ->
|
||||
Row(
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader.ui.widget
|
||||
package me.ash.reader.ui.component
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader.ui.widget
|
||||
package me.ash.reader.ui.component
|
||||
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader.ui.widget
|
||||
package me.ash.reader.ui.component
|
||||
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
|
@ -8,6 +8,9 @@ import android.util.Log
|
|||
import android.webkit.*
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
@ -29,80 +32,94 @@ fun WebView(
|
|||
val context = LocalContext.current
|
||||
val color = MaterialTheme.colorScheme.onSurfaceVariant.toArgb()
|
||||
val backgroundColor = MaterialTheme.colorScheme.surface.toArgb()
|
||||
val webViewClient = object : WebViewClient() {
|
||||
val webViewClient by remember {
|
||||
mutableStateOf(object : WebViewClient() {
|
||||
|
||||
override fun shouldInterceptRequest(view: WebView?, url: String?): WebResourceResponse? {
|
||||
if (url != null && url.contains(INJECTION_TOKEN)) {
|
||||
try {
|
||||
val assetPath = url.substring(
|
||||
url.indexOf(INJECTION_TOKEN) + INJECTION_TOKEN.length,
|
||||
url.length
|
||||
)
|
||||
return WebResourceResponse(
|
||||
"text/HTML",
|
||||
"UTF-8",
|
||||
context.assets.open(assetPath)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e("RLog", "WebView shouldInterceptRequest: $e")
|
||||
override fun shouldInterceptRequest(
|
||||
view: WebView?,
|
||||
url: String?
|
||||
): WebResourceResponse? {
|
||||
if (url != null && url.contains(INJECTION_TOKEN)) {
|
||||
try {
|
||||
val assetPath = url.substring(
|
||||
url.indexOf(INJECTION_TOKEN) + INJECTION_TOKEN.length,
|
||||
url.length
|
||||
)
|
||||
return WebResourceResponse(
|
||||
"text/HTML",
|
||||
"UTF-8",
|
||||
context.assets.open(assetPath)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e("RLog", "WebView shouldInterceptRequest: $e")
|
||||
}
|
||||
}
|
||||
return super.shouldInterceptRequest(view, url);
|
||||
}
|
||||
return super.shouldInterceptRequest(view, url);
|
||||
}
|
||||
|
||||
override fun onPageStarted(
|
||||
view: WebView?,
|
||||
url: String?,
|
||||
favicon: Bitmap?
|
||||
) {
|
||||
super.onPageStarted(view, url, favicon)
|
||||
override fun onPageStarted(
|
||||
view: WebView?,
|
||||
url: String?,
|
||||
favicon: Bitmap?
|
||||
) {
|
||||
super.onPageStarted(view, url, favicon)
|
||||
// _isLoading = true
|
||||
onProgressChange(-1)
|
||||
}
|
||||
onProgressChange(-1)
|
||||
}
|
||||
|
||||
override fun onPageFinished(view: WebView?, url: String?) {
|
||||
super.onPageFinished(view, url)
|
||||
val jsCode = "javascript:(function(){" +
|
||||
"var imgs=document.getElementsByTagName(\"img\");" +
|
||||
"for(var i=0;i<imgs.length;i++){" +
|
||||
"imgs[i].pos = i;" +
|
||||
"imgs[i].onclick=function(){" +
|
||||
override fun onPageFinished(view: WebView?, url: String?) {
|
||||
super.onPageFinished(view, url)
|
||||
val jsCode = "javascript:(function(){" +
|
||||
"var imgs=document.getElementsByTagName(\"img\");" +
|
||||
"for(var i=0;i<imgs.length;i++){" +
|
||||
"imgs[i].pos = i;" +
|
||||
"imgs[i].onclick=function(){" +
|
||||
// "window.jsCallJavaObj.openImage(this.src,this.pos);" +
|
||||
"alert('asf');" +
|
||||
"}}})()"
|
||||
view!!.loadUrl(jsCode)
|
||||
viewModel.dispatch(ReadViewAction.ChangeLoading(false))
|
||||
onProgressChange(100)
|
||||
}
|
||||
"alert('asf');" +
|
||||
"}}})()"
|
||||
view!!.loadUrl(jsCode)
|
||||
viewModel.dispatch(ReadViewAction.ChangeLoading(false))
|
||||
onProgressChange(100)
|
||||
}
|
||||
|
||||
override fun shouldOverrideUrlLoading(
|
||||
view: WebView?,
|
||||
request: WebResourceRequest?
|
||||
): Boolean {
|
||||
if (null == request?.url) return false
|
||||
val url = request.url.toString()
|
||||
context.startActivity(
|
||||
Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
||||
)
|
||||
return true
|
||||
}
|
||||
override fun shouldOverrideUrlLoading(
|
||||
view: WebView?,
|
||||
request: WebResourceRequest?
|
||||
): Boolean {
|
||||
if (null == request?.url) return false
|
||||
val url = request.url.toString()
|
||||
context.startActivity(
|
||||
Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onReceivedError(
|
||||
view: WebView?,
|
||||
request: WebResourceRequest?,
|
||||
error: WebResourceError?
|
||||
) {
|
||||
super.onReceivedError(view, request, error)
|
||||
onReceivedError(error)
|
||||
}
|
||||
override fun onReceivedError(
|
||||
view: WebView?,
|
||||
request: WebResourceRequest?,
|
||||
error: WebResourceError?
|
||||
) {
|
||||
super.onReceivedError(view, request, error)
|
||||
onReceivedError(error)
|
||||
}
|
||||
|
||||
override fun onReceivedSslError(
|
||||
view: WebView?,
|
||||
handler: SslErrorHandler?,
|
||||
error: SslError?
|
||||
) {
|
||||
handler?.cancel()
|
||||
}
|
||||
override fun onReceivedSslError(
|
||||
view: WebView?,
|
||||
handler: SslErrorHandler?,
|
||||
error: SslError?
|
||||
) {
|
||||
handler?.cancel()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
val webView by remember {
|
||||
mutableStateOf(WebView(context).apply {
|
||||
this.webViewClient = webViewClient
|
||||
setBackgroundColor(backgroundColor)
|
||||
isHorizontalScrollBarEnabled = false
|
||||
isVerticalScrollBarEnabled = false
|
||||
})
|
||||
}
|
||||
|
||||
// Column(
|
||||
|
@ -121,14 +138,7 @@ fun WebView(
|
|||
|
||||
AndroidView(
|
||||
modifier = modifier,
|
||||
factory = {
|
||||
WebView(it).apply {
|
||||
this.webViewClient = webViewClient
|
||||
setBackgroundColor(backgroundColor)
|
||||
isHorizontalScrollBarEnabled = false
|
||||
isVerticalScrollBarEnabled = false
|
||||
}
|
||||
},
|
||||
factory = { webView },
|
||||
update = {
|
||||
it.apply {
|
||||
Log.i("RLog", "CustomWebView: ${content}")
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader.ui.extension
|
||||
package me.ash.reader.ui.ext
|
||||
|
||||
import androidx.compose.material3.ColorScheme
|
||||
import androidx.compose.ui.graphics.Color
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader.ui.extension
|
||||
package me.ash.reader.ui.ext
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader
|
||||
package me.ash.reader.ui.ext
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
|
@ -1,12 +1,13 @@
|
|||
package me.ash.reader
|
||||
package me.ash.reader.ui.ext
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.os.ConfigurationCompat
|
||||
import me.ash.reader.R
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
fun Date.formatToString(
|
||||
fun Date.formatAsString(
|
||||
context: Context,
|
||||
onlyHourMinute: Boolean? = false,
|
||||
atHourMinute: Boolean? = false,
|
|
@ -1,9 +1,9 @@
|
|||
package me.ash.reader.ui.extension
|
||||
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.constant.Filter
|
||||
import me.ash.reader.data.entity.Filter
|
||||
|
||||
@Composable
|
||||
fun Filter.getName(): String = when (this) {
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader.ui.extension
|
||||
package me.ash.reader.ui.ext
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import kotlin.math.abs
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader.ui.extension
|
||||
package me.ash.reader.ui.ext
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.padding
|
3
app/src/main/java/me/ash/reader/ui/ext/NumberExt.kt
Normal file
3
app/src/main/java/me/ash/reader/ui/ext/NumberExt.kt
Normal file
|
@ -0,0 +1,3 @@
|
|||
package me.ash.reader.ui.ext
|
||||
|
||||
fun Int.spacerDollar(str: Any): String = "$this$$str"
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader.ui.extension
|
||||
package me.ash.reader.ui.ext
|
||||
|
||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||
import com.google.accompanist.pager.PagerState
|
12
app/src/main/java/me/ash/reader/ui/ext/StateFlowExt.kt
Normal file
12
app/src/main/java/me/ash/reader/ui/ext/StateFlowExt.kt
Normal file
|
@ -0,0 +1,12 @@
|
|||
package me.ash.reader.ui.ext
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
@Composable
|
||||
fun <T> StateFlow<T>.collectAsStateValue(
|
||||
context: CoroutineContext = Dispatchers.Default
|
||||
): T = collectAsState(context).value
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader
|
||||
package me.ash.reader.ui.ext
|
||||
|
||||
fun String.formatUrl(): String {
|
||||
if (this.startsWith("//")) {
|
|
@ -1,23 +0,0 @@
|
|||
package me.ash.reader.ui.extension
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
@Composable
|
||||
fun <T> StateFlow<T>.collectAsStateValue(
|
||||
context: CoroutineContext = Dispatchers.Default
|
||||
): T = collectAsState(context).value
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline operator fun <T> State<T>.getValue(thisObj: Any?, property: KProperty<*>): T = value
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline operator fun <T> MutableState<T>.setValue(thisObj: Any?, property: KProperty<*>, value: T) {
|
||||
this.value = value
|
||||
}
|
|
@ -7,8 +7,8 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||
import me.ash.reader.data.constant.Filter
|
||||
import me.ash.reader.ui.extension.getName
|
||||
import me.ash.reader.data.entity.Filter
|
||||
import me.ash.reader.ui.ext.getName
|
||||
|
||||
@OptIn(ExperimentalPagerApi::class)
|
||||
@Composable
|
||||
|
@ -21,7 +21,10 @@ fun FilterBar(
|
|||
modifier = Modifier.height(60.dp)
|
||||
) {
|
||||
Divider(
|
||||
modifier = Modifier.fillMaxWidth().height(1.dp).zIndex(1f),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(1.dp)
|
||||
.zIndex(1f),
|
||||
color = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.24f)
|
||||
)
|
||||
NavigationBar(
|
||||
|
|
|
@ -10,8 +10,9 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
|||
import androidx.navigation.NavHostController
|
||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||
import kotlinx.coroutines.launch
|
||||
import me.ash.reader.ui.extension.collectAsStateValue
|
||||
import me.ash.reader.ui.extension.findActivity
|
||||
import me.ash.reader.ui.component.ViewPager
|
||||
import me.ash.reader.ui.ext.collectAsStateValue
|
||||
import me.ash.reader.ui.ext.findActivity
|
||||
import me.ash.reader.ui.page.common.ExtraName
|
||||
import me.ash.reader.ui.page.home.drawer.feed.FeedOptionDrawer
|
||||
import me.ash.reader.ui.page.home.drawer.feed.FeedOptionViewAction
|
||||
|
@ -21,7 +22,6 @@ import me.ash.reader.ui.page.home.flow.FlowPage
|
|||
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.ReadViewModel
|
||||
import me.ash.reader.ui.widget.ViewPager
|
||||
|
||||
@OptIn(ExperimentalPagerApi::class, androidx.compose.material.ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
|
|
|
@ -9,12 +9,12 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
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.entity.Feed
|
||||
import me.ash.reader.data.entity.Filter
|
||||
import me.ash.reader.data.entity.Group
|
||||
import me.ash.reader.data.repository.AbstractRssRepository
|
||||
import me.ash.reader.data.repository.RssRepository
|
||||
import me.ash.reader.ui.extension.animateScrollToPage
|
||||
import me.ash.reader.ui.ext.animateScrollToPage
|
||||
import javax.inject.Inject
|
||||
|
||||
@OptIn(ExperimentalPagerApi::class)
|
||||
|
|
|
@ -14,8 +14,8 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.ui.extension.collectAsStateValue
|
||||
import me.ash.reader.ui.widget.Dialog
|
||||
import me.ash.reader.ui.component.Dialog
|
||||
import me.ash.reader.ui.ext.collectAsStateValue
|
||||
|
||||
@OptIn(ExperimentalPagerApi::class)
|
||||
@Composable
|
||||
|
|
|
@ -17,11 +17,11 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.ui.extension.collectAsStateValue
|
||||
import me.ash.reader.ui.extension.roundClick
|
||||
import me.ash.reader.ui.component.BottomDrawer
|
||||
import me.ash.reader.ui.component.Subtitle
|
||||
import me.ash.reader.ui.ext.collectAsStateValue
|
||||
import me.ash.reader.ui.ext.roundClick
|
||||
import me.ash.reader.ui.page.home.feeds.subscribe.ResultViewPage
|
||||
import me.ash.reader.ui.widget.BottomDrawer
|
||||
import me.ash.reader.ui.widget.Subtitle
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
|
|
|
@ -15,8 +15,8 @@ import kotlinx.coroutines.flow.asStateFlow
|
|||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.ash.reader.data.feed.Feed
|
||||
import me.ash.reader.data.group.Group
|
||||
import me.ash.reader.data.entity.Feed
|
||||
import me.ash.reader.data.entity.Group
|
||||
import me.ash.reader.data.repository.RssRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ import androidx.compose.ui.platform.LocalView
|
|||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import me.ash.reader.data.feed.Feed
|
||||
import me.ash.reader.data.entity.Feed
|
||||
import me.ash.reader.ui.page.home.drawer.feed.FeedOptionViewAction
|
||||
import me.ash.reader.ui.page.home.drawer.feed.FeedOptionViewModel
|
||||
|
||||
|
|
|
@ -28,16 +28,16 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
|||
import androidx.navigation.NavHostController
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.data.repository.AbstractRssRepository
|
||||
import me.ash.reader.ui.extension.collectAsStateValue
|
||||
import me.ash.reader.ui.extension.getDesc
|
||||
import me.ash.reader.ui.extension.getName
|
||||
import me.ash.reader.ui.component.Banner
|
||||
import me.ash.reader.ui.component.Subtitle
|
||||
import me.ash.reader.ui.ext.collectAsStateValue
|
||||
import me.ash.reader.ui.ext.getDesc
|
||||
import me.ash.reader.ui.ext.getName
|
||||
import me.ash.reader.ui.page.home.FilterBar
|
||||
import me.ash.reader.ui.page.home.FilterState
|
||||
import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeDialog
|
||||
import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeViewAction
|
||||
import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeViewModel
|
||||
import me.ash.reader.ui.widget.Banner
|
||||
import me.ash.reader.ui.widget.Subtitle
|
||||
|
||||
@OptIn(
|
||||
ExperimentalMaterial3Api::class, com.google.accompanist.pager.ExperimentalPagerApi::class,
|
||||
|
|
|
@ -8,9 +8,9 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import me.ash.reader.data.account.Account
|
||||
import me.ash.reader.data.constant.Filter
|
||||
import me.ash.reader.data.group.GroupWithFeed
|
||||
import me.ash.reader.data.entity.Account
|
||||
import me.ash.reader.data.entity.Filter
|
||||
import me.ash.reader.data.entity.GroupWithFeed
|
||||
import me.ash.reader.data.repository.AccountRepository
|
||||
import me.ash.reader.data.repository.OpmlRepository
|
||||
import me.ash.reader.data.repository.RssRepository
|
||||
|
|
|
@ -22,7 +22,7 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.data.feed.Feed
|
||||
import me.ash.reader.data.entity.Feed
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class, androidx.compose.foundation.ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
|
@ -57,7 +57,9 @@ fun GroupItem(
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f).padding(start = 28.dp),
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = 28.dp),
|
||||
text = text,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||
|
|
|
@ -22,11 +22,11 @@ import androidx.compose.ui.unit.dp
|
|||
import com.google.accompanist.flowlayout.FlowRow
|
||||
import com.google.accompanist.flowlayout.MainAxisAlignment
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.data.group.Group
|
||||
import me.ash.reader.ui.extension.roundClick
|
||||
import me.ash.reader.ui.widget.SelectionChip
|
||||
import me.ash.reader.ui.widget.SelectionEditorChip
|
||||
import me.ash.reader.ui.widget.Subtitle
|
||||
import me.ash.reader.data.entity.Group
|
||||
import me.ash.reader.ui.component.SelectionChip
|
||||
import me.ash.reader.ui.component.SelectionEditorChip
|
||||
import me.ash.reader.ui.component.Subtitle
|
||||
import me.ash.reader.ui.ext.roundClick
|
||||
|
||||
@Composable
|
||||
fun ResultViewPage(
|
||||
|
|
|
@ -21,10 +21,9 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||
import me.ash.reader.*
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.ui.extension.collectAsStateValue
|
||||
import me.ash.reader.ui.widget.Dialog
|
||||
import me.ash.reader.ui.component.Dialog
|
||||
import me.ash.reader.ui.ext.*
|
||||
|
||||
@OptIn(ExperimentalPagerApi::class, androidx.compose.ui.ExperimentalComposeUiApi::class)
|
||||
@Composable
|
||||
|
|
|
@ -9,15 +9,15 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
|||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.data.article.Article
|
||||
import me.ash.reader.data.feed.Feed
|
||||
import me.ash.reader.data.group.Group
|
||||
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.repository.OpmlRepository
|
||||
import me.ash.reader.data.repository.RssHelper
|
||||
import me.ash.reader.data.repository.RssRepository
|
||||
import me.ash.reader.data.repository.StringsRepository
|
||||
import me.ash.reader.formatUrl
|
||||
import me.ash.reader.ui.extension.animateScrollToPage
|
||||
import me.ash.reader.ui.ext.animateScrollToPage
|
||||
import me.ash.reader.ui.ext.formatUrl
|
||||
import java.io.InputStream
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||
import me.ash.reader.data.group.Group
|
||||
import me.ash.reader.ui.widget.ViewPager
|
||||
import me.ash.reader.data.entity.Group
|
||||
import me.ash.reader.ui.component.ViewPager
|
||||
|
||||
@OptIn(ExperimentalPagerApi::class)
|
||||
@Composable
|
||||
|
|
|
@ -15,8 +15,8 @@ import androidx.compose.ui.draw.clip
|
|||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import me.ash.reader.data.article.ArticleWithFeed
|
||||
import me.ash.reader.formatToString
|
||||
import me.ash.reader.data.entity.ArticleWithFeed
|
||||
import me.ash.reader.ui.ext.formatAsString
|
||||
|
||||
@Composable
|
||||
fun ArticleItem(
|
||||
|
@ -40,7 +40,9 @@ fun ArticleItem(
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f).padding(start = 30.dp),
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = 30.dp),
|
||||
text = articleWithFeed.feed.name,
|
||||
color = MaterialTheme.colorScheme.tertiary,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
|
@ -49,7 +51,7 @@ fun ArticleItem(
|
|||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 6.dp),
|
||||
text = articleWithFeed.article.date.formatToString(context, onlyHourMinute = true),
|
||||
text = articleWithFeed.article.date.formatAsString(context, onlyHourMinute = true),
|
||||
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
)
|
||||
|
|
|
@ -8,8 +8,8 @@ import androidx.compose.foundation.lazy.LazyListScope
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.paging.compose.LazyPagingItems
|
||||
import me.ash.reader.data.article.ArticleWithFeed
|
||||
import me.ash.reader.formatToString
|
||||
import me.ash.reader.data.entity.ArticleWithFeed
|
||||
import me.ash.reader.ui.ext.formatAsString
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
fun LazyListScope.generateArticleList(
|
||||
|
@ -20,7 +20,7 @@ fun LazyListScope.generateArticleList(
|
|||
var lastItemDay: String? = null
|
||||
for (itemIndex in 0 until pagingItems.itemCount) {
|
||||
val currentItem = pagingItems.peek(itemIndex) ?: continue
|
||||
val currentItemDay = currentItem.article.date.formatToString(context)
|
||||
val currentItemDay = currentItem.article.date.formatAsString(context)
|
||||
if (lastItemDay != currentItemDay) {
|
||||
if (itemIndex != 0) {
|
||||
item { Spacer(modifier = Modifier.height(40.dp)) }
|
||||
|
|
|
@ -24,9 +24,9 @@ import androidx.paging.LoadState
|
|||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import kotlinx.coroutines.launch
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.data.article.ArticleWithFeed
|
||||
import me.ash.reader.ui.extension.collectAsStateValue
|
||||
import me.ash.reader.ui.extension.getName
|
||||
import me.ash.reader.data.entity.ArticleWithFeed
|
||||
import me.ash.reader.ui.ext.collectAsStateValue
|
||||
import me.ash.reader.ui.ext.getName
|
||||
import me.ash.reader.ui.page.home.FilterBar
|
||||
import me.ash.reader.ui.page.home.FilterState
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import me.ash.reader.data.article.ArticleWithFeed
|
||||
import me.ash.reader.data.entity.ArticleWithFeed
|
||||
import me.ash.reader.data.repository.RssRepository
|
||||
import me.ash.reader.ui.page.home.FilterState
|
||||
import javax.inject.Inject
|
||||
|
|
|
@ -9,9 +9,9 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import me.ash.reader.data.article.ArticleWithFeed
|
||||
import me.ash.reader.formatToString
|
||||
import me.ash.reader.ui.extension.roundClick
|
||||
import me.ash.reader.data.entity.ArticleWithFeed
|
||||
import me.ash.reader.ui.ext.formatAsString
|
||||
import me.ash.reader.ui.ext.roundClick
|
||||
|
||||
@Composable
|
||||
fun Header(
|
||||
|
@ -30,7 +30,7 @@ fun Header(
|
|||
.padding(12.dp)
|
||||
) {
|
||||
Text(
|
||||
text = articleWithFeed.article.date.formatToString(context, atHourMinute = true),
|
||||
text = articleWithFeed.article.date.formatAsString(context, atHourMinute = true),
|
||||
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
)
|
||||
|
|
|
@ -22,7 +22,7 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.ui.widget.CanBeDisabledIconButton
|
||||
import me.ash.reader.ui.component.CanBeDisabledIconButton
|
||||
|
||||
@Composable
|
||||
fun ReadBar(
|
||||
|
|
|
@ -21,9 +21,9 @@ import androidx.compose.ui.zIndex
|
|||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.navigation.NavHostController
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.data.article.ArticleWithFeed
|
||||
import me.ash.reader.ui.extension.collectAsStateValue
|
||||
import me.ash.reader.ui.widget.WebView
|
||||
import me.ash.reader.data.entity.ArticleWithFeed
|
||||
import me.ash.reader.ui.component.WebView
|
||||
import me.ash.reader.ui.ext.collectAsStateValue
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
|
|
@ -11,7 +11,7 @@ import kotlinx.coroutines.flow.StateFlow
|
|||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import me.ash.reader.data.article.ArticleWithFeed
|
||||
import me.ash.reader.data.entity.ArticleWithFeed
|
||||
import me.ash.reader.data.repository.RssHelper
|
||||
import me.ash.reader.data.repository.RssRepository
|
||||
import javax.inject.Inject
|
||||
|
|
|
@ -11,7 +11,6 @@ import androidx.compose.material3.*
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
@ -20,9 +19,8 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavHostController
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.ui.extension.paddingFixedHorizontal
|
||||
import me.ash.reader.ui.extension.roundClick
|
||||
import me.ash.reader.ui.widget.TopTitleBox
|
||||
import me.ash.reader.ui.ext.paddingFixedHorizontal
|
||||
import me.ash.reader.ui.ext.roundClick
|
||||
|
||||
@Composable
|
||||
fun SettingsPage(
|
||||
|
@ -33,17 +31,6 @@ fun SettingsPage(
|
|||
// LargeTopAppBar(
|
||||
// title = { Text(text = "Settings") }
|
||||
// )
|
||||
TopTitleBox(
|
||||
title = "Settings",
|
||||
description = "",
|
||||
listState = listState,
|
||||
startOffset = Offset(20f, 78f),
|
||||
startHeight = 72f,
|
||||
startTitleFontSize = 36f,
|
||||
startDescriptionFontSize = 0f,
|
||||
) {
|
||||
|
||||
}
|
||||
Column {
|
||||
SmallTopAppBar(
|
||||
title = {},
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
package me.ash.reader.ui.widget
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
|
||||
@Composable
|
||||
fun AnimateLazyColumn(
|
||||
modifier: Modifier = Modifier,
|
||||
state: LazyListState = rememberLazyListState(),
|
||||
reference: Any?,
|
||||
content: LazyListScope.() -> Unit,
|
||||
) {
|
||||
var visible by remember { mutableStateOf(true) }
|
||||
LaunchedEffect(reference) {
|
||||
Log.i("RLog", "reference change")
|
||||
visible = false
|
||||
// delay(50)
|
||||
visible = true
|
||||
}
|
||||
|
||||
AnimatedVisibility(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
visible = visible,
|
||||
enter = fadeIn() + expandVertically(),
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = modifier,
|
||||
state = state,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,134 +0,0 @@
|
|||
import android.annotation.SuppressLint
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.Animatable
|
||||
import androidx.compose.animation.core.MutableTransitionState
|
||||
import androidx.compose.foundation.lazy.LazyItemScope
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListUpdateCallback
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@Suppress("UpdateTransitionLabel", "TransitionPropertiesLabel")
|
||||
@SuppressLint("ComposableNaming", "UnusedTransitionTargetStateParameter")
|
||||
/**
|
||||
* @param state Use [updateAnimatedItemsState].
|
||||
*/
|
||||
inline fun <T> LazyListScope.animatedItemsIndexed(
|
||||
state: List<AnimatedItem<T>>,
|
||||
enterTransition: EnterTransition = expandVertically(),
|
||||
exitTransition: ExitTransition = shrinkVertically(),
|
||||
noinline key: ((item: T) -> Any)? = null,
|
||||
crossinline itemContent: @Composable LazyItemScope.(index: Int, item: T) -> Unit
|
||||
) {
|
||||
items(
|
||||
state.size,
|
||||
if (key != null) { keyIndex: Int -> key(state[keyIndex].item) } else null
|
||||
) { index ->
|
||||
|
||||
val item = state[index]
|
||||
val visibility = item.visibility
|
||||
|
||||
key(key?.invoke(item.item)) {
|
||||
AnimatedVisibility(
|
||||
visibleState = visibility,
|
||||
enter = enterTransition,
|
||||
exit = exitTransition
|
||||
) {
|
||||
itemContent(index, item.item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun <T> updateAnimatedItemsState(
|
||||
newList: List<T>
|
||||
): State<List<AnimatedItem<T>>> {
|
||||
|
||||
val state = remember { mutableStateOf(emptyList<AnimatedItem<T>>()) }
|
||||
LaunchedEffect(newList) {
|
||||
if (state.value == newList) {
|
||||
return@LaunchedEffect
|
||||
}
|
||||
val oldList = state.value.toList()
|
||||
|
||||
val diffCb = object : DiffUtil.Callback() {
|
||||
override fun getOldListSize(): Int = oldList.size
|
||||
override fun getNewListSize(): Int = newList.size
|
||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
|
||||
oldList[oldItemPosition].item == newList[newItemPosition]
|
||||
|
||||
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
|
||||
oldList[oldItemPosition].item == newList[newItemPosition]
|
||||
}
|
||||
val diffResult = calculateDiff(false, diffCb)
|
||||
val compositeList = oldList.toMutableList()
|
||||
|
||||
diffResult.dispatchUpdatesTo(object : ListUpdateCallback {
|
||||
override fun onInserted(position: Int, count: Int) {
|
||||
for (i in 0 until count) {
|
||||
val newItem = AnimatedItem(
|
||||
visibility = MutableTransitionState(false),
|
||||
newList[position + i]
|
||||
)
|
||||
newItem.visibility.targetState = true
|
||||
compositeList.add(position + i, newItem)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRemoved(position: Int, count: Int) {
|
||||
for (i in 0 until count) {
|
||||
compositeList[position + i].visibility.targetState = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMoved(fromPosition: Int, toPosition: Int) {
|
||||
// not detecting moves.
|
||||
}
|
||||
|
||||
override fun onChanged(position: Int, count: Int, payload: Any?) {
|
||||
// irrelevant with compose.
|
||||
}
|
||||
})
|
||||
if (state.value != compositeList) {
|
||||
state.value = compositeList
|
||||
}
|
||||
val initialAnimation = Animatable(1.0f)
|
||||
initialAnimation.animateTo(0f)
|
||||
state.value = state.value.filter { it.visibility.targetState }
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
data class AnimatedItem<T>(
|
||||
val visibility: MutableTransitionState<Boolean>,
|
||||
val item: T,
|
||||
) {
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return item?.hashCode() ?: 0
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as AnimatedItem<*>
|
||||
|
||||
if (item != other.item) return false
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun calculateDiff(
|
||||
detectMoves: Boolean = true,
|
||||
diffCb: DiffUtil.Callback
|
||||
): DiffUtil.DiffResult {
|
||||
return withContext(Dispatchers.Unconfined) {
|
||||
DiffUtil.calculateDiff(diffCb, detectMoves)
|
||||
}
|
||||
}
|
|
@ -1,305 +0,0 @@
|
|||
package me.ash.reader.ui.widget
|
||||
|
||||
import androidx.compose.animation.core.Animatable
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.SpringSpec
|
||||
import androidx.compose.animation.core.calculateTargetValue
|
||||
import androidx.compose.animation.splineBasedDecay
|
||||
import androidx.compose.foundation.gestures.*
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clipToBounds
|
||||
import androidx.compose.ui.input.pointer.PointerInputChange
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.input.pointer.positionChange
|
||||
import androidx.compose.ui.input.pointer.util.VelocityTracker
|
||||
import androidx.compose.ui.layout.Layout
|
||||
import androidx.compose.ui.layout.Placeable
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.Constraints
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.math.sign
|
||||
|
||||
//val items = listOf(
|
||||
// Color.Red,
|
||||
// Color.Blue,
|
||||
// Color.Green,
|
||||
// Color.Yellow,
|
||||
// Color.Cyan,
|
||||
// Color.Magenta,
|
||||
//)
|
||||
|
||||
@Composable
|
||||
fun <T : Any> CustomPager(
|
||||
items: List<T>,
|
||||
modifier: Modifier = Modifier,
|
||||
orientation: Orientation = Orientation.Horizontal,
|
||||
initialIndex: Int = 0,
|
||||
/*@FloatRange(from = 0.0, to = 1.0)*/
|
||||
itemFraction: Float = 1f,
|
||||
itemSpacing: Dp = 0.dp,
|
||||
/*@FloatRange(from = 0.0, to = 1.0)*/
|
||||
overshootFraction: Float = .5f,
|
||||
onItemSelect: (T) -> Unit = {},
|
||||
contentFactory: @Composable (T) -> Unit,
|
||||
) {
|
||||
Pager(
|
||||
items,
|
||||
modifier,
|
||||
orientation,
|
||||
initialIndex,
|
||||
itemFraction,
|
||||
itemSpacing,
|
||||
overshootFraction,
|
||||
onItemSelect = { index -> onItemSelect(items[index]) },
|
||||
) {
|
||||
items.forEach { item ->
|
||||
Box(
|
||||
modifier = when (orientation) {
|
||||
Orientation.Horizontal -> Modifier.fillMaxWidth()
|
||||
Orientation.Vertical -> Modifier.fillMaxHeight()
|
||||
},
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
contentFactory(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun <T : Any> Pager(
|
||||
items: List<T>,
|
||||
modifier: Modifier = Modifier,
|
||||
orientation: Orientation = Orientation.Horizontal,
|
||||
initialIndex: Int = 0,
|
||||
/*@FloatRange(from = 0.0, to = 1.0)*/
|
||||
itemFraction: Float = 1f,
|
||||
itemSpacing: Dp = 0.dp,
|
||||
/*@FloatRange(from = 0.0, to = 1.0)*/
|
||||
overshootFraction: Float = .5f,
|
||||
onItemSelect: (Int) -> Unit = {},
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
require(initialIndex in 0..items.lastIndex) { "Initial index out of bounds" }
|
||||
require(itemFraction > 0f && itemFraction <= 1f) { "Item fraction must be in the (0f, 1f] range" }
|
||||
require(overshootFraction > 0f && itemFraction <= 1f) { "Overshoot fraction must be in the (0f, 1f] range" }
|
||||
val scope = rememberCoroutineScope()
|
||||
val state = rememberPagerState()
|
||||
state.currentIndex = initialIndex
|
||||
state.numberOfItems = items.size
|
||||
state.itemFraction = itemFraction
|
||||
state.overshootFraction = overshootFraction
|
||||
state.itemSpacing = with(LocalDensity.current) { itemSpacing.toPx() }
|
||||
state.orientation = orientation
|
||||
state.listener = onItemSelect
|
||||
state.scope = scope
|
||||
|
||||
Layout(
|
||||
content = content,
|
||||
modifier = modifier
|
||||
.clipToBounds()
|
||||
.then(state.inputModifier),
|
||||
) { measurables, constraints ->
|
||||
val dimension = constraints.dimension(orientation)
|
||||
val looseConstraints = constraints.toLooseConstraints(orientation, state.itemFraction)
|
||||
val placeables = measurables.map { measurable -> measurable.measure(looseConstraints) }
|
||||
val size = placeables.getSize(orientation, dimension)
|
||||
val itemDimension = (dimension * state.itemFraction).roundToInt()
|
||||
state.itemDimension = itemDimension
|
||||
val halfItemDimension = itemDimension / 2
|
||||
layout(size.width, size.height) {
|
||||
val centerOffset = dimension / 2 - halfItemDimension
|
||||
val dragOffset = state.dragOffset.value
|
||||
val roundedDragOffset = dragOffset.roundToInt()
|
||||
val spacing = state.itemSpacing.roundToInt()
|
||||
val itemDimensionWithSpace = itemDimension + state.itemSpacing
|
||||
val first = ceil(
|
||||
(dragOffset - itemDimension - centerOffset) / itemDimensionWithSpace
|
||||
).toInt().coerceAtLeast(0)
|
||||
val last = ((dimension + dragOffset - centerOffset) / itemDimensionWithSpace).toInt()
|
||||
.coerceAtMost(items.lastIndex)
|
||||
for (i in first..last) {
|
||||
val offset = i * (itemDimension + spacing) - roundedDragOffset + centerOffset
|
||||
placeables[i].place(
|
||||
x = when (orientation) {
|
||||
Orientation.Horizontal -> offset
|
||||
Orientation.Vertical -> 0
|
||||
},
|
||||
y = when (orientation) {
|
||||
Orientation.Horizontal -> 0
|
||||
Orientation.Vertical -> offset
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(key1 = items, key2 = initialIndex) {
|
||||
state.snapTo(initialIndex)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun rememberPagerState(): PagerState = remember { PagerState() }
|
||||
|
||||
private fun Constraints.dimension(orientation: Orientation) = when (orientation) {
|
||||
Orientation.Horizontal -> maxWidth
|
||||
Orientation.Vertical -> maxHeight
|
||||
}
|
||||
|
||||
private fun Constraints.toLooseConstraints(
|
||||
orientation: Orientation,
|
||||
itemFraction: Float,
|
||||
): Constraints {
|
||||
val dimension = dimension(orientation)
|
||||
return when (orientation) {
|
||||
Orientation.Horizontal -> copy(
|
||||
minWidth = (dimension * itemFraction).roundToInt(),
|
||||
maxWidth = (dimension * itemFraction).roundToInt(),
|
||||
minHeight = 0,
|
||||
)
|
||||
Orientation.Vertical -> copy(
|
||||
minWidth = 0,
|
||||
minHeight = (dimension * itemFraction).roundToInt(),
|
||||
maxHeight = (dimension * itemFraction).roundToInt(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<Placeable>.getSize(
|
||||
orientation: Orientation,
|
||||
dimension: Int,
|
||||
): IntSize {
|
||||
return when (orientation) {
|
||||
Orientation.Horizontal -> IntSize(
|
||||
dimension,
|
||||
maxByOrNull { it.height }?.height ?: 0
|
||||
)
|
||||
Orientation.Vertical -> IntSize(
|
||||
maxByOrNull { it.width }?.width ?: 0,
|
||||
dimension
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class PagerState {
|
||||
var currentIndex by mutableStateOf(0)
|
||||
var numberOfItems by mutableStateOf(0)
|
||||
var itemFraction by mutableStateOf(0f)
|
||||
var overshootFraction by mutableStateOf(0f)
|
||||
var itemSpacing by mutableStateOf(0f)
|
||||
var itemDimension by mutableStateOf(0)
|
||||
var orientation by mutableStateOf(Orientation.Horizontal)
|
||||
var scope: CoroutineScope? by mutableStateOf(null)
|
||||
var listener: (Int) -> Unit by mutableStateOf({})
|
||||
val dragOffset = Animatable(0f)
|
||||
|
||||
private val animationSpec = SpringSpec<Float>(
|
||||
dampingRatio = Spring.DampingRatioLowBouncy,
|
||||
stiffness = Spring.StiffnessLow,
|
||||
)
|
||||
|
||||
suspend fun snapTo(index: Int) {
|
||||
dragOffset.snapTo(index.toFloat() * (itemDimension + itemSpacing))
|
||||
}
|
||||
|
||||
val inputModifier = Modifier.pointerInput(numberOfItems) {
|
||||
fun itemIndex(offset: Int): Int = (offset / (itemDimension + itemSpacing)).roundToInt()
|
||||
.coerceIn(0, numberOfItems - 1)
|
||||
|
||||
fun updateIndex(offset: Float) {
|
||||
val index = itemIndex(offset.roundToInt())
|
||||
if (index != currentIndex) {
|
||||
currentIndex = index
|
||||
listener(index)
|
||||
}
|
||||
}
|
||||
|
||||
fun calculateOffsetLimit(): OffsetLimit {
|
||||
val dimension = when (orientation) {
|
||||
Orientation.Horizontal -> size.width
|
||||
Orientation.Vertical -> size.height
|
||||
}
|
||||
val itemSideMargin = (dimension - itemDimension) / 2f
|
||||
return OffsetLimit(
|
||||
min = -dimension * overshootFraction + itemSideMargin,
|
||||
max = numberOfItems * (itemDimension + itemSpacing) - (1f - overshootFraction) * dimension + itemSideMargin,
|
||||
)
|
||||
}
|
||||
|
||||
forEachGesture {
|
||||
awaitPointerEventScope {
|
||||
val tracker = VelocityTracker()
|
||||
val decay = splineBasedDecay<Float>(this)
|
||||
val down = awaitFirstDown()
|
||||
val offsetLimit = calculateOffsetLimit()
|
||||
val dragHandler = { change: PointerInputChange ->
|
||||
scope?.launch {
|
||||
val dragChange = change.calculateDragChange(orientation)
|
||||
dragOffset.snapTo(
|
||||
(dragOffset.value - dragChange).coerceIn(
|
||||
offsetLimit.min,
|
||||
offsetLimit.max
|
||||
)
|
||||
)
|
||||
updateIndex(dragOffset.value)
|
||||
}
|
||||
tracker.addPosition(change.uptimeMillis, change.position)
|
||||
}
|
||||
when (orientation) {
|
||||
Orientation.Horizontal -> horizontalDrag(down.id, dragHandler)
|
||||
Orientation.Vertical -> verticalDrag(down.id, dragHandler)
|
||||
}
|
||||
val velocity = tracker.calculateVelocity(orientation)
|
||||
scope?.launch {
|
||||
var targetOffset = decay.calculateTargetValue(dragOffset.value, -velocity)
|
||||
val remainder = targetOffset.toInt().absoluteValue % itemDimension
|
||||
val extra = if (remainder > itemDimension / 2f) 1 else 0
|
||||
val lastVisibleIndex =
|
||||
(targetOffset.absoluteValue / itemDimension.toFloat()).toInt() + extra
|
||||
targetOffset =
|
||||
(lastVisibleIndex * (itemDimension + itemSpacing) * targetOffset.sign)
|
||||
.coerceIn(
|
||||
0f,
|
||||
(numberOfItems - 1).toFloat() * (itemDimension + itemSpacing)
|
||||
)
|
||||
dragOffset.animateTo(
|
||||
animationSpec = animationSpec,
|
||||
targetValue = targetOffset,
|
||||
initialVelocity = -velocity
|
||||
) {
|
||||
updateIndex(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class OffsetLimit(
|
||||
val min: Float,
|
||||
val max: Float,
|
||||
)
|
||||
}
|
||||
|
||||
private fun VelocityTracker.calculateVelocity(orientation: Orientation) = when (orientation) {
|
||||
Orientation.Horizontal -> calculateVelocity().x
|
||||
Orientation.Vertical -> calculateVelocity().y
|
||||
}
|
||||
|
||||
private fun PointerInputChange.calculateDragChange(orientation: Orientation) =
|
||||
when (orientation) {
|
||||
Orientation.Horizontal -> positionChange().x
|
||||
Orientation.Vertical -> positionChange().y
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
package me.ash.reader.ui.widget
|
||||
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.animation.core.updateTransition
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||
import com.google.accompanist.pager.PagerState
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
@OptIn(ExperimentalPagerApi::class)
|
||||
@Composable
|
||||
fun BoxScope.MaskBox(
|
||||
modifier: Modifier = Modifier,
|
||||
pagerState: PagerState,
|
||||
currentPage: Int = 0,
|
||||
) {
|
||||
val transition = updateTransition(targetState = pagerState, label = "")
|
||||
val maskAlpha by transition.animateFloat(
|
||||
label = "",
|
||||
transitionSpec = {
|
||||
spring()
|
||||
}
|
||||
) {
|
||||
when {
|
||||
it.targetPage == currentPage -> {
|
||||
if (it.currentPage > currentPage) {
|
||||
1f - it.currentPageOffset.absoluteValue
|
||||
} else {
|
||||
0f
|
||||
}
|
||||
}
|
||||
it.targetPage > currentPage -> {
|
||||
it.currentPageOffset.absoluteValue
|
||||
}
|
||||
else -> 0f
|
||||
}
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier
|
||||
.alpha(maskAlpha)
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
package me.ash.reader.ui.widget
|
||||
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.zIndex
|
||||
import me.ash.reader.ui.extension.calculateTopBarAnimateValue
|
||||
|
||||
@Composable
|
||||
fun BoxScope.TopTitleBox(
|
||||
title: String,
|
||||
description: String,
|
||||
listState: LazyListState,
|
||||
SpacerHeight: Float = Float.NaN,
|
||||
startOffset: Offset,
|
||||
startHeight: Float,
|
||||
startTitleFontSize: Float,
|
||||
startDescriptionFontSize: Float,
|
||||
clickable: () -> Unit = {},
|
||||
) {
|
||||
val transition = updateTransition(targetState = listState, label = "")
|
||||
val offset by transition.animateOffset(
|
||||
label = "",
|
||||
transitionSpec = { spring() }
|
||||
) {
|
||||
Offset(
|
||||
x = it.calculateTopBarAnimateValue(startOffset.x, 56f),
|
||||
y = it.calculateTopBarAnimateValue(startOffset.y, 0f)
|
||||
)
|
||||
}
|
||||
|
||||
val height by transition.animateFloat(
|
||||
label = "",
|
||||
transitionSpec = { spring() }
|
||||
) {
|
||||
it.calculateTopBarAnimateValue(startHeight, 64f)
|
||||
}
|
||||
|
||||
val titleFontSize by transition.animateFloat(
|
||||
label = "",
|
||||
transitionSpec = { spring(stiffness = Spring.StiffnessHigh) }
|
||||
) {
|
||||
it.calculateTopBarAnimateValue(startTitleFontSize, 16f)
|
||||
}
|
||||
|
||||
val descriptionFontSize by transition.animateFloat(
|
||||
label = "",
|
||||
transitionSpec = { spring(stiffness = Spring.StiffnessHigh) }
|
||||
) {
|
||||
it.calculateTopBarAnimateValue(startDescriptionFontSize, 12f)
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.zIndex(1f)
|
||||
.height(height.dp)
|
||||
.offset(offset.x.dp, offset.y.dp)
|
||||
.clickable(
|
||||
interactionSource = MutableInteractionSource(),
|
||||
indication = null,
|
||||
onClickLabel = "回到顶部",
|
||||
onClick = clickable
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Column {
|
||||
AnimatedText(
|
||||
text = title,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = titleFontSize.sp,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
Spacer(modifier = Modifier.height(SpacerHeight.dp))
|
||||
AnimatedText(
|
||||
modifier = Modifier.width(200.dp),
|
||||
text = description,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
fontSize = descriptionFontSize.sp,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user