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.OpmlLocalDataSource
|
||||||
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.ui.ext.DataStoreKeys
|
||||||
|
import me.ash.reader.ui.ext.dataStore
|
||||||
|
import me.ash.reader.ui.ext.put
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltAndroidApp
|
@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 androidx.room.*
|
||||||
|
import me.ash.reader.data.entity.Account
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface AccountDao {
|
interface AccountDao {
|
|
@ -1,8 +1,11 @@
|
||||||
package me.ash.reader.data.article
|
package me.ash.reader.data.dao
|
||||||
|
|
||||||
import androidx.paging.PagingSource
|
import androidx.paging.PagingSource
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
import kotlinx.coroutines.flow.Flow
|
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
|
@Dao
|
||||||
interface ArticleDao {
|
interface ArticleDao {
|
|
@ -1,6 +1,7 @@
|
||||||
package me.ash.reader.data.feed
|
package me.ash.reader.data.dao
|
||||||
|
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
|
import me.ash.reader.data.entity.Feed
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface FeedDao {
|
interface FeedDao {
|
|
@ -1,7 +1,9 @@
|
||||||
package me.ash.reader.data.group
|
package me.ash.reader.data.dao
|
||||||
|
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import me.ash.reader.data.entity.Group
|
||||||
|
import me.ash.reader.data.entity.GroupWithFeed
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface GroupDao {
|
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.ColumnInfo
|
||||||
import androidx.room.Entity
|
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.ColumnInfo
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.ForeignKey
|
import androidx.room.ForeignKey
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import me.ash.reader.data.feed.Feed
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@Entity(
|
@Entity(
|
|
@ -1,8 +1,7 @@
|
||||||
package me.ash.reader.data.article
|
package me.ash.reader.data.entity
|
||||||
|
|
||||||
import androidx.room.Embedded
|
import androidx.room.Embedded
|
||||||
import androidx.room.Relation
|
import androidx.room.Relation
|
||||||
import me.ash.reader.data.feed.Feed
|
|
||||||
|
|
||||||
data class ArticleWithFeed(
|
data class ArticleWithFeed(
|
||||||
@Embedded
|
@Embedded
|
|
@ -1,7 +1,6 @@
|
||||||
package me.ash.reader.data.feed
|
package me.ash.reader.data.entity
|
||||||
|
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
import me.ash.reader.data.group.Group
|
|
||||||
|
|
||||||
@Entity(
|
@Entity(
|
||||||
tableName = "feed",
|
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.Embedded
|
||||||
import androidx.room.Relation
|
import androidx.room.Relation
|
||||||
import me.ash.reader.data.article.Article
|
|
||||||
|
|
||||||
data class FeedWithArticle(
|
data class FeedWithArticle(
|
||||||
@Embedded
|
@Embedded
|
|
@ -1,8 +1,7 @@
|
||||||
package me.ash.reader.data.feed
|
package me.ash.reader.data.entity
|
||||||
|
|
||||||
import androidx.room.Embedded
|
import androidx.room.Embedded
|
||||||
import androidx.room.Relation
|
import androidx.room.Relation
|
||||||
import me.ash.reader.data.group.Group
|
|
||||||
|
|
||||||
data class FeedWithGroup(
|
data class FeedWithGroup(
|
||||||
@Embedded
|
@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.Icons
|
||||||
import androidx.compose.material.icons.filled.FiberManualRecord
|
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.ColumnInfo
|
||||||
import androidx.room.Entity
|
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.Embedded
|
||||||
import androidx.room.Relation
|
import androidx.room.Relation
|
||||||
import me.ash.reader.data.feed.Feed
|
|
||||||
|
|
||||||
data class GroupWithFeed(
|
data class GroupWithFeed(
|
||||||
@Embedded
|
@Embedded
|
|
@ -1,4 +1,4 @@
|
||||||
package me.ash.reader.data.article
|
package me.ash.reader.data.entity
|
||||||
|
|
||||||
data class ImportantCount(
|
data class ImportantCount(
|
||||||
val important: Int,
|
val important: Int,
|
|
@ -6,10 +6,10 @@ import dagger.Provides
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
import me.ash.reader.data.account.AccountDao
|
import me.ash.reader.data.dao.AccountDao
|
||||||
import me.ash.reader.data.article.ArticleDao
|
import me.ash.reader.data.dao.ArticleDao
|
||||||
import me.ash.reader.data.feed.FeedDao
|
import me.ash.reader.data.dao.FeedDao
|
||||||
import me.ash.reader.data.group.GroupDao
|
import me.ash.reader.data.dao.GroupDao
|
||||||
import me.ash.reader.data.source.ReaderDatabase
|
import me.ash.reader.data.source.ReaderDatabase
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
|
|
@ -10,18 +10,13 @@ import dagger.assisted.AssistedInject
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import me.ash.reader.currentAccountId
|
import me.ash.reader.data.dao.AccountDao
|
||||||
import me.ash.reader.data.account.AccountDao
|
import me.ash.reader.data.dao.ArticleDao
|
||||||
import me.ash.reader.data.article.Article
|
import me.ash.reader.data.dao.FeedDao
|
||||||
import me.ash.reader.data.article.ArticleDao
|
import me.ash.reader.data.dao.GroupDao
|
||||||
import me.ash.reader.data.article.ArticleWithFeed
|
import me.ash.reader.data.entity.*
|
||||||
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.source.RssNetworkDataSource
|
import me.ash.reader.data.source.RssNetworkDataSource
|
||||||
|
import me.ash.reader.ui.ext.currentAccountId
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
abstract class AbstractRssRepository constructor(
|
abstract class AbstractRssRepository constructor(
|
||||||
|
|
|
@ -3,12 +3,12 @@ package me.ash.reader.data.repository
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import me.ash.reader.R
|
import me.ash.reader.R
|
||||||
import me.ash.reader.currentAccountId
|
import me.ash.reader.data.dao.AccountDao
|
||||||
import me.ash.reader.data.account.Account
|
import me.ash.reader.data.dao.GroupDao
|
||||||
import me.ash.reader.data.account.AccountDao
|
import me.ash.reader.data.entity.Account
|
||||||
import me.ash.reader.data.group.Group
|
import me.ash.reader.data.entity.Group
|
||||||
import me.ash.reader.data.group.GroupDao
|
import me.ash.reader.ui.ext.currentAccountId
|
||||||
import me.ash.reader.spacerDollar
|
import me.ash.reader.ui.ext.spacerDollar
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class AccountRepository @Inject constructor(
|
class AccountRepository @Inject constructor(
|
||||||
|
|
|
@ -8,20 +8,20 @@ import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import me.ash.reader.currentAccountId
|
import me.ash.reader.data.dao.AccountDao
|
||||||
import me.ash.reader.data.account.AccountDao
|
import me.ash.reader.data.dao.ArticleDao
|
||||||
import me.ash.reader.data.article.Article
|
import me.ash.reader.data.dao.FeedDao
|
||||||
import me.ash.reader.data.article.ArticleDao
|
import me.ash.reader.data.dao.GroupDao
|
||||||
import me.ash.reader.data.feed.Feed
|
import me.ash.reader.data.entity.Article
|
||||||
import me.ash.reader.data.feed.FeedDao
|
import me.ash.reader.data.entity.Feed
|
||||||
import me.ash.reader.data.group.Group
|
import me.ash.reader.data.entity.Group
|
||||||
import me.ash.reader.data.group.GroupDao
|
|
||||||
import me.ash.reader.data.module.ApplicationScope
|
import me.ash.reader.data.module.ApplicationScope
|
||||||
import me.ash.reader.data.module.DispatcherDefault
|
import me.ash.reader.data.module.DispatcherDefault
|
||||||
import me.ash.reader.data.module.DispatcherIO
|
import me.ash.reader.data.module.DispatcherIO
|
||||||
import me.ash.reader.data.source.FeverApiDataSource
|
import me.ash.reader.data.source.FeverApiDataSource
|
||||||
import me.ash.reader.data.source.RssNetworkDataSource
|
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 net.dankito.readability4j.extended.Readability4JExtended
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
|
@ -14,18 +14,18 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import me.ash.reader.MainActivity
|
import me.ash.reader.MainActivity
|
||||||
import me.ash.reader.R
|
import me.ash.reader.R
|
||||||
import me.ash.reader.currentAccountId
|
import me.ash.reader.data.dao.AccountDao
|
||||||
import me.ash.reader.data.account.AccountDao
|
import me.ash.reader.data.dao.ArticleDao
|
||||||
import me.ash.reader.data.article.Article
|
import me.ash.reader.data.dao.FeedDao
|
||||||
import me.ash.reader.data.article.ArticleDao
|
import me.ash.reader.data.dao.GroupDao
|
||||||
import me.ash.reader.data.feed.Feed
|
import me.ash.reader.data.entity.Article
|
||||||
import me.ash.reader.data.feed.FeedDao
|
import me.ash.reader.data.entity.Feed
|
||||||
import me.ash.reader.data.group.Group
|
import me.ash.reader.data.entity.Group
|
||||||
import me.ash.reader.data.group.GroupDao
|
|
||||||
import me.ash.reader.data.module.ApplicationScope
|
import me.ash.reader.data.module.ApplicationScope
|
||||||
import me.ash.reader.data.module.DispatcherDefault
|
import me.ash.reader.data.module.DispatcherDefault
|
||||||
import me.ash.reader.data.module.DispatcherIO
|
import me.ash.reader.data.module.DispatcherIO
|
||||||
import me.ash.reader.data.source.RssNetworkDataSource
|
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.ExtraName
|
||||||
import me.ash.reader.ui.page.common.NotificationGroupName
|
import me.ash.reader.ui.page.common.NotificationGroupName
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
|
@ -8,13 +8,13 @@ import be.ceau.opml.entity.Opml
|
||||||
import be.ceau.opml.entity.Outline
|
import be.ceau.opml.entity.Outline
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import me.ash.reader.R
|
import me.ash.reader.R
|
||||||
import me.ash.reader.currentAccountId
|
import me.ash.reader.data.dao.AccountDao
|
||||||
import me.ash.reader.data.account.AccountDao
|
import me.ash.reader.data.dao.FeedDao
|
||||||
import me.ash.reader.data.feed.Feed
|
import me.ash.reader.data.dao.GroupDao
|
||||||
import me.ash.reader.data.feed.FeedDao
|
import me.ash.reader.data.entity.Feed
|
||||||
import me.ash.reader.data.group.GroupDao
|
|
||||||
import me.ash.reader.data.source.OpmlLocalDataSource
|
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.io.InputStream
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
|
@ -6,14 +6,14 @@ import android.util.Log
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import me.ash.reader.currentAccountId
|
import me.ash.reader.data.dao.FeedDao
|
||||||
import me.ash.reader.data.article.Article
|
import me.ash.reader.data.entity.Article
|
||||||
import me.ash.reader.data.feed.Feed
|
import me.ash.reader.data.entity.Feed
|
||||||
import me.ash.reader.data.feed.FeedDao
|
import me.ash.reader.data.entity.FeedWithArticle
|
||||||
import me.ash.reader.data.feed.FeedWithArticle
|
|
||||||
import me.ash.reader.data.module.DispatcherIO
|
import me.ash.reader.data.module.DispatcherIO
|
||||||
import me.ash.reader.data.source.RssNetworkDataSource
|
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.Readability4J
|
||||||
import net.dankito.readability4j.extended.Readability4JExtended
|
import net.dankito.readability4j.extended.Readability4JExtended
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
|
|
|
@ -2,8 +2,8 @@ package me.ash.reader.data.repository
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import me.ash.reader.currentAccountType
|
import me.ash.reader.data.entity.Account
|
||||||
import me.ash.reader.data.account.Account
|
import me.ash.reader.ui.ext.currentAccountType
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class RssRepository @Inject constructor(
|
class RssRepository @Inject constructor(
|
||||||
|
|
|
@ -5,11 +5,11 @@ import be.ceau.opml.OpmlParser
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import me.ash.reader.currentAccountId
|
import me.ash.reader.data.entity.Feed
|
||||||
import me.ash.reader.data.feed.Feed
|
import me.ash.reader.data.entity.Group
|
||||||
import me.ash.reader.data.group.Group
|
import me.ash.reader.data.entity.GroupWithFeed
|
||||||
import me.ash.reader.data.group.GroupWithFeed
|
|
||||||
import me.ash.reader.data.module.DispatcherIO
|
import me.ash.reader.data.module.DispatcherIO
|
||||||
|
import me.ash.reader.ui.ext.currentAccountId
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
|
@ -1,26 +1,23 @@
|
||||||
package me.ash.reader.data.source
|
package me.ash.reader.data.source
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.room.Database
|
import androidx.room.*
|
||||||
import androidx.room.Room
|
import me.ash.reader.data.dao.AccountDao
|
||||||
import androidx.room.RoomDatabase
|
import me.ash.reader.data.dao.ArticleDao
|
||||||
import androidx.room.TypeConverters
|
import me.ash.reader.data.dao.FeedDao
|
||||||
import me.ash.reader.data.Converters
|
import me.ash.reader.data.dao.GroupDao
|
||||||
import me.ash.reader.data.account.Account
|
import me.ash.reader.data.entity.Account
|
||||||
import me.ash.reader.data.account.AccountDao
|
import me.ash.reader.data.entity.Article
|
||||||
import me.ash.reader.data.article.Article
|
import me.ash.reader.data.entity.Feed
|
||||||
import me.ash.reader.data.article.ArticleDao
|
import me.ash.reader.data.entity.Group
|
||||||
import me.ash.reader.data.feed.Feed
|
import java.util.*
|
||||||
import me.ash.reader.data.feed.FeedDao
|
|
||||||
import me.ash.reader.data.group.Group
|
|
||||||
import me.ash.reader.data.group.GroupDao
|
|
||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
entities = [Account::class, Feed::class, Article::class, Group::class],
|
entities = [Account::class, Feed::class, Article::class, Group::class],
|
||||||
version = 1,
|
version = 1,
|
||||||
exportSchema = false,
|
exportSchema = false,
|
||||||
)
|
)
|
||||||
@TypeConverters(Converters::class)
|
@TypeConverters(ReaderDatabase.Converters::class)
|
||||||
abstract class ReaderDatabase : RoomDatabase() {
|
abstract class ReaderDatabase : RoomDatabase() {
|
||||||
abstract fun accountDao(): AccountDao
|
abstract fun accountDao(): AccountDao
|
||||||
abstract fun feedDao(): FeedDao
|
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.*
|
||||||
import androidx.compose.animation.core.FastOutSlowInEasing
|
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.animation.Crossfade
|
||||||
import androidx.compose.foundation.background
|
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.background
|
||||||
import androidx.compose.foundation.layout.*
|
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.foundation.layout.size
|
||||||
import androidx.compose.material3.Icon
|
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.material3.AlertDialog
|
||||||
import androidx.compose.runtime.Composable
|
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.Composable
|
||||||
import androidx.compose.runtime.getValue
|
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.foundation.layout.Box
|
||||||
import androidx.compose.material3.DropdownMenu
|
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.BorderStroke
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
@ -153,7 +153,7 @@ fun SelectionEditorChip(
|
||||||
onValueChange = { onValueChange(it) },
|
onValueChange = { onValueChange(it) },
|
||||||
cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurfaceVariant),
|
cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurfaceVariant),
|
||||||
textStyle = MaterialTheme.typography.titleSmall.copy(
|
textStyle = MaterialTheme.typography.titleSmall.copy(
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
decorationBox = { innerTextField ->
|
decorationBox = { innerTextField ->
|
||||||
Row(
|
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.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
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.animation.animateContentSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
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.content.Intent
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
|
@ -8,6 +8,9 @@ import android.util.Log
|
||||||
import android.webkit.*
|
import android.webkit.*
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
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.Modifier
|
||||||
import androidx.compose.ui.graphics.toArgb
|
import androidx.compose.ui.graphics.toArgb
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
@ -29,80 +32,94 @@ fun WebView(
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val color = MaterialTheme.colorScheme.onSurfaceVariant.toArgb()
|
val color = MaterialTheme.colorScheme.onSurfaceVariant.toArgb()
|
||||||
val backgroundColor = MaterialTheme.colorScheme.surface.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? {
|
override fun shouldInterceptRequest(
|
||||||
if (url != null && url.contains(INJECTION_TOKEN)) {
|
view: WebView?,
|
||||||
try {
|
url: String?
|
||||||
val assetPath = url.substring(
|
): WebResourceResponse? {
|
||||||
url.indexOf(INJECTION_TOKEN) + INJECTION_TOKEN.length,
|
if (url != null && url.contains(INJECTION_TOKEN)) {
|
||||||
url.length
|
try {
|
||||||
)
|
val assetPath = url.substring(
|
||||||
return WebResourceResponse(
|
url.indexOf(INJECTION_TOKEN) + INJECTION_TOKEN.length,
|
||||||
"text/HTML",
|
url.length
|
||||||
"UTF-8",
|
)
|
||||||
context.assets.open(assetPath)
|
return WebResourceResponse(
|
||||||
)
|
"text/HTML",
|
||||||
} catch (e: Exception) {
|
"UTF-8",
|
||||||
Log.e("RLog", "WebView shouldInterceptRequest: $e")
|
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(
|
override fun onPageStarted(
|
||||||
view: WebView?,
|
view: WebView?,
|
||||||
url: String?,
|
url: String?,
|
||||||
favicon: Bitmap?
|
favicon: Bitmap?
|
||||||
) {
|
) {
|
||||||
super.onPageStarted(view, url, favicon)
|
super.onPageStarted(view, url, favicon)
|
||||||
// _isLoading = true
|
// _isLoading = true
|
||||||
onProgressChange(-1)
|
onProgressChange(-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPageFinished(view: WebView?, url: String?) {
|
override fun onPageFinished(view: WebView?, url: String?) {
|
||||||
super.onPageFinished(view, url)
|
super.onPageFinished(view, url)
|
||||||
val jsCode = "javascript:(function(){" +
|
val jsCode = "javascript:(function(){" +
|
||||||
"var imgs=document.getElementsByTagName(\"img\");" +
|
"var imgs=document.getElementsByTagName(\"img\");" +
|
||||||
"for(var i=0;i<imgs.length;i++){" +
|
"for(var i=0;i<imgs.length;i++){" +
|
||||||
"imgs[i].pos = i;" +
|
"imgs[i].pos = i;" +
|
||||||
"imgs[i].onclick=function(){" +
|
"imgs[i].onclick=function(){" +
|
||||||
// "window.jsCallJavaObj.openImage(this.src,this.pos);" +
|
// "window.jsCallJavaObj.openImage(this.src,this.pos);" +
|
||||||
"alert('asf');" +
|
"alert('asf');" +
|
||||||
"}}})()"
|
"}}})()"
|
||||||
view!!.loadUrl(jsCode)
|
view!!.loadUrl(jsCode)
|
||||||
viewModel.dispatch(ReadViewAction.ChangeLoading(false))
|
viewModel.dispatch(ReadViewAction.ChangeLoading(false))
|
||||||
onProgressChange(100)
|
onProgressChange(100)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun shouldOverrideUrlLoading(
|
override fun shouldOverrideUrlLoading(
|
||||||
view: WebView?,
|
view: WebView?,
|
||||||
request: WebResourceRequest?
|
request: WebResourceRequest?
|
||||||
): Boolean {
|
): Boolean {
|
||||||
if (null == request?.url) return false
|
if (null == request?.url) return false
|
||||||
val url = request.url.toString()
|
val url = request.url.toString()
|
||||||
context.startActivity(
|
context.startActivity(
|
||||||
Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
||||||
)
|
)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onReceivedError(
|
override fun onReceivedError(
|
||||||
view: WebView?,
|
view: WebView?,
|
||||||
request: WebResourceRequest?,
|
request: WebResourceRequest?,
|
||||||
error: WebResourceError?
|
error: WebResourceError?
|
||||||
) {
|
) {
|
||||||
super.onReceivedError(view, request, error)
|
super.onReceivedError(view, request, error)
|
||||||
onReceivedError(error)
|
onReceivedError(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onReceivedSslError(
|
override fun onReceivedSslError(
|
||||||
view: WebView?,
|
view: WebView?,
|
||||||
handler: SslErrorHandler?,
|
handler: SslErrorHandler?,
|
||||||
error: SslError?
|
error: SslError?
|
||||||
) {
|
) {
|
||||||
handler?.cancel()
|
handler?.cancel()
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
val webView by remember {
|
||||||
|
mutableStateOf(WebView(context).apply {
|
||||||
|
this.webViewClient = webViewClient
|
||||||
|
setBackgroundColor(backgroundColor)
|
||||||
|
isHorizontalScrollBarEnabled = false
|
||||||
|
isVerticalScrollBarEnabled = false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Column(
|
// Column(
|
||||||
|
@ -121,14 +138,7 @@ fun WebView(
|
||||||
|
|
||||||
AndroidView(
|
AndroidView(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
factory = {
|
factory = { webView },
|
||||||
WebView(it).apply {
|
|
||||||
this.webViewClient = webViewClient
|
|
||||||
setBackgroundColor(backgroundColor)
|
|
||||||
isHorizontalScrollBarEnabled = false
|
|
||||||
isVerticalScrollBarEnabled = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
update = {
|
update = {
|
||||||
it.apply {
|
it.apply {
|
||||||
Log.i("RLog", "CustomWebView: ${content}")
|
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.material3.ColorScheme
|
||||||
import androidx.compose.ui.graphics.Color
|
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.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
|
@ -1,4 +1,4 @@
|
||||||
package me.ash.reader
|
package me.ash.reader.ui.ext
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
|
@ -1,12 +1,13 @@
|
||||||
package me.ash.reader
|
package me.ash.reader.ui.ext
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.core.os.ConfigurationCompat
|
import androidx.core.os.ConfigurationCompat
|
||||||
|
import me.ash.reader.R
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
fun Date.formatToString(
|
fun Date.formatAsString(
|
||||||
context: Context,
|
context: Context,
|
||||||
onlyHourMinute: Boolean? = false,
|
onlyHourMinute: Boolean? = false,
|
||||||
atHourMinute: 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.runtime.Composable
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import me.ash.reader.R
|
import me.ash.reader.R
|
||||||
import me.ash.reader.data.constant.Filter
|
import me.ash.reader.data.entity.Filter
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Filter.getName(): String = when (this) {
|
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 androidx.compose.foundation.lazy.LazyListState
|
||||||
import kotlin.math.abs
|
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.clickable
|
||||||
import androidx.compose.foundation.layout.padding
|
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.ExperimentalPagerApi
|
||||||
import com.google.accompanist.pager.PagerState
|
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 {
|
fun String.formatUrl(): String {
|
||||||
if (this.startsWith("//")) {
|
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.unit.dp
|
||||||
import androidx.compose.ui.zIndex
|
import androidx.compose.ui.zIndex
|
||||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
import me.ash.reader.data.constant.Filter
|
import me.ash.reader.data.entity.Filter
|
||||||
import me.ash.reader.ui.extension.getName
|
import me.ash.reader.ui.ext.getName
|
||||||
|
|
||||||
@OptIn(ExperimentalPagerApi::class)
|
@OptIn(ExperimentalPagerApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -21,7 +21,10 @@ fun FilterBar(
|
||||||
modifier = Modifier.height(60.dp)
|
modifier = Modifier.height(60.dp)
|
||||||
) {
|
) {
|
||||||
Divider(
|
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)
|
color = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.24f)
|
||||||
)
|
)
|
||||||
NavigationBar(
|
NavigationBar(
|
||||||
|
|
|
@ -10,8 +10,9 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import me.ash.reader.ui.extension.collectAsStateValue
|
import me.ash.reader.ui.component.ViewPager
|
||||||
import me.ash.reader.ui.extension.findActivity
|
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.common.ExtraName
|
||||||
import me.ash.reader.ui.page.home.drawer.feed.FeedOptionDrawer
|
import me.ash.reader.ui.page.home.drawer.feed.FeedOptionDrawer
|
||||||
import me.ash.reader.ui.page.home.drawer.feed.FeedOptionViewAction
|
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.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.widget.ViewPager
|
|
||||||
|
|
||||||
@OptIn(ExperimentalPagerApi::class, androidx.compose.material.ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalPagerApi::class, androidx.compose.material.ExperimentalMaterialApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
@ -9,12 +9,12 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import me.ash.reader.data.constant.Filter
|
import me.ash.reader.data.entity.Feed
|
||||||
import me.ash.reader.data.feed.Feed
|
import me.ash.reader.data.entity.Filter
|
||||||
import me.ash.reader.data.group.Group
|
import me.ash.reader.data.entity.Group
|
||||||
import me.ash.reader.data.repository.AbstractRssRepository
|
import me.ash.reader.data.repository.AbstractRssRepository
|
||||||
import me.ash.reader.data.repository.RssRepository
|
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
|
import javax.inject.Inject
|
||||||
|
|
||||||
@OptIn(ExperimentalPagerApi::class)
|
@OptIn(ExperimentalPagerApi::class)
|
||||||
|
|
|
@ -14,8 +14,8 @@ import androidx.compose.ui.res.stringResource
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
import me.ash.reader.R
|
import me.ash.reader.R
|
||||||
import me.ash.reader.ui.extension.collectAsStateValue
|
import me.ash.reader.ui.component.Dialog
|
||||||
import me.ash.reader.ui.widget.Dialog
|
import me.ash.reader.ui.ext.collectAsStateValue
|
||||||
|
|
||||||
@OptIn(ExperimentalPagerApi::class)
|
@OptIn(ExperimentalPagerApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
@ -17,11 +17,11 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||||
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 me.ash.reader.R
|
import me.ash.reader.R
|
||||||
import me.ash.reader.ui.extension.collectAsStateValue
|
import me.ash.reader.ui.component.BottomDrawer
|
||||||
import me.ash.reader.ui.extension.roundClick
|
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.page.home.feeds.subscribe.ResultViewPage
|
||||||
import me.ash.reader.ui.widget.BottomDrawer
|
|
||||||
import me.ash.reader.ui.widget.Subtitle
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
@ -15,8 +15,8 @@ import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import me.ash.reader.data.feed.Feed
|
import me.ash.reader.data.entity.Feed
|
||||||
import me.ash.reader.data.group.Group
|
import me.ash.reader.data.entity.Group
|
||||||
import me.ash.reader.data.repository.RssRepository
|
import me.ash.reader.data.repository.RssRepository
|
||||||
import javax.inject.Inject
|
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.text.style.TextOverflow
|
||||||
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 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.FeedOptionViewAction
|
||||||
import me.ash.reader.ui.page.home.drawer.feed.FeedOptionViewModel
|
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 androidx.navigation.NavHostController
|
||||||
import me.ash.reader.R
|
import me.ash.reader.R
|
||||||
import me.ash.reader.data.repository.AbstractRssRepository
|
import me.ash.reader.data.repository.AbstractRssRepository
|
||||||
import me.ash.reader.ui.extension.collectAsStateValue
|
import me.ash.reader.ui.component.Banner
|
||||||
import me.ash.reader.ui.extension.getDesc
|
import me.ash.reader.ui.component.Subtitle
|
||||||
import me.ash.reader.ui.extension.getName
|
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.FilterBar
|
||||||
import me.ash.reader.ui.page.home.FilterState
|
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.SubscribeDialog
|
||||||
import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeViewAction
|
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.page.home.feeds.subscribe.SubscribeViewModel
|
||||||
import me.ash.reader.ui.widget.Banner
|
|
||||||
import me.ash.reader.ui.widget.Subtitle
|
|
||||||
|
|
||||||
@OptIn(
|
@OptIn(
|
||||||
ExperimentalMaterial3Api::class, com.google.accompanist.pager.ExperimentalPagerApi::class,
|
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.Dispatchers
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import me.ash.reader.data.account.Account
|
import me.ash.reader.data.entity.Account
|
||||||
import me.ash.reader.data.constant.Filter
|
import me.ash.reader.data.entity.Filter
|
||||||
import me.ash.reader.data.group.GroupWithFeed
|
import me.ash.reader.data.entity.GroupWithFeed
|
||||||
import me.ash.reader.data.repository.AccountRepository
|
import me.ash.reader.data.repository.AccountRepository
|
||||||
import me.ash.reader.data.repository.OpmlRepository
|
import me.ash.reader.data.repository.OpmlRepository
|
||||||
import me.ash.reader.data.repository.RssRepository
|
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.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import me.ash.reader.R
|
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)
|
@OptIn(ExperimentalMaterialApi::class, androidx.compose.foundation.ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -57,7 +57,9 @@ fun GroupItem(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.weight(1f).padding(start = 28.dp),
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.padding(start = 28.dp),
|
||||||
text = text,
|
text = text,
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
color = MaterialTheme.colorScheme.onSecondaryContainer,
|
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.FlowRow
|
||||||
import com.google.accompanist.flowlayout.MainAxisAlignment
|
import com.google.accompanist.flowlayout.MainAxisAlignment
|
||||||
import me.ash.reader.R
|
import me.ash.reader.R
|
||||||
import me.ash.reader.data.group.Group
|
import me.ash.reader.data.entity.Group
|
||||||
import me.ash.reader.ui.extension.roundClick
|
import me.ash.reader.ui.component.SelectionChip
|
||||||
import me.ash.reader.ui.widget.SelectionChip
|
import me.ash.reader.ui.component.SelectionEditorChip
|
||||||
import me.ash.reader.ui.widget.SelectionEditorChip
|
import me.ash.reader.ui.component.Subtitle
|
||||||
import me.ash.reader.ui.widget.Subtitle
|
import me.ash.reader.ui.ext.roundClick
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ResultViewPage(
|
fun ResultViewPage(
|
||||||
|
|
|
@ -21,10 +21,9 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
import me.ash.reader.*
|
|
||||||
import me.ash.reader.R
|
import me.ash.reader.R
|
||||||
import me.ash.reader.ui.extension.collectAsStateValue
|
import me.ash.reader.ui.component.Dialog
|
||||||
import me.ash.reader.ui.widget.Dialog
|
import me.ash.reader.ui.ext.*
|
||||||
|
|
||||||
@OptIn(ExperimentalPagerApi::class, androidx.compose.ui.ExperimentalComposeUiApi::class)
|
@OptIn(ExperimentalPagerApi::class, androidx.compose.ui.ExperimentalComposeUiApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
@ -9,15 +9,15 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import me.ash.reader.R
|
import me.ash.reader.R
|
||||||
import me.ash.reader.data.article.Article
|
import me.ash.reader.data.entity.Article
|
||||||
import me.ash.reader.data.feed.Feed
|
import me.ash.reader.data.entity.Feed
|
||||||
import me.ash.reader.data.group.Group
|
import me.ash.reader.data.entity.Group
|
||||||
import me.ash.reader.data.repository.OpmlRepository
|
import me.ash.reader.data.repository.OpmlRepository
|
||||||
import me.ash.reader.data.repository.RssHelper
|
import me.ash.reader.data.repository.RssHelper
|
||||||
import me.ash.reader.data.repository.RssRepository
|
import me.ash.reader.data.repository.RssRepository
|
||||||
import me.ash.reader.data.repository.StringsRepository
|
import me.ash.reader.data.repository.StringsRepository
|
||||||
import me.ash.reader.formatUrl
|
import me.ash.reader.ui.ext.animateScrollToPage
|
||||||
import me.ash.reader.ui.extension.animateScrollToPage
|
import me.ash.reader.ui.ext.formatUrl
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.platform.LocalFocusManager
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
import me.ash.reader.data.group.Group
|
import me.ash.reader.data.entity.Group
|
||||||
import me.ash.reader.ui.widget.ViewPager
|
import me.ash.reader.ui.component.ViewPager
|
||||||
|
|
||||||
@OptIn(ExperimentalPagerApi::class)
|
@OptIn(ExperimentalPagerApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
@ -15,8 +15,8 @@ import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import me.ash.reader.data.article.ArticleWithFeed
|
import me.ash.reader.data.entity.ArticleWithFeed
|
||||||
import me.ash.reader.formatToString
|
import me.ash.reader.ui.ext.formatAsString
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ArticleItem(
|
fun ArticleItem(
|
||||||
|
@ -40,7 +40,9 @@ fun ArticleItem(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.weight(1f).padding(start = 30.dp),
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.padding(start = 30.dp),
|
||||||
text = articleWithFeed.feed.name,
|
text = articleWithFeed.feed.name,
|
||||||
color = MaterialTheme.colorScheme.tertiary,
|
color = MaterialTheme.colorScheme.tertiary,
|
||||||
style = MaterialTheme.typography.labelMedium,
|
style = MaterialTheme.typography.labelMedium,
|
||||||
|
@ -49,7 +51,7 @@ fun ArticleItem(
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.padding(start = 6.dp),
|
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),
|
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f),
|
||||||
style = MaterialTheme.typography.labelMedium,
|
style = MaterialTheme.typography.labelMedium,
|
||||||
)
|
)
|
||||||
|
|
|
@ -8,8 +8,8 @@ import androidx.compose.foundation.lazy.LazyListScope
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.paging.compose.LazyPagingItems
|
import androidx.paging.compose.LazyPagingItems
|
||||||
import me.ash.reader.data.article.ArticleWithFeed
|
import me.ash.reader.data.entity.ArticleWithFeed
|
||||||
import me.ash.reader.formatToString
|
import me.ash.reader.ui.ext.formatAsString
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
fun LazyListScope.generateArticleList(
|
fun LazyListScope.generateArticleList(
|
||||||
|
@ -20,7 +20,7 @@ fun LazyListScope.generateArticleList(
|
||||||
var lastItemDay: String? = null
|
var lastItemDay: String? = null
|
||||||
for (itemIndex in 0 until pagingItems.itemCount) {
|
for (itemIndex in 0 until pagingItems.itemCount) {
|
||||||
val currentItem = pagingItems.peek(itemIndex) ?: continue
|
val currentItem = pagingItems.peek(itemIndex) ?: continue
|
||||||
val currentItemDay = currentItem.article.date.formatToString(context)
|
val currentItemDay = currentItem.article.date.formatAsString(context)
|
||||||
if (lastItemDay != currentItemDay) {
|
if (lastItemDay != currentItemDay) {
|
||||||
if (itemIndex != 0) {
|
if (itemIndex != 0) {
|
||||||
item { Spacer(modifier = Modifier.height(40.dp)) }
|
item { Spacer(modifier = Modifier.height(40.dp)) }
|
||||||
|
|
|
@ -24,9 +24,9 @@ import androidx.paging.LoadState
|
||||||
import androidx.paging.compose.collectAsLazyPagingItems
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import me.ash.reader.R
|
import me.ash.reader.R
|
||||||
import me.ash.reader.data.article.ArticleWithFeed
|
import me.ash.reader.data.entity.ArticleWithFeed
|
||||||
import me.ash.reader.ui.extension.collectAsStateValue
|
import me.ash.reader.ui.ext.collectAsStateValue
|
||||||
import me.ash.reader.ui.extension.getName
|
import me.ash.reader.ui.ext.getName
|
||||||
import me.ash.reader.ui.page.home.FilterBar
|
import me.ash.reader.ui.page.home.FilterBar
|
||||||
import me.ash.reader.ui.page.home.FilterState
|
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.Dispatchers
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.launch
|
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.data.repository.RssRepository
|
||||||
import me.ash.reader.ui.page.home.FilterState
|
import me.ash.reader.ui.page.home.FilterState
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
|
@ -9,9 +9,9 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
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 me.ash.reader.data.article.ArticleWithFeed
|
import me.ash.reader.data.entity.ArticleWithFeed
|
||||||
import me.ash.reader.formatToString
|
import me.ash.reader.ui.ext.formatAsString
|
||||||
import me.ash.reader.ui.extension.roundClick
|
import me.ash.reader.ui.ext.roundClick
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Header(
|
fun Header(
|
||||||
|
@ -30,7 +30,7 @@ fun Header(
|
||||||
.padding(12.dp)
|
.padding(12.dp)
|
||||||
) {
|
) {
|
||||||
Text(
|
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),
|
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f),
|
||||||
style = MaterialTheme.typography.labelMedium,
|
style = MaterialTheme.typography.labelMedium,
|
||||||
)
|
)
|
||||||
|
|
|
@ -22,7 +22,7 @@ import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.zIndex
|
import androidx.compose.ui.zIndex
|
||||||
import me.ash.reader.R
|
import me.ash.reader.R
|
||||||
import me.ash.reader.ui.widget.CanBeDisabledIconButton
|
import me.ash.reader.ui.component.CanBeDisabledIconButton
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ReadBar(
|
fun ReadBar(
|
||||||
|
|
|
@ -21,9 +21,9 @@ import androidx.compose.ui.zIndex
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import me.ash.reader.R
|
import me.ash.reader.R
|
||||||
import me.ash.reader.data.article.ArticleWithFeed
|
import me.ash.reader.data.entity.ArticleWithFeed
|
||||||
import me.ash.reader.ui.extension.collectAsStateValue
|
import me.ash.reader.ui.component.WebView
|
||||||
import me.ash.reader.ui.widget.WebView
|
import me.ash.reader.ui.ext.collectAsStateValue
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
@ -11,7 +11,7 @@ import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
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.RssHelper
|
||||||
import me.ash.reader.data.repository.RssRepository
|
import me.ash.reader.data.repository.RssRepository
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
|
@ -11,7 +11,6 @@ import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.geometry.Offset
|
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
@ -20,9 +19,8 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import me.ash.reader.R
|
import me.ash.reader.R
|
||||||
import me.ash.reader.ui.extension.paddingFixedHorizontal
|
import me.ash.reader.ui.ext.paddingFixedHorizontal
|
||||||
import me.ash.reader.ui.extension.roundClick
|
import me.ash.reader.ui.ext.roundClick
|
||||||
import me.ash.reader.ui.widget.TopTitleBox
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsPage(
|
fun SettingsPage(
|
||||||
|
@ -33,17 +31,6 @@ fun SettingsPage(
|
||||||
// LargeTopAppBar(
|
// LargeTopAppBar(
|
||||||
// title = { Text(text = "Settings") }
|
// title = { Text(text = "Settings") }
|
||||||
// )
|
// )
|
||||||
TopTitleBox(
|
|
||||||
title = "Settings",
|
|
||||||
description = "",
|
|
||||||
listState = listState,
|
|
||||||
startOffset = Offset(20f, 78f),
|
|
||||||
startHeight = 72f,
|
|
||||||
startTitleFontSize = 36f,
|
|
||||||
startDescriptionFontSize = 0f,
|
|
||||||
) {
|
|
||||||
|
|
||||||
}
|
|
||||||
Column {
|
Column {
|
||||||
SmallTopAppBar(
|
SmallTopAppBar(
|
||||||
title = {},
|
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