Add feed's icon in articles list
This commit is contained in:
parent
8d296b4b01
commit
bfe1307b27
|
@ -19,7 +19,7 @@ data class Feed(
|
||||||
@ColumnInfo
|
@ColumnInfo
|
||||||
val name: String,
|
val name: String,
|
||||||
@ColumnInfo
|
@ColumnInfo
|
||||||
var icon: String? = null,
|
var icon: ByteArray? = null,
|
||||||
@ColumnInfo
|
@ColumnInfo
|
||||||
val url: String,
|
val url: String,
|
||||||
@ColumnInfo(index = true)
|
@ColumnInfo(index = true)
|
||||||
|
@ -31,4 +31,37 @@ data class Feed(
|
||||||
) {
|
) {
|
||||||
@Ignore
|
@Ignore
|
||||||
var important: Int? = 0
|
var important: Int? = 0
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as Feed
|
||||||
|
|
||||||
|
if (id != other.id) return false
|
||||||
|
if (name != other.name) return false
|
||||||
|
if (icon != null) {
|
||||||
|
if (other.icon == null) return false
|
||||||
|
if (!icon.contentEquals(other.icon)) return false
|
||||||
|
} else if (other.icon != null) return false
|
||||||
|
if (url != other.url) return false
|
||||||
|
if (groupId != other.groupId) return false
|
||||||
|
if (accountId != other.accountId) return false
|
||||||
|
if (isFullContent != other.isFullContent) return false
|
||||||
|
if (important != other.important) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = id ?: 0
|
||||||
|
result = 31 * result + name.hashCode()
|
||||||
|
result = 31 * result + (icon?.contentHashCode() ?: 0)
|
||||||
|
result = 31 * result + url.hashCode()
|
||||||
|
result = 31 * result + groupId
|
||||||
|
result = 31 * result + accountId
|
||||||
|
result = 31 * result + isFullContent.hashCode()
|
||||||
|
result = 31 * result + (important ?: 0)
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,7 +93,6 @@ class RssRepository @Inject constructor(
|
||||||
Constraints.Builder()
|
Constraints.Builder()
|
||||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||||
.setRequiresCharging(true)
|
.setRequiresCharging(true)
|
||||||
.setRequiresDeviceIdle(true)
|
|
||||||
.build()
|
.build()
|
||||||
).addTag("sync").build()
|
).addTag("sync").build()
|
||||||
workManager.enqueue(syncWorkerRequest)
|
workManager.enqueue(syncWorkerRequest)
|
||||||
|
@ -161,18 +160,18 @@ class RssRepository @Inject constructor(
|
||||||
val articles = mutableListOf<Article>()
|
val articles = mutableListOf<Article>()
|
||||||
chunked[it].forEach { feed ->
|
chunked[it].forEach { feed ->
|
||||||
val latest = articleDao.queryLatestByFeedId(accountId, feed.id ?: 0)
|
val latest = articleDao.queryLatestByFeedId(accountId, feed.id ?: 0)
|
||||||
// if (feed.icon == null) {
|
|
||||||
// queryRssIcon(feedDao, feed, latest?.link)
|
|
||||||
// }
|
|
||||||
articles.addAll(
|
articles.addAll(
|
||||||
queryRssXml(
|
queryRssXml(
|
||||||
rssNetworkDataSource,
|
rssNetworkDataSource,
|
||||||
accountId,
|
accountId,
|
||||||
feed,
|
feed,
|
||||||
latest?.title,
|
latest?.title,
|
||||||
)
|
).also {
|
||||||
|
if (feed.icon == null && it.isNotEmpty()) {
|
||||||
|
queryRssIcon(feedDao, feed, it.first().link)
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
syncState.update {
|
syncState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
feedCount = feeds.size,
|
feedCount = feeds.size,
|
||||||
|
@ -280,34 +279,44 @@ class RssRepository @Inject constructor(
|
||||||
articleLink: String?,
|
articleLink: String?,
|
||||||
) {
|
) {
|
||||||
if (articleLink == null) return
|
if (articleLink == null) return
|
||||||
val exe = OkHttpClient()
|
val execute = OkHttpClient()
|
||||||
.newCall(Request.Builder().url(articleLink).build()).execute()
|
.newCall(Request.Builder().url(articleLink).build())
|
||||||
val content = exe.body?.string()
|
.execute()
|
||||||
Log.i("rlog", "queryRssIcon: $content")
|
val content = execute.body?.string()
|
||||||
val regex =
|
val regex =
|
||||||
Regex("""<link(.+?)rel="shortcut icon"(.+?)type="image/x-icon"(.+?)href="(.+?)"""")
|
Regex("""<link(.+?)rel="shortcut icon"(.+?)href="(.+?)"""")
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
var iconLink = regex
|
var iconLink = regex
|
||||||
.find(content)
|
.find(content)
|
||||||
?.groups?.get(4)
|
?.groups?.get(3)
|
||||||
?.value
|
?.value
|
||||||
|
Log.i("rlog", "queryRssIcon: $iconLink")
|
||||||
if (iconLink != null) {
|
if (iconLink != null) {
|
||||||
if (iconLink.startsWith("//")) {
|
if (iconLink.startsWith("//")) {
|
||||||
iconLink = "http:$iconLink"
|
iconLink = "http:$iconLink"
|
||||||
}
|
}
|
||||||
|
if (iconLink.startsWith("/")) {
|
||||||
|
val domainRegex =
|
||||||
|
Regex("""http(s)?://(([\w-]+\.)+\w+(:\d{1,5})?)""")
|
||||||
|
iconLink =
|
||||||
|
"http://${domainRegex.find(articleLink)?.groups?.get(2)?.value}$iconLink"
|
||||||
|
}
|
||||||
saveRssIcon(feedDao, feed, iconLink)
|
saveRssIcon(feedDao, feed, iconLink)
|
||||||
} else {
|
} else {
|
||||||
saveRssIcon(feedDao, feed, "")
|
// saveRssIcon(feedDao, feed, "")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
saveRssIcon(feedDao, feed, "")
|
// saveRssIcon(feedDao, feed, "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun saveRssIcon(feedDao: FeedDao, feed: Feed, iconLink: String) {
|
private suspend fun saveRssIcon(feedDao: FeedDao, feed: Feed, iconLink: String) {
|
||||||
|
val execute = OkHttpClient()
|
||||||
|
.newCall(Request.Builder().url(iconLink).build())
|
||||||
|
.execute()
|
||||||
feedDao.update(
|
feedDao.update(
|
||||||
feed.apply {
|
feed.apply {
|
||||||
icon = iconLink
|
icon = execute.body?.bytes()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
package me.ash.reader.ui.page.home.article
|
package me.ash.reader.ui.page.home.article
|
||||||
|
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.rounded.ArrowBackIosNew
|
import androidx.compose.material.icons.rounded.ArrowBackIosNew
|
||||||
import androidx.compose.material.icons.rounded.DoneAll
|
import androidx.compose.material.icons.rounded.DoneAll
|
||||||
|
@ -17,7 +21,10 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
|
import androidx.compose.ui.graphics.painter.BitmapPainter
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
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
|
||||||
|
@ -31,6 +38,7 @@ import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
import me.ash.reader.DateTimeExt
|
import me.ash.reader.DateTimeExt
|
||||||
import me.ash.reader.DateTimeExt.toString
|
import me.ash.reader.DateTimeExt.toString
|
||||||
|
import me.ash.reader.R
|
||||||
import me.ash.reader.data.article.ArticleWithFeed
|
import me.ash.reader.data.article.ArticleWithFeed
|
||||||
import me.ash.reader.data.repository.RssRepository
|
import me.ash.reader.data.repository.RssRepository
|
||||||
import me.ash.reader.ui.data.Filter
|
import me.ash.reader.ui.data.Filter
|
||||||
|
@ -97,7 +105,7 @@ fun ArticlePage(
|
||||||
"${viewState.filterImportant}${filterState.filter.description}"
|
"${viewState.filterImportant}${filterState.filter.description}"
|
||||||
},
|
},
|
||||||
listState = viewState.listState,
|
listState = viewState.listState,
|
||||||
startOffset = Offset(20f, 72f),
|
startOffset = Offset(if (true) 52f else 20f, 72f),
|
||||||
startHeight = 50f,
|
startHeight = 50f,
|
||||||
startTitleFontSize = 24f,
|
startTitleFontSize = 24f,
|
||||||
startDescriptionFontSize = 14f,
|
startDescriptionFontSize = 14f,
|
||||||
|
@ -161,7 +169,7 @@ fun ArticlePage(
|
||||||
item { Spacer(modifier = Modifier.height(40.dp)) }
|
item { Spacer(modifier = Modifier.height(40.dp)) }
|
||||||
}
|
}
|
||||||
stickyHeader {
|
stickyHeader {
|
||||||
ArticleDateHeader(currentItemDay)
|
ArticleDateHeader(currentItemDay, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
|
@ -214,6 +222,7 @@ private fun ArticleItem(
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
|
modifier = Modifier.padding(start = 32.dp),
|
||||||
text = articleWithFeed.feed.name,
|
text = articleWithFeed.feed.name,
|
||||||
fontSize = 13.sp,
|
fontSize = 13.sp,
|
||||||
fontWeight = FontWeight.Medium,
|
fontWeight = FontWeight.Medium,
|
||||||
|
@ -232,32 +241,72 @@ private fun ArticleItem(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Spacer(modifier = modifier.height(1.dp))
|
Spacer(modifier = modifier.height(1.dp))
|
||||||
Text(
|
if (true) {
|
||||||
text = articleWithFeed.article.title,
|
Box(
|
||||||
fontSize = 18.sp,
|
modifier = Modifier
|
||||||
fontWeight = FontWeight.Bold,
|
.padding(top = 3.dp)
|
||||||
color = if (isStarredFilter || articleWithFeed.article.isUnread) {
|
.size(24.dp)
|
||||||
MaterialTheme.colorScheme.onPrimaryContainer
|
.border(
|
||||||
} else {
|
2.dp,
|
||||||
MaterialTheme.colorScheme.outline
|
MaterialTheme.colorScheme.inverseOnSurface,
|
||||||
},
|
RoundedCornerShape(4.dp)
|
||||||
maxLines = 2,
|
),
|
||||||
overflow = TextOverflow.Ellipsis
|
) {
|
||||||
)
|
if (articleWithFeed.feed.icon == null) {
|
||||||
Spacer(modifier = modifier.height(1.dp))
|
Icon(
|
||||||
Text(
|
painter = painterResource(id = R.drawable.default_folder),
|
||||||
text = articleWithFeed.article.shortDescription,
|
contentDescription = "icon",
|
||||||
fontSize = 18.sp,
|
modifier = modifier
|
||||||
color = MaterialTheme.colorScheme.outline,
|
.fillMaxSize()
|
||||||
maxLines = 2,
|
.padding(2.dp),
|
||||||
overflow = TextOverflow.Ellipsis
|
tint = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
Image(
|
||||||
|
painter = BitmapPainter(
|
||||||
|
BitmapFactory.decodeByteArray(
|
||||||
|
articleWithFeed.feed.icon,
|
||||||
|
0,
|
||||||
|
articleWithFeed.feed.icon!!.size
|
||||||
|
).asImageBitmap()
|
||||||
|
),
|
||||||
|
contentDescription = "icon",
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(2.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
}
|
||||||
|
Column {
|
||||||
|
Text(
|
||||||
|
text = articleWithFeed.article.title,
|
||||||
|
fontSize = 18.sp,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = if (isStarredFilter || articleWithFeed.article.isUnread) {
|
||||||
|
MaterialTheme.colorScheme.onPrimaryContainer
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.outline
|
||||||
|
},
|
||||||
|
maxLines = 2,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
Spacer(modifier = modifier.height(1.dp))
|
||||||
|
Text(
|
||||||
|
text = articleWithFeed.article.shortDescription,
|
||||||
|
fontSize = 18.sp,
|
||||||
|
color = MaterialTheme.colorScheme.outline,
|
||||||
|
maxLines = 2,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ArticleDateHeader(date: String) {
|
private fun ArticleDateHeader(date: String, isDisplayIcon: Boolean) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.height(28.dp)
|
.height(28.dp)
|
||||||
|
@ -269,7 +318,7 @@ private fun ArticleDateHeader(date: String) {
|
||||||
text = date,
|
text = date,
|
||||||
fontSize = 13.sp,
|
fontSize = 13.sp,
|
||||||
color = MaterialTheme.colorScheme.secondary,
|
color = MaterialTheme.colorScheme.secondary,
|
||||||
modifier = Modifier.padding(horizontal = 20.dp),
|
modifier = Modifier.padding(start = (if (isDisplayIcon) 52 else 20).dp),
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package me.ash.reader.ui.page.home.feed
|
package me.ash.reader.ui.page.home.feed
|
||||||
|
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
@ -20,15 +21,15 @@ import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
|
import androidx.compose.ui.graphics.painter.BitmapPainter
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
|
||||||
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 com.google.accompanist.pager.ExperimentalPagerApi
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
import me.ash.reader.DateTimeExt
|
import me.ash.reader.DateTimeExt
|
||||||
import me.ash.reader.DateTimeExt.toString
|
import me.ash.reader.DateTimeExt.toString
|
||||||
import me.ash.reader.R
|
|
||||||
import me.ash.reader.data.feed.Feed
|
import me.ash.reader.data.feed.Feed
|
||||||
import me.ash.reader.data.group.Group
|
import me.ash.reader.data.group.Group
|
||||||
import me.ash.reader.data.group.GroupWithFeed
|
import me.ash.reader.data.group.GroupWithFeed
|
||||||
|
@ -239,10 +240,21 @@ private fun ColumnScope.FeedList(
|
||||||
) {
|
) {
|
||||||
Column(modifier = Modifier.animateContentSize()) {
|
Column(modifier = Modifier.animateContentSize()) {
|
||||||
feeds.forEach { feed ->
|
feeds.forEach { feed ->
|
||||||
|
Log.i("RLog", "FeedList: ${feed.icon}")
|
||||||
BarButton(
|
BarButton(
|
||||||
barButtonType = ItemType(
|
barButtonType = ItemType(
|
||||||
// icon = feed.icon ?: "",
|
// icon = feed.icon ?: "",
|
||||||
icon = painterResource(id = R.drawable.default_folder),
|
icon = if (feed.icon == null) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
BitmapPainter(
|
||||||
|
BitmapFactory.decodeByteArray(
|
||||||
|
feed.icon,
|
||||||
|
0,
|
||||||
|
feed.icon!!.size
|
||||||
|
).asImageBitmap()
|
||||||
|
)
|
||||||
|
},
|
||||||
content = feed.name,
|
content = feed.name,
|
||||||
important = feed.important ?: 0
|
important = feed.important ?: 0
|
||||||
)
|
)
|
||||||
|
|
|
@ -168,6 +168,7 @@ private fun Header(
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
.roundClick {
|
.roundClick {
|
||||||
context.startActivity(
|
context.startActivity(
|
||||||
Intent(Intent.ACTION_VIEW, Uri.parse(article.link))
|
Intent(Intent.ACTION_VIEW, Uri.parse(article.link))
|
||||||
|
|
5
app/src/main/java/me/ash/reader/ui/util/Symbol.kt
Normal file
5
app/src/main/java/me/ash/reader/ui/util/Symbol.kt
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package me.ash.reader.ui.util
|
||||||
|
|
||||||
|
object Symbol {
|
||||||
|
const val nothing: String = "null"
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package me.ash.reader.ui.widget
|
package me.ash.reader.ui.widget
|
||||||
|
|
||||||
|
import android.view.HapticFeedbackConstants
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.animateContentSize
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.animation.core.FastOutLinearInEasing
|
import androidx.compose.animation.core.FastOutLinearInEasing
|
||||||
|
@ -27,6 +28,7 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalView
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
@ -83,7 +85,9 @@ fun AppNavigationBar(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Divider(modifier = Modifier.alpha(0.3f))
|
Divider(
|
||||||
|
modifier = Modifier.alpha(0.3f),
|
||||||
|
)
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.background(MaterialTheme.colorScheme.surface)
|
.background(MaterialTheme.colorScheme.surface)
|
||||||
|
@ -141,6 +145,7 @@ private fun FilterBar(
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
onSelected: (Filter) -> Unit = {},
|
onSelected: (Filter) -> Unit = {},
|
||||||
) {
|
) {
|
||||||
|
val view = LocalView.current
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
horizontalArrangement = Arrangement.Center,
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
@ -168,6 +173,7 @@ private fun FilterBar(
|
||||||
)
|
)
|
||||||
.clip(CircleShape)
|
.clip(CircleShape)
|
||||||
.clickable(onClick = {
|
.clickable(onClick = {
|
||||||
|
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||||
onSelected(
|
onSelected(
|
||||||
when (index) {
|
when (index) {
|
||||||
0 -> Filter.Starred
|
0 -> Filter.Starred
|
||||||
|
@ -237,6 +243,7 @@ private fun ReaderBar(
|
||||||
starredOnClick: (afterIsStarred: Boolean) -> Unit = {},
|
starredOnClick: (afterIsStarred: Boolean) -> Unit = {},
|
||||||
fullContentOnClick: (afterIsFullContent: Boolean) -> Unit = {},
|
fullContentOnClick: (afterIsFullContent: Boolean) -> Unit = {},
|
||||||
) {
|
) {
|
||||||
|
val view = LocalView.current
|
||||||
var fullContent by remember { mutableStateOf(isFullContent) }
|
var fullContent by remember { mutableStateOf(isFullContent) }
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
@ -246,6 +253,7 @@ private fun ReaderBar(
|
||||||
.padding(horizontal = 8.dp)
|
.padding(horizontal = 8.dp)
|
||||||
) {
|
) {
|
||||||
CanBeDisabledIconButton(
|
CanBeDisabledIconButton(
|
||||||
|
modifier = Modifier.size(18.dp),
|
||||||
disabled = disabled,
|
disabled = disabled,
|
||||||
imageVector = if (isUnread) {
|
imageVector = if (isUnread) {
|
||||||
Icons.Rounded.Circle
|
Icons.Rounded.Circle
|
||||||
|
@ -255,6 +263,7 @@ private fun ReaderBar(
|
||||||
contentDescription = "Mark Unread",
|
contentDescription = "Mark Unread",
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
) {
|
) {
|
||||||
|
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||||
unreadOnClick(!isUnread)
|
unreadOnClick(!isUnread)
|
||||||
}
|
}
|
||||||
CanBeDisabledIconButton(
|
CanBeDisabledIconButton(
|
||||||
|
@ -268,6 +277,7 @@ private fun ReaderBar(
|
||||||
contentDescription = "Starred",
|
contentDescription = "Starred",
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
) {
|
) {
|
||||||
|
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||||
starredOnClick(!isStarred)
|
starredOnClick(!isStarred)
|
||||||
}
|
}
|
||||||
CanBeDisabledIconButton(
|
CanBeDisabledIconButton(
|
||||||
|
@ -277,7 +287,7 @@ private fun ReaderBar(
|
||||||
contentDescription = "Next Article",
|
contentDescription = "Next Article",
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
) {
|
) {
|
||||||
|
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||||
}
|
}
|
||||||
CanBeDisabledIconButton(
|
CanBeDisabledIconButton(
|
||||||
disabled = disabled,
|
disabled = disabled,
|
||||||
|
@ -285,7 +295,7 @@ private fun ReaderBar(
|
||||||
contentDescription = "Add Tag",
|
contentDescription = "Add Tag",
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
) {
|
) {
|
||||||
|
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||||
}
|
}
|
||||||
CanBeDisabledIconButton(
|
CanBeDisabledIconButton(
|
||||||
disabled = disabled,
|
disabled = disabled,
|
||||||
|
@ -298,6 +308,7 @@ private fun ReaderBar(
|
||||||
contentDescription = "Full Content Parsing",
|
contentDescription = "Full Content Parsing",
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
) {
|
) {
|
||||||
|
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||||
val afterIsFullContent = !fullContent
|
val afterIsFullContent = !fullContent
|
||||||
fullContent = afterIsFullContent
|
fullContent = afterIsFullContent
|
||||||
fullContentOnClick(afterIsFullContent)
|
fullContentOnClick(afterIsFullContent)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package me.ash.reader.ui.widget
|
package me.ash.reader.ui.widget
|
||||||
|
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
|
@ -12,11 +14,17 @@ 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.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
|
import androidx.compose.ui.graphics.painter.BitmapPainter
|
||||||
import androidx.compose.ui.graphics.painter.Painter
|
import androidx.compose.ui.graphics.painter.Painter
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
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.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import me.ash.reader.R
|
||||||
import me.ash.reader.ui.util.paddingFixedHorizontal
|
import me.ash.reader.ui.util.paddingFixedHorizontal
|
||||||
import me.ash.reader.ui.util.roundClick
|
import me.ash.reader.ui.util.roundClick
|
||||||
|
|
||||||
|
@ -43,60 +51,73 @@ fun BarButton(
|
||||||
end = if (barButtonType is FirstExpandType) 2.dp else 10.dp
|
end = if (barButtonType is FirstExpandType) 2.dp else 10.dp
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
when (barButtonType) {
|
||||||
when (barButtonType) {
|
is SecondExpandType -> {
|
||||||
is SecondExpandType -> {
|
Icon(
|
||||||
Icon(
|
imageVector = barButtonType.img,
|
||||||
imageVector = barButtonType.img as ImageVector,
|
contentDescription = "icon",
|
||||||
contentDescription = "icon",
|
modifier = Modifier
|
||||||
modifier = Modifier
|
.padding(end = 4.dp)
|
||||||
.padding(end = 4.dp)
|
.clip(CircleShape)
|
||||||
.clip(CircleShape)
|
.clickable(onClick = iconOnClickListener),
|
||||||
.clickable(onClick = iconOnClickListener),
|
tint = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||||
tint = MaterialTheme.colorScheme.onPrimaryContainer,
|
)
|
||||||
)
|
}
|
||||||
}
|
is ItemType -> {
|
||||||
is ItemType -> {
|
val modifier = Modifier
|
||||||
val modifier = Modifier
|
Row(
|
||||||
Row(
|
modifier = modifier
|
||||||
modifier = modifier
|
.padding(start = 28.dp, end = 4.dp)
|
||||||
.padding(start = 28.dp, end = 4.dp)
|
.size(24.dp)
|
||||||
.size(24.dp)
|
.background(
|
||||||
// .background(if (barButtonType.img.isBlank()) MaterialTheme.colorScheme.inversePrimary else Color.Unspecified),
|
if (barButtonType.icon != null) Color.Unspecified
|
||||||
.background(MaterialTheme.colorScheme.inversePrimary),
|
else MaterialTheme.colorScheme.inversePrimary
|
||||||
horizontalArrangement = Arrangement.Center,
|
),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
// .background(MaterialTheme.colorScheme.inversePrimary),
|
||||||
) {
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
if (barButtonType.icon == null) {
|
||||||
Icon(
|
Icon(
|
||||||
// painter = rememberImagePainter(barButtonType.img),
|
painter = painterResource(id = R.drawable.default_folder),
|
||||||
painter = barButtonType.img,
|
|
||||||
contentDescription = "icon",
|
contentDescription = "icon",
|
||||||
modifier = modifier.fillMaxSize(),
|
modifier = modifier.fillMaxSize(),
|
||||||
tint = MaterialTheme.colorScheme.onPrimaryContainer,
|
tint = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
Image(
|
||||||
|
painter = barButtonType.icon,
|
||||||
|
contentDescription = "icon",
|
||||||
|
modifier = modifier.fillMaxSize(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
when (barButtonType) {
|
}
|
||||||
is ButtonType -> {
|
when (barButtonType) {
|
||||||
AnimatedText(
|
is ButtonType -> {
|
||||||
text = barButtonType.text,
|
AnimatedText(
|
||||||
fontSize = 22.sp,
|
modifier = Modifier.weight(1f),
|
||||||
fontWeight = FontWeight.Bold,
|
text = barButtonType.text,
|
||||||
color = MaterialTheme.colorScheme.primary,
|
fontSize = 22.sp,
|
||||||
)
|
fontWeight = FontWeight.Bold,
|
||||||
}
|
color = MaterialTheme.colorScheme.primary,
|
||||||
else -> {
|
)
|
||||||
Text(
|
}
|
||||||
text = barButtonType.text,
|
else -> {
|
||||||
fontSize = if (barButtonType is FirstExpandType) 22.sp else 18.sp,
|
Text(
|
||||||
fontWeight = if (barButtonType is FirstExpandType) FontWeight.Bold else FontWeight.SemiBold,
|
modifier = Modifier
|
||||||
color = if (barButtonType is FirstExpandType)
|
.weight(1f)
|
||||||
MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onPrimaryContainer,
|
.padding(end = 20.dp),
|
||||||
)
|
maxLines = 1,
|
||||||
}
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
text = barButtonType.text,
|
||||||
|
fontSize = if (barButtonType is FirstExpandType) 22.sp else 18.sp,
|
||||||
|
fontWeight = if (barButtonType is FirstExpandType) FontWeight.Bold else FontWeight.SemiBold,
|
||||||
|
color = if (barButtonType is FirstExpandType)
|
||||||
|
MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onPrimaryContainer,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
when (barButtonType) {
|
when (barButtonType) {
|
||||||
is ButtonType, is ItemType, is SecondExpandType -> {
|
is ButtonType, is ItemType, is SecondExpandType -> {
|
||||||
|
@ -163,13 +184,15 @@ class SecondExpandType(
|
||||||
|
|
||||||
class ItemType(
|
class ItemType(
|
||||||
// private val icon: String,
|
// private val icon: String,
|
||||||
private val icon: Painter,
|
val icon: BitmapPainter?,
|
||||||
private val content: String,
|
private val content: String,
|
||||||
private val important: Int,
|
private val important: Int,
|
||||||
) : BarButtonType {
|
) : BarButtonType {
|
||||||
// override val img: String
|
// override val img: String
|
||||||
override val img: Painter
|
override val img: Painter
|
||||||
get() = icon
|
get() = icon ?: BitmapPainter(
|
||||||
|
BitmapFactory.decodeByteArray(byteArrayOf(), 0, 0).asImageBitmap()
|
||||||
|
)
|
||||||
override val text: String
|
override val text: String
|
||||||
get() = content
|
get() = content
|
||||||
override val additional: Any
|
override val additional: Any
|
||||||
|
|
|
@ -73,7 +73,7 @@ fun BoxScope.TopTitleBox(
|
||||||
interactionSource = MutableInteractionSource(),
|
interactionSource = MutableInteractionSource(),
|
||||||
indication = null,
|
indication = null,
|
||||||
onClickLabel = "回到顶部",
|
onClickLabel = "回到顶部",
|
||||||
onClick = clickable ?: {}
|
onClick = clickable
|
||||||
),
|
),
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
|
@ -86,6 +86,7 @@ fun BoxScope.TopTitleBox(
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(SpacerHeight.dp))
|
Spacer(modifier = Modifier.height(SpacerHeight.dp))
|
||||||
AnimatedText(
|
AnimatedText(
|
||||||
|
modifier = Modifier.width(200.dp),
|
||||||
text = description,
|
text = description,
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
fontSize = descriptionFontSize.sp,
|
fontSize = descriptionFontSize.sp,
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="28dp"
|
|
||||||
android:height="28dp"
|
|
||||||
android:viewportWidth="28"
|
|
||||||
android:viewportHeight="28">
|
|
||||||
<path
|
|
||||||
android:pathData="M0,0H28V28H0V0Z"/>
|
|
||||||
</vector>
|
|
|
@ -1,5 +0,0 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="44dp"
|
|
||||||
android:height="44dp"
|
|
||||||
android:viewportWidth="44"
|
|
||||||
android:viewportHeight="44" />
|
|
|
@ -1,36 +0,0 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="647.6362dp"
|
|
||||||
android:height="632.1738dp"
|
|
||||||
android:viewportWidth="647.6362"
|
|
||||||
android:viewportHeight="632.1738">
|
|
||||||
<path
|
|
||||||
android:pathData="M411.146,142.174L236.636,142.174a15.018,15.018 0,0 0,-15 15v387.85l-2,0.61 -42.81,13.11a8.007,8.007 0,0 1,-9.99 -5.31L39.496,137.484a8.003,8.003 0,0 1,5.31 -9.99l65.97,-20.2 191.25,-58.54 65.97,-20.2a7.989,7.989 0,0 1,9.99 5.3l32.55,106.32Z"
|
|
||||||
android:fillColor="#f2f2f2"/>
|
|
||||||
<path
|
|
||||||
android:pathData="M449.226,140.174l-39.23,-128.14a16.994,16.994 0,0 0,-21.23 -11.28l-92.75,28.39L104.776,87.694l-92.75,28.4a17.015,17.015 0,0 0,-11.28 21.23l134.08,437.93a17.027,17.027 0,0 0,16.26 12.03,16.789 16.789,0 0,0 4.97,-0.75l63.58,-19.46 2,-0.62v-2.09l-2,0.61 -64.17,19.65a15.015,15.015 0,0 1,-18.73 -9.95l-134.07,-437.94a14.979,14.979 0,0 1,9.95 -18.73l92.75,-28.4 191.24,-58.54 92.75,-28.4a15.156,15.156 0,0 1,4.41 -0.66,15.015 15.015,0 0,1 14.32,10.61l39.05,127.56 0.62,2h2.08Z"
|
|
||||||
android:fillColor="#3f3d56"/>
|
|
||||||
<path
|
|
||||||
android:pathData="M122.681,127.821a9.016,9.016 0,0 1,-8.611 -6.367l-12.88,-42.072a8.999,8.999 0,0 1,5.971 -11.24l175.939,-53.864a9.009,9.009 0,0 1,11.241 5.971l12.88,42.072a9.01,9.01 0,0 1,-5.971 11.241L125.31,127.426A8.976,8.976 0,0 1,122.681 127.821Z"
|
|
||||||
android:fillColor="#6c63ff"/>
|
|
||||||
<path
|
|
||||||
android:pathData="M190.154,24.955m-20,0a20,20 0,1 1,40 0a20,20 0,1 1,-40 0"
|
|
||||||
android:fillColor="#6c63ff"/>
|
|
||||||
<path
|
|
||||||
android:pathData="M190.154,24.955m-12.665,0a12.665,12.665 0,1 1,25.329 0a12.665,12.665 0,1 1,-25.329 0"
|
|
||||||
android:fillColor="#fff"/>
|
|
||||||
<path
|
|
||||||
android:pathData="M602.636,582.174h-338a8.51,8.51 0,0 1,-8.5 -8.5v-405a8.51,8.51 0,0 1,8.5 -8.5h338a8.51,8.51 0,0 1,8.5 8.5v405A8.51,8.51 0,0 1,602.636 582.174Z"
|
|
||||||
android:fillColor="#e6e6e6"/>
|
|
||||||
<path
|
|
||||||
android:pathData="M447.136,140.174h-210.5a17.024,17.024 0,0 0,-17 17v407.8l2,-0.61v-407.19a15.018,15.018 0,0 1,15 -15L447.756,142.174ZM630.636,140.174h-394a17.024,17.024 0,0 0,-17 17v458a17.024,17.024 0,0 0,17 17h394a17.024,17.024 0,0 0,17 -17v-458A17.024,17.024 0,0 0,630.636 140.174ZM645.636,615.174a15.018,15.018 0,0 1,-15 15h-394a15.018,15.018 0,0 1,-15 -15v-458a15.018,15.018 0,0 1,15 -15h394a15.018,15.018 0,0 1,15 15Z"
|
|
||||||
android:fillColor="#3f3d56"/>
|
|
||||||
<path
|
|
||||||
android:pathData="M525.636,184.174h-184a9.01,9.01 0,0 1,-9 -9v-44a9.01,9.01 0,0 1,9 -9h184a9.01,9.01 0,0 1,9 9v44A9.01,9.01 0,0 1,525.636 184.174Z"
|
|
||||||
android:fillColor="#6c63ff"/>
|
|
||||||
<path
|
|
||||||
android:pathData="M433.636,105.174m-20,0a20,20 0,1 1,40 0a20,20 0,1 1,-40 0"
|
|
||||||
android:fillColor="#6c63ff"/>
|
|
||||||
<path
|
|
||||||
android:pathData="M433.636,105.174m-12.182,0a12.182,12.182 0,1 1,24.364 0a12.182,12.182 0,1 1,-24.364 0"
|
|
||||||
android:fillColor="#fff"/>
|
|
||||||
</vector>
|
|
Loading…
Reference in New Issue
Block a user