Compose DownloadCard (#339)
* Cleanup res * Compose DownloadCard * Refine * Fix CI * Fixes * Fix recompose * Fix textStyle * Fixes * Fixes * Restore design specs and clean up --------- Co-authored-by: GG Bond <whatever@tagkong.com>
This commit is contained in:
1 parent
618f14a732
commit
198960f93c
13 files changed
+342
-604
No files matched your search
@@ -18,6 +18,12 @@ package com.hippo.ehviewer.download
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import android.util.SparseLongArray
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisallowComposableCalls
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.remember
|
||||
import com.google.android.material.math.MathUtils
|
||||
import com.hippo.ehviewer.EhDB
|
||||
import com.hippo.ehviewer.Settings
|
||||
@@ -47,6 +53,7 @@ import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.shareIn
|
||||
import kotlinx.coroutines.launch
|
||||
import splitties.init.appCtx
|
||||
@@ -246,15 +253,12 @@ object DownloadManager : OnSpiderListener {
|
||||
|
||||
private val callbackFlowScope = CoroutineScope(Dispatchers.IO)
|
||||
|
||||
private val _stateFlow = callbackFlow {
|
||||
val _stateFlow = callbackFlow {
|
||||
val listener = object : DownloadInfoListener {
|
||||
override fun onUpdate(info: DownloadInfo) {
|
||||
trySend(info.gid)
|
||||
}
|
||||
|
||||
override fun onUpdateAll() {
|
||||
trySend(0)
|
||||
trySend(info)
|
||||
}
|
||||
override fun onUpdateAll() {}
|
||||
}
|
||||
mDownloadInfoListener = listener
|
||||
awaitClose {
|
||||
@@ -262,7 +266,21 @@ object DownloadManager : OnSpiderListener {
|
||||
}
|
||||
}.shareIn(callbackFlowScope, SharingStarted.Eagerly)
|
||||
|
||||
fun stateFlow(gid: Long) = _stateFlow.filter { it == gid }
|
||||
@Stable
|
||||
@Composable
|
||||
fun collectDownloadState(gid: Long): State<Int> {
|
||||
val flow = remember { stateFlow(gid).map { getDownloadState(it) } }
|
||||
return flow.collectAsState(getDownloadState(gid))
|
||||
}
|
||||
|
||||
@Stable
|
||||
@Composable
|
||||
inline fun <T> updatedDownloadInfo(info: DownloadInfo, crossinline transform: @DisallowComposableCalls DownloadInfo.() -> T): T {
|
||||
val flow = remember { _stateFlow.filter { it.gid == info.gid }.map { transform(it) } }
|
||||
return flow.collectAsState(transform(info)).value
|
||||
}
|
||||
|
||||
fun stateFlow(gid: Long) = _stateFlow.map { it.gid }.filter { it == gid }
|
||||
|
||||
fun stateFlow() = _stateFlow
|
||||
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
package com.hippo.ehviewer.ui.main
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.DragHandle
|
||||
import androidx.compose.material.icons.filled.Pause
|
||||
import androidx.compose.material.icons.filled.PlayArrow
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ProvideTextStyle
|
||||
import androidx.compose.material3.ShapeDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import coil.compose.AsyncImage
|
||||
import coil.compose.AsyncImagePainter
|
||||
import com.hippo.ehviewer.EhApplication
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhUtils
|
||||
import com.hippo.ehviewer.coil.read
|
||||
import com.hippo.ehviewer.dao.DownloadInfo
|
||||
import com.hippo.ehviewer.download.DownloadManager
|
||||
import com.hippo.ehviewer.download.downloadDir
|
||||
import com.hippo.ehviewer.ktbuilder.imageRequest
|
||||
import com.hippo.ehviewer.ui.tools.CropDefaults
|
||||
import com.hippo.ehviewer.ui.tools.CrystalCard
|
||||
import com.hippo.ehviewer.ui.tools.GalleryListCardRating
|
||||
import com.hippo.ehviewer.util.FileUtils
|
||||
import com.hippo.ehviewer.util.sendTo
|
||||
import com.hippo.unifile.UniFile
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
private fun AsyncThumb(
|
||||
info: DownloadInfo,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val path = remember(info.gid) { info.downloadDir?.subFile(".thumb") }
|
||||
var contentScale by remember(info.gid) { mutableStateOf(ContentScale.Fit) }
|
||||
val coroutineScope = rememberCoroutineScope { Dispatchers.IO }
|
||||
val context = LocalContext.current
|
||||
var localReq by remember(info.gid) {
|
||||
path?.takeIf { it.isFile }?.uri?.let {
|
||||
context.imageRequest {
|
||||
data(it.toString())
|
||||
memoryCacheKey(info.thumbKey)
|
||||
}
|
||||
}.let { mutableStateOf(it) }
|
||||
}
|
||||
AsyncImage(
|
||||
model = localReq ?: requestOf(info),
|
||||
contentDescription = null,
|
||||
modifier = modifier,
|
||||
onState = { state ->
|
||||
if (state is AsyncImagePainter.State.Success) {
|
||||
state.result.drawable.run {
|
||||
if (CropDefaults.shouldCrop(intrinsicWidth, intrinsicHeight)) {
|
||||
contentScale = ContentScale.Crop
|
||||
}
|
||||
}
|
||||
path?.let {
|
||||
coroutineScope.launch {
|
||||
runCatching {
|
||||
if (!path.exists() && path.ensureFile()) {
|
||||
val key = info.thumbKey!!
|
||||
EhApplication.imageCache.read(key) {
|
||||
UniFile.fromFile(data.toFile())!!.openFileDescriptor("r").use { src ->
|
||||
path.openFileDescriptor("w").use { dst ->
|
||||
src sendTo dst
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state is AsyncImagePainter.State.Error) {
|
||||
path?.let {
|
||||
coroutineScope.launch {
|
||||
if (path.exists()) {
|
||||
path.delete()
|
||||
localReq = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
contentScale = contentScale,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DownloadCard(
|
||||
onClick: () -> Unit,
|
||||
onThumbClick: () -> Unit,
|
||||
onLongClick: () -> Unit,
|
||||
onStart: () -> Unit,
|
||||
onStop: () -> Unit,
|
||||
onDrag: () -> Unit,
|
||||
info: DownloadInfo,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
CrystalCard(
|
||||
modifier = modifier,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
) {
|
||||
Row {
|
||||
Card(onClick = onThumbClick) {
|
||||
AsyncThumb(
|
||||
info = info,
|
||||
modifier = Modifier.aspectRatio(DEFAULT_ASPECT).fillMaxSize(),
|
||||
)
|
||||
}
|
||||
|
||||
val stateFailed = stringResource(R.string.download_state_failed)
|
||||
val stateFailed2 = stringResource(R.string.download_state_failed_2, info.legacy)
|
||||
val downloadState by DownloadManager.collectDownloadState(info.gid)
|
||||
val stateText = when (downloadState) {
|
||||
DownloadInfo.STATE_NONE -> stringResource(R.string.download_state_none)
|
||||
DownloadInfo.STATE_WAIT -> stringResource(R.string.download_state_wait)
|
||||
DownloadInfo.STATE_DOWNLOAD -> null
|
||||
DownloadInfo.STATE_FAILED -> if (info.legacy <= 0) stateFailed else stateFailed2
|
||||
DownloadInfo.STATE_FINISH -> stringResource(R.string.download_state_finish)
|
||||
else -> error("Illegal State $downloadState!!!")
|
||||
}
|
||||
ConstraintLayout(modifier = Modifier.padding(8.dp, 4.dp).fillMaxSize()) {
|
||||
val (
|
||||
titleRef, uploaderRef, ratingRef, categoryRef, actionsRef, stateTextRef,
|
||||
progressBarRef, progressTextRef, speedRef,
|
||||
) = createRefs()
|
||||
Text(
|
||||
text = EhUtils.getSuitableTitle(info),
|
||||
maxLines = 2,
|
||||
modifier = Modifier.constrainAs(titleRef) {
|
||||
top.linkTo(parent.top)
|
||||
start.linkTo(parent.start)
|
||||
}.fillMaxWidth(),
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
)
|
||||
if (downloadState != DownloadInfo.STATE_DOWNLOAD) {
|
||||
ProvideTextStyle(MaterialTheme.typography.labelLarge) {
|
||||
info.uploader?.let {
|
||||
Text(
|
||||
text = it,
|
||||
modifier = Modifier.constrainAs(uploaderRef) {
|
||||
start.linkTo(parent.start)
|
||||
bottom.linkTo(ratingRef.top)
|
||||
}.alpha(if (info.disowned) 0.5f else 1f),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
GalleryListCardRating(
|
||||
rating = info.rating,
|
||||
modifier = Modifier.constrainAs(ratingRef) {
|
||||
start.linkTo(parent.start)
|
||||
bottom.linkTo(categoryRef.top)
|
||||
},
|
||||
)
|
||||
val categoryColor = EhUtils.getCategoryColor(info.category)
|
||||
val categoryText = EhUtils.getCategory(info.category).uppercase()
|
||||
Text(
|
||||
text = categoryText,
|
||||
modifier = Modifier.constrainAs(categoryRef) {
|
||||
start.linkTo(parent.start)
|
||||
bottom.linkTo(parent.bottom)
|
||||
}.clip(ShapeDefaults.Small).background(categoryColor).padding(vertical = 2.dp, horizontal = 8.dp),
|
||||
color = if (Settings.harmonizeCategoryColor) Color.Unspecified else EhUtils.categoryTextColor,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val (total, finished, speed) = DownloadManager.updatedDownloadInfo(info) {
|
||||
Triple(total, finished, speed)
|
||||
}
|
||||
ProvideTextStyle(MaterialTheme.typography.labelMedium) {
|
||||
if (total <= 0 || finished < 0) {
|
||||
LinearProgressIndicator(
|
||||
modifier = Modifier.constrainAs(progressBarRef) {
|
||||
bottom.linkTo(actionsRef.top)
|
||||
}.fillMaxWidth(),
|
||||
)
|
||||
} else {
|
||||
LinearProgressIndicator(
|
||||
progress = { finished.toFloat() / total.toFloat() },
|
||||
modifier = Modifier.constrainAs(progressBarRef) {
|
||||
bottom.linkTo(actionsRef.top)
|
||||
}.fillMaxWidth(),
|
||||
)
|
||||
Text(
|
||||
text = "$finished/$total",
|
||||
modifier = Modifier.constrainAs(progressTextRef) {
|
||||
bottom.linkTo(progressBarRef.top)
|
||||
start.linkTo(progressBarRef.start)
|
||||
},
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = FileUtils.humanReadableByteCount(speed.coerceAtLeast(0), false) + "/S",
|
||||
modifier = Modifier.constrainAs(speedRef) {
|
||||
bottom.linkTo(progressBarRef.top)
|
||||
end.linkTo(progressBarRef.end)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.constrainAs(actionsRef) {
|
||||
end.linkTo(parent.end)
|
||||
bottom.linkTo(parent.bottom)
|
||||
},
|
||||
) {
|
||||
IconButton(onClick = { }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.DragHandle,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.pointerInput(info.gid) {
|
||||
awaitPointerEventScope { onDrag() }
|
||||
},
|
||||
)
|
||||
}
|
||||
if (downloadState == DownloadInfo.STATE_WAIT || downloadState == DownloadInfo.STATE_DOWNLOAD) {
|
||||
IconButton(onClick = onStop) {
|
||||
Icon(imageVector = Icons.Default.Pause, contentDescription = null)
|
||||
}
|
||||
} else {
|
||||
IconButton(onClick = onStart) {
|
||||
Icon(imageVector = Icons.Default.PlayArrow, contentDescription = null)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (stateText != null) {
|
||||
Text(
|
||||
text = stateText,
|
||||
modifier = Modifier.constrainAs(stateTextRef) {
|
||||
end.linkTo(parent.end, 16.dp)
|
||||
bottom.linkTo(actionsRef.top)
|
||||
},
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,24 +16,17 @@
|
||||
package com.hippo.ehviewer.ui.scene
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.graphics.Rect
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.DriveFileMove
|
||||
import androidx.compose.material.icons.automirrored.outlined.Label
|
||||
@@ -42,29 +35,22 @@ import androidx.compose.material.icons.filled.DoneAll
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.icons.filled.Pause
|
||||
import androidx.compose.material.icons.filled.PlayArrow
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ShapeDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringArrayResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
@@ -75,27 +61,17 @@ import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import arrow.core.partially1
|
||||
import coil.compose.AsyncImage
|
||||
import coil.compose.AsyncImagePainter
|
||||
import com.hippo.ehviewer.EhApplication.Companion.imageCache
|
||||
import com.hippo.ehviewer.EhDB
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.Settings.detailSize
|
||||
import com.hippo.ehviewer.client.EhUtils
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||
import com.hippo.ehviewer.coil.read
|
||||
import com.hippo.ehviewer.dao.DownloadInfo
|
||||
import com.hippo.ehviewer.databinding.DrawerListRvBinding
|
||||
import com.hippo.ehviewer.databinding.ItemDownloadBinding
|
||||
import com.hippo.ehviewer.databinding.ItemDrawerListBinding
|
||||
import com.hippo.ehviewer.databinding.SceneDownloadBinding
|
||||
import com.hippo.ehviewer.download.DownloadManager
|
||||
import com.hippo.ehviewer.download.DownloadService
|
||||
import com.hippo.ehviewer.download.DownloadService.Companion.clear
|
||||
import com.hippo.ehviewer.download.downloadDir
|
||||
import com.hippo.ehviewer.ktbuilder.imageRequest
|
||||
import com.hippo.ehviewer.ui.confirmRemoveDownload
|
||||
import com.hippo.ehviewer.ui.confirmRemoveDownloadRange
|
||||
import com.hippo.ehviewer.ui.legacy.AutoStaggeredGridLayoutManager
|
||||
@@ -103,23 +79,17 @@ import com.hippo.ehviewer.ui.legacy.BaseDialogBuilder
|
||||
import com.hippo.ehviewer.ui.legacy.EditTextDialogBuilder
|
||||
import com.hippo.ehviewer.ui.legacy.HandlerDrawable
|
||||
import com.hippo.ehviewer.ui.legacy.ViewTransition
|
||||
import com.hippo.ehviewer.ui.main.DEFAULT_ASPECT
|
||||
import com.hippo.ehviewer.ui.main.requestOf
|
||||
import com.hippo.ehviewer.ui.main.DownloadCard
|
||||
import com.hippo.ehviewer.ui.navToReader
|
||||
import com.hippo.ehviewer.ui.setMD3Content
|
||||
import com.hippo.ehviewer.ui.tools.CropDefaults
|
||||
import com.hippo.ehviewer.ui.tools.DialogState
|
||||
import com.hippo.ehviewer.util.FileUtils
|
||||
import com.hippo.ehviewer.util.containsIgnoreCase
|
||||
import com.hippo.ehviewer.util.mapToLongArray
|
||||
import com.hippo.ehviewer.util.sendTo
|
||||
import com.hippo.unifile.UniFile
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import eu.kanade.tachiyomi.util.lang.launchUI
|
||||
import eu.kanade.tachiyomi.util.lang.withNonCancellableContext
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import rikka.core.res.resolveColor
|
||||
@@ -555,16 +525,6 @@ class DownloadsScene : SearchBarScene() {
|
||||
return drawerBinding.root
|
||||
}
|
||||
|
||||
fun onItemClick(position: Int): Boolean {
|
||||
val context = context ?: return false
|
||||
val list = mList ?: return false
|
||||
if (position < 0 || position >= list.size) {
|
||||
return false
|
||||
}
|
||||
context.navToReader(list[position].galleryInfo)
|
||||
return true
|
||||
}
|
||||
|
||||
private class DownloadLabelHolder(val binding: ItemDrawerListBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
@@ -666,218 +626,58 @@ class DownloadsScene : SearchBarScene() {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private inner class DownloadHolder(
|
||||
private val binding: ItemDownloadBinding,
|
||||
) : RecyclerView.ViewHolder(binding.root), View.OnClickListener {
|
||||
|
||||
init {
|
||||
// TODO cancel on click listener when select items
|
||||
binding.start.setOnClickListener(this)
|
||||
binding.stop.setOnClickListener(this)
|
||||
binding.thumb.setMD3Content {
|
||||
val height by collectListThumbSizeAsState()
|
||||
Spacer(modifier = Modifier.height(height).fillMaxWidth())
|
||||
}
|
||||
binding.handle.setOnTouchListener { _, event ->
|
||||
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
|
||||
mItemTouchHelper!!.startDrag(this)
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
val context = context
|
||||
val activity: Activity? = mainActivity
|
||||
val recyclerView = this@DownloadsScene.binding.recyclerView
|
||||
if (null == context || null == activity || tracker.isInCustomChoice) {
|
||||
return
|
||||
}
|
||||
val list = mList ?: return
|
||||
val size = list.size
|
||||
val index = recyclerView.getChildAdapterPosition(itemView)
|
||||
if (index < 0 || index >= size) {
|
||||
return
|
||||
}
|
||||
when (v) {
|
||||
binding.thumb -> {
|
||||
navAnimated(
|
||||
R.id.galleryDetailScene,
|
||||
bundleOf(GalleryDetailScene.KEY_ARGS to GalleryInfoArgs(list[index].galleryInfo)),
|
||||
)
|
||||
}
|
||||
|
||||
binding.start -> {
|
||||
val intent = Intent(activity, DownloadService::class.java)
|
||||
intent.action = DownloadService.ACTION_START
|
||||
intent.putExtra(DownloadService.KEY_GALLERY_INFO, list[index].galleryInfo)
|
||||
ContextCompat.startForegroundService(activity, intent)
|
||||
}
|
||||
|
||||
binding.stop -> {
|
||||
lifecycleScope.launchIO {
|
||||
DownloadManager.stopDownload(list[index].gid)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun bind(info: DownloadInfo, isChecked: Boolean) {
|
||||
binding.root.isChecked = isChecked
|
||||
binding.thumb.setMD3Content {
|
||||
val height by collectListThumbSizeAsState()
|
||||
Card(onClick = ::onClick.partially1(binding.thumb)) {
|
||||
CompanionAsyncThumb(
|
||||
info = info,
|
||||
path = info.downloadDir?.subFile(".thumb"),
|
||||
modifier = Modifier.height(height).aspectRatio(DEFAULT_ASPECT),
|
||||
)
|
||||
}
|
||||
}
|
||||
binding.title.text = EhUtils.getSuitableTitle(info)
|
||||
binding.uploader.text = info.uploader
|
||||
binding.rating.rating = info.rating
|
||||
binding.category.setMD3Content {
|
||||
val categoryColor = EhUtils.getCategoryColor(info.category)
|
||||
val categoryText = EhUtils.getCategory(info.category).uppercase()
|
||||
Text(
|
||||
text = categoryText,
|
||||
modifier = Modifier.clip(ShapeDefaults.Small).background(categoryColor).padding(vertical = 2.dp, horizontal = 8.dp),
|
||||
color = if (Settings.harmonizeCategoryColor) Color.Unspecified else EhUtils.categoryTextColor,
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
)
|
||||
}
|
||||
bindForState(info)
|
||||
}
|
||||
|
||||
fun bindForState(info: DownloadInfo) {
|
||||
val context = context ?: return
|
||||
when (info.state) {
|
||||
DownloadInfo.STATE_NONE -> bindState(
|
||||
info,
|
||||
context.getString(R.string.download_state_none),
|
||||
)
|
||||
|
||||
DownloadInfo.STATE_WAIT -> bindState(
|
||||
info,
|
||||
context.getString(R.string.download_state_wait),
|
||||
)
|
||||
|
||||
DownloadInfo.STATE_DOWNLOAD -> bindProgress(info)
|
||||
DownloadInfo.STATE_FAILED -> {
|
||||
val text: String = if (info.legacy <= 0) {
|
||||
context.getString(R.string.download_state_failed)
|
||||
} else {
|
||||
context.getString(R.string.download_state_failed_2, info.legacy)
|
||||
}
|
||||
bindState(info, text)
|
||||
}
|
||||
|
||||
DownloadInfo.STATE_FINISH -> bindState(
|
||||
info,
|
||||
context.getString(R.string.download_state_finish),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindState(info: DownloadInfo, newState: String) {
|
||||
binding.run {
|
||||
uploader.visibility = View.VISIBLE
|
||||
rating.visibility = View.VISIBLE
|
||||
category.visibility = View.VISIBLE
|
||||
state.visibility = View.VISIBLE
|
||||
progressBar.visibility = View.GONE
|
||||
percent.visibility = View.GONE
|
||||
speed.visibility = View.GONE
|
||||
if (info.state == DownloadInfo.STATE_WAIT || info.state == DownloadInfo.STATE_DOWNLOAD) {
|
||||
start.visibility = View.GONE
|
||||
stop.visibility = View.VISIBLE
|
||||
} else {
|
||||
start.visibility = View.VISIBLE
|
||||
stop.visibility = View.GONE
|
||||
}
|
||||
state.text = newState
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun bindProgress(info: DownloadInfo) {
|
||||
binding.run {
|
||||
uploader.visibility = View.GONE
|
||||
rating.visibility = View.GONE
|
||||
category.visibility = View.GONE
|
||||
state.visibility = View.GONE
|
||||
progressBar.visibility = View.VISIBLE
|
||||
percent.visibility = View.VISIBLE
|
||||
speed.visibility = View.VISIBLE
|
||||
if (info.state == DownloadInfo.STATE_WAIT || info.state == DownloadInfo.STATE_DOWNLOAD) {
|
||||
start.visibility = View.GONE
|
||||
stop.visibility = View.VISIBLE
|
||||
} else {
|
||||
start.visibility = View.VISIBLE
|
||||
stop.visibility = View.GONE
|
||||
}
|
||||
if (info.total <= 0 || info.finished < 0) {
|
||||
percent.text = null
|
||||
progressBar.isIndeterminate = true
|
||||
} else {
|
||||
percent.text = info.finished.toString() + "/" + info.total
|
||||
progressBar.isIndeterminate = false
|
||||
progressBar.max = info.total
|
||||
progressBar.progress = info.finished
|
||||
}
|
||||
speed.text =
|
||||
FileUtils.humanReadableByteCount(info.speed.coerceAtLeast(0), false) + "/S"
|
||||
}
|
||||
}
|
||||
}
|
||||
class DownloadHolder(val view: ComposeView) : RecyclerView.ViewHolder(view)
|
||||
|
||||
private val diffCallback = object : DiffUtil.ItemCallback<DownloadInfo>() {
|
||||
override fun areItemsTheSame(oldItem: DownloadInfo, newItem: DownloadInfo) = oldItem.gid == newItem.gid
|
||||
override fun areContentsTheSame(oldItem: DownloadInfo, newItem: DownloadInfo) = oldItem == newItem
|
||||
override fun getChangePayload(oldItem: DownloadInfo, newItem: DownloadInfo): Any? {
|
||||
return if (oldItem.downloadInfo != newItem.downloadInfo) {
|
||||
PAYLOAD_DOWNLOAD_INFO
|
||||
} else {
|
||||
super.getChangePayload(oldItem, newItem)
|
||||
}
|
||||
}
|
||||
override fun areContentsTheSame(oldItem: DownloadInfo, newItem: DownloadInfo) = oldItem.gid == newItem.gid
|
||||
}
|
||||
|
||||
private inner class DownloadAdapter : ListAdapter<DownloadInfo, DownloadHolder>(diffCallback) {
|
||||
private val mInflater: LayoutInflater = layoutInflater
|
||||
var tracker: GallerySelectionTracker<DownloadInfo>? = null
|
||||
|
||||
override fun getItemId(position: Int) = getItem(position).gid
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DownloadHolder {
|
||||
val holder = DownloadHolder(ItemDownloadBinding.inflate(mInflater, parent, false))
|
||||
holder.itemView.setOnClickListener { onItemClick(holder.bindingAdapterPosition) }
|
||||
return holder
|
||||
}
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = DownloadHolder(ComposeView(parent.context))
|
||||
|
||||
override fun onBindViewHolder(holder: DownloadHolder, position: Int) {
|
||||
val info = getItem(position)
|
||||
holder.bind(info, tracker?.isSelected(info.gid) ?: false)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(
|
||||
holder: DownloadHolder,
|
||||
position: Int,
|
||||
payloads: MutableList<Any>,
|
||||
) {
|
||||
payloads.forEach { payload ->
|
||||
when (payload) {
|
||||
PAYLOAD_DOWNLOAD_INFO -> {
|
||||
val info = getItem(position)
|
||||
holder.bindForState(info)
|
||||
return
|
||||
val checked = tracker?.isSelected(info.gid) ?: false
|
||||
holder.view.setMD3Content {
|
||||
val context = LocalContext.current
|
||||
val height by collectListThumbSizeAsState()
|
||||
key(info.gid) {
|
||||
CheckableItem(checked = checked) {
|
||||
DownloadCard(
|
||||
onClick = {
|
||||
context.navToReader(info.galleryInfo)
|
||||
},
|
||||
onThumbClick = {
|
||||
navAnimated(
|
||||
R.id.galleryDetailScene,
|
||||
bundleOf(GalleryDetailScene.KEY_ARGS to GalleryInfoArgs(info.galleryInfo)),
|
||||
)
|
||||
},
|
||||
onLongClick = {
|
||||
},
|
||||
onStart = {
|
||||
val intent = Intent(activity, DownloadService::class.java)
|
||||
intent.action = DownloadService.ACTION_START
|
||||
intent.putExtra(DownloadService.KEY_GALLERY_INFO, info.galleryInfo)
|
||||
ContextCompat.startForegroundService(requireActivity(), intent)
|
||||
},
|
||||
onStop = {
|
||||
lifecycleScope.launchIO {
|
||||
DownloadManager.stopDownload(info.gid)
|
||||
}
|
||||
},
|
||||
onDrag = { mItemTouchHelper?.startDrag(holder) },
|
||||
info = info,
|
||||
modifier = Modifier.height(height),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
super.onBindViewHolder(holder, position, payloads)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1021,65 +821,3 @@ class DownloadsScene : SearchBarScene() {
|
||||
private const val PAYLOAD_DOWNLOAD_INFO = 0
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CompanionAsyncThumb(
|
||||
info: GalleryInfo,
|
||||
path: UniFile?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var contentScale by remember(info.gid) { mutableStateOf(ContentScale.Fit) }
|
||||
val coroutineScope = rememberCoroutineScope { Dispatchers.IO }
|
||||
val context = LocalContext.current
|
||||
var localReq by remember(info.gid) {
|
||||
path?.takeIf { it.isFile }?.uri?.let {
|
||||
context.imageRequest {
|
||||
data(it.toString())
|
||||
memoryCacheKey(info.thumbKey)
|
||||
}
|
||||
}.let { mutableStateOf(it) }
|
||||
}
|
||||
AsyncImage(
|
||||
model = localReq ?: requestOf(info),
|
||||
contentDescription = null,
|
||||
modifier = modifier,
|
||||
onState = { state ->
|
||||
if (state is AsyncImagePainter.State.Success) {
|
||||
state.result.drawable.run {
|
||||
if (CropDefaults.shouldCrop(intrinsicWidth, intrinsicHeight)) {
|
||||
contentScale = ContentScale.Crop
|
||||
}
|
||||
}
|
||||
path?.let {
|
||||
coroutineScope.launch {
|
||||
runCatching {
|
||||
if (!path.exists() && path.ensureFile()) {
|
||||
val key = info.thumbKey!!
|
||||
imageCache.read(key) {
|
||||
UniFile.fromFile(data.toFile())!!.openFileDescriptor("r").use { src ->
|
||||
path.openFileDescriptor("w").use { dst ->
|
||||
src sendTo dst
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state is AsyncImagePainter.State.Error) {
|
||||
path?.let {
|
||||
coroutineScope.launch {
|
||||
if (path.exists()) {
|
||||
path.delete()
|
||||
localReq = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
contentScale = contentScale,
|
||||
)
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright 2018 Hippo Seven
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="120dp"
|
||||
android:height="120dp"
|
||||
android:tint="?colorTertiary"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M16.5,10.8c-0.61,0-1.1,0.49-1.1,1.1c0,0.61,0.49,1.1,1.1,1.1s1.1-0.49,1.1-1.1C17.6,11.29,17.11,10.8,16.5,10.8z" />
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M8.2,10.1c-0.61,0-1.1,0.49-1.1,1.1s0.49,1.1,1.1,1.1s1.1-0.49,1.1-1.1S8.81,10.1,8.2,10.1z" />
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M22.1,16.4c-0.46-3.33-2.24-5.93-4.81-7.45L19.5,6.3c0.1-0.1,0.1-0.3,0-0.4c-0.1-0.1-0.3-0.1-0.4,0l-2.27,2.79c-1.26-0.66-2.69-1.07-4.23-1.19c-1.4-0.15-2.77-0.01-4.05,0.37L6.7,4.7C6.6,4.6,6.4,4.5,6.3,4.6C6.2,4.7,6.1,4.9,6.2,5l1.79,3.05c-2.83,1.02-5.12,3.3-6.19,6.55l15.44,1.37c0,0.02-0.01,0.04-0.01,0.05c0,0.59,0.48,1.07,1.07,1.07c0.54,0,0.97-0.41,1.04-0.93L22.1,16.4z M2.63,14.07c1.51-3.74,4.83-6.03,8.84-6.03c0.35,0,0.71,0.02,1.07,0.06c4.55,0.36,7.94,3.33,8.84,7.64l-2.12-0.19c-0.27-0.68-0.95-1.46-0.95-1.46s-0.58,0.66-0.89,1.29L2.63,14.07z" />
|
||||
|
||||
</vector>
|
||||
@@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright 2016 Hippo Seven
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="@string/pd_help_circle" />
|
||||
|
||||
</vector>
|
||||
@@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright 2016 Hippo Seven
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="@string/pd_play" />
|
||||
|
||||
</vector>
|
||||
@@ -1,151 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright 2016 Hippo Seven
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
style="@style/Widget.Material3.CardView.Outlined"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checkable="true"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
app:strokeColor="@android:color/transparent">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.compose.ui.platform.ComposeView
|
||||
android:id="@+id/thumb"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
style="@style/CardTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_toEndOf="@id/thumb" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/uploader"
|
||||
style="@style/CardMessage"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@id/rating"
|
||||
android:layout_alignStart="@id/title"
|
||||
android:layout_marginTop="2dp" />
|
||||
|
||||
<com.hippo.ehviewer.ui.legacy.SimpleRatingView
|
||||
android:id="@+id/rating"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@id/category"
|
||||
android:layout_alignStart="@id/title"
|
||||
android:layout_marginBottom="2dp" />
|
||||
|
||||
<androidx.compose.ui.platform.ComposeView
|
||||
android:id="@+id/category"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignStart="@id/title"
|
||||
android:layout_alignBottom="@id/thumb"
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/handle"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_alignBottom="@+id/thumb"
|
||||
android:layout_toStartOf="@+id/actions"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:padding="8dp"
|
||||
app:srcCompat="@drawable/ic_baseline_reorder_24" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/actions"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignBottom="@+id/thumb"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:paddingEnd="8dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/start"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:padding="8dp"
|
||||
app:srcCompat="@drawable/v_play_x24" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/stop"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:padding="8dp"
|
||||
app:srcCompat="@drawable/v_pause_x24" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/state"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@id/actions"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:paddingEnd="16dp" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@id/actions"
|
||||
android:layout_alignStart="@id/title"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:indeterminate="false" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/percent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@id/progress_bar"
|
||||
android:layout_alignStart="@id/title"
|
||||
android:textSize="@dimen/text_super_small" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/speed"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@id/progress_bar"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:textSize="@dimen/text_super_small" />
|
||||
|
||||
</RelativeLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
@@ -1,32 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright 2016 Hippo Seven
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_help"
|
||||
android:icon="@drawable/v_help_circle_x24"
|
||||
android:title="@string/add"
|
||||
app:showAsAction="always" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_add"
|
||||
android:icon="@drawable/v_plus_dark_x24"
|
||||
android:title="@string/add"
|
||||
app:showAsAction="always" />
|
||||
|
||||
</menu>
|
||||
@@ -23,6 +23,4 @@
|
||||
|
||||
<dimen name="gallery_list_margin_h">20dp</dimen>
|
||||
|
||||
<dimen name="search_layout_margin_h">20dp</dimen>
|
||||
|
||||
</resources>
|
||||
@@ -18,6 +18,4 @@
|
||||
|
||||
<dimen name="gallery_list_margin_h">76dp</dimen>
|
||||
|
||||
<dimen name="search_layout_margin_h">76dp</dimen>
|
||||
|
||||
</resources>
|
||||
@@ -27,7 +27,6 @@
|
||||
|
||||
<!-- Text size -->
|
||||
<dimen name="text_little_small">16sp</dimen>
|
||||
<dimen name="text_super_small">12sp</dimen>
|
||||
|
||||
<!-- RatingView -->
|
||||
<dimen name="rating_size">16dp</dimen>
|
||||
@@ -39,12 +38,9 @@
|
||||
|
||||
<dimen name="gallery_grid_interval">4dp</dimen>
|
||||
|
||||
<dimen name="gallery_search_bar_margin_v">10dp</dimen>
|
||||
|
||||
<dimen name="gallery_list_column_width_long">480dp</dimen>
|
||||
<dimen name="gallery_list_column_width_short">320dp</dimen>
|
||||
|
||||
<dimen name="search_layout_margin_h">4dp</dimen>
|
||||
<dimen name="search_layout_margin_v">4dp</dimen>
|
||||
|
||||
<!-- Search item -->
|
||||
|
||||
@@ -17,9 +17,7 @@
|
||||
<resources>
|
||||
|
||||
<string name="pd_pause" translatable="false">M14,19.14H18V5.14H14M6,19.14H10V5.14H6V19.14Z</string>
|
||||
<string name="pd_play" translatable="false">M8,5.14V19.14L19,12.14L8,5.14Z</string>
|
||||
<string name="pd_plus" translatable="false">M21,13H13V21H11V13H3V11H11V3H13V11H21V13Z</string>
|
||||
<string name="pd_help_circle" translatable="false">M15.07,11.25L14.17,12.17C13.45,12.89 13,13.5 13,15H11V14.5C11,13.39 11.45,12.39 12.17,11.67L13.41,10.41C13.78,10.05 14,9.55 14,9C14,7.89 13.1,7 12,7A2,2 0 0,0 10,9H8A4,4 0 0,1 12,5A4,4 0 0,1 16,9C16,9.88 15.64,10.67 15.07,11.25M13,19H11V17H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12C22,6.47 17.5,2 12,2Z</string>
|
||||
<string name="pd_download_box" translatable="false">M19,3H5C3.9,3,3,3.9,3,5V19C3,20.1,3.9,21,5,21H19C20.1,21,21,20.1,21,19V5C21,3.9,20.1,3,19,3ZM12,17L7,12H10V8H14V12H17L12,17Z</string>
|
||||
<string name="pd_refresh" translatable="false">M17.65,6.35C16.2,4.9 14.21,4 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20C15.73,20 18.84,17.45 19.73,14H17.65C16.83,16.33 14.61,18 12,18A6,6 0 0,1 6,12A6,6 0 0,1 12,6C13.66,6 15.14,6.69 16.22,7.78L13,11H20V4L17.65,6.35Z</string>
|
||||
<string name="pd_file_image" translatable="false">M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M6,20H15L18,20V12L14,16L12,14L6,20M8,9A2,2 0 0,0 6,11A2,2 0 0,0 8,13A2,2 0 0,0 10,11A2,2 0 0,0 8,9Z</string>
|
||||
|
||||
@@ -29,19 +29,6 @@
|
||||
<item name="android:minHeight">48dp</item>
|
||||
</style>
|
||||
|
||||
<style name="CardTitle">
|
||||
<item name="android:textAppearance">@style/TextAppearance.Material3.TitleSmall</item>
|
||||
<item name="android:ellipsize">end</item>
|
||||
<item name="android:maxLines">2</item>
|
||||
</style>
|
||||
|
||||
<style name="CardMessage">
|
||||
<item name="android:textAppearance">@style/TextAppearance.Material3.LabelLarge</item>
|
||||
<item name="android:ellipsize">end</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
<item name="android:maxLines">1</item>
|
||||
</style>
|
||||
|
||||
<style name="Snackbar" parent="Widget.Material3.Snackbar">
|
||||
<item name="materialThemeOverlay">@style/SnackbarTheme</item>
|
||||
<item name="actionTextColorAlpha">1</item>
|
||||
|
||||
Reference in new issue
Block a user