Support grid view in DownloadsScreen (#714)
* Support grid view in DownloadsScreen * Fix aspect * Refine
This commit is contained in:
1 parent
7ef70aa16e
commit
51c0efebc4
4 files changed
+133
-78
No files matched your search
@@ -151,6 +151,7 @@ object Settings : DataStorePreferences(null) {
|
||||
var downloadOriginImage by boolPref("download_origin_image", false)
|
||||
var archiveMetadata by boolPref("archive_metadata", true)
|
||||
var enableCronet by boolPref("enable_cronet", true)
|
||||
var gridView by boolPref("grid_view", false)
|
||||
var recentFavCat by intPref("recent_fav_cat", FavListUrlBuilder.FAV_CAT_LOCAL)
|
||||
var defaultFavSlot by intPref("default_favorite_slot", -2)
|
||||
var securityDelay by intPref("require_unlock_delay", 0)
|
||||
|
||||
@@ -7,6 +7,7 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import coil3.compose.AsyncImage
|
||||
@@ -26,11 +27,13 @@ fun requestOf(model: GalleryInfo): ImageRequest {
|
||||
fun EhAsyncThumb(
|
||||
model: GalleryInfo,
|
||||
modifier: Modifier = Modifier,
|
||||
placeholder: Painter? = null,
|
||||
contentScale: ContentScale = ContentScale.Fit,
|
||||
) = AsyncImage(
|
||||
model = requestOf(model),
|
||||
contentDescription = "",
|
||||
modifier = modifier,
|
||||
placeholder = placeholder,
|
||||
contentScale = contentScale,
|
||||
)
|
||||
|
||||
|
||||
@@ -24,11 +24,15 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.contentColorFor
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
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.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.painter.BrushPainter
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.layout.layoutId
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
@@ -191,11 +195,6 @@ fun GalleryInfoGridItem(
|
||||
info: GalleryInfo,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val aspect = if (info.thumbHeight != 0) {
|
||||
(info.thumbWidth.toFloat() / info.thumbHeight).coerceIn(MIN_ASPECT, MAX_ASPECT)
|
||||
} else {
|
||||
DEFAULT_ASPECT
|
||||
}
|
||||
val simpleLang = info.simpleLanguage
|
||||
ElevatedCard(
|
||||
modifier = modifier,
|
||||
@@ -203,9 +202,18 @@ fun GalleryInfoGridItem(
|
||||
onLongClick = onLongClick,
|
||||
) {
|
||||
Box {
|
||||
val placeholder = remember {
|
||||
val aspect = if (info.thumbHeight != 0) {
|
||||
(info.thumbWidth.toFloat() / info.thumbHeight).coerceIn(MIN_ASPECT, MAX_ASPECT)
|
||||
} else {
|
||||
DEFAULT_ASPECT
|
||||
}
|
||||
BrushPainter(Brush.linearGradient(PlaceholderColors, end = Offset(aspect, 1f)))
|
||||
}
|
||||
EhAsyncThumb(
|
||||
model = info,
|
||||
modifier = Modifier.aspectRatio(aspect).fillMaxWidth(),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
placeholder = placeholder,
|
||||
contentScale = ContentScale.Crop,
|
||||
)
|
||||
val categoryColor = EhUtils.getCategoryColor(info.category)
|
||||
@@ -222,6 +230,8 @@ fun GalleryInfoGridItem(
|
||||
}
|
||||
}
|
||||
|
||||
private val PlaceholderColors = listOf(Color.Transparent, Color.Transparent)
|
||||
|
||||
private const val MIN_ASPECT = 0.33F
|
||||
private const val MAX_ASPECT = 1.5F
|
||||
const val DEFAULT_ASPECT = 0.67F
|
||||
@@ -20,13 +20,16 @@ import androidx.compose.foundation.layout.systemBars
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.items
|
||||
import androidx.compose.foundation.text.input.rememberTextFieldState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.DriveFileMove
|
||||
import androidx.compose.material.icons.automirrored.outlined.Label
|
||||
import androidx.compose.material.icons.automirrored.filled.ViewList
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.DoneAll
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material.icons.filled.GridView
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.icons.filled.NewLabel
|
||||
import androidx.compose.material.icons.filled.Pause
|
||||
@@ -68,9 +71,11 @@ import androidx.compose.ui.res.stringArrayResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.ContextCompat
|
||||
import arrow.core.partially1
|
||||
import com.hippo.ehviewer.EhDB
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.collectAsState
|
||||
import com.hippo.ehviewer.dao.DownloadInfo
|
||||
import com.hippo.ehviewer.download.DownloadManager
|
||||
import com.hippo.ehviewer.download.DownloadManager.labelList
|
||||
@@ -83,11 +88,13 @@ import com.hippo.ehviewer.ui.MainActivity
|
||||
import com.hippo.ehviewer.ui.confirmRemoveDownloadRange
|
||||
import com.hippo.ehviewer.ui.main.DownloadCard
|
||||
import com.hippo.ehviewer.ui.main.FabLayout
|
||||
import com.hippo.ehviewer.ui.main.GalleryInfoGridItem
|
||||
import com.hippo.ehviewer.ui.navToReader
|
||||
import com.hippo.ehviewer.ui.showMoveDownloadLabelList
|
||||
import com.hippo.ehviewer.ui.tools.Deferred
|
||||
import com.hippo.ehviewer.ui.tools.DragHandle
|
||||
import com.hippo.ehviewer.ui.tools.FastScrollLazyColumn
|
||||
import com.hippo.ehviewer.ui.tools.FastScrollLazyVerticalStaggeredGrid
|
||||
import com.hippo.ehviewer.ui.tools.LocalDialogState
|
||||
import com.hippo.ehviewer.ui.tools.SwipeToDismissBox2
|
||||
import com.hippo.ehviewer.ui.tools.draggingHapticFeedback
|
||||
@@ -111,6 +118,7 @@ import sh.calvin.reorderable.rememberReorderableLazyColumnState
|
||||
@Composable
|
||||
fun DownloadsScreen(navigator: DestinationsNavigator) {
|
||||
var label by Settings::recentDownloadLabel.observed
|
||||
var gridView by Settings::gridView.observed
|
||||
var keyword by rememberSaveable { mutableStateOf<String?>(null) }
|
||||
var filterType by rememberSaveable { mutableIntStateOf(-1) }
|
||||
var searchBarOffsetY by remember(label) { mutableIntStateOf(0) }
|
||||
@@ -333,14 +341,24 @@ fun DownloadsScreen(navigator: DestinationsNavigator) {
|
||||
trailingIcon = {
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
val sideSheetState = LocalSideSheetState.current
|
||||
IconButton(onClick = { coroutineScope.launch { sideSheetState.open() } }) {
|
||||
Icon(imageVector = Icons.AutoMirrored.Outlined.Label, contentDescription = stringResource(id = R.string.download_labels))
|
||||
IconButton(onClick = { gridView = !gridView }) {
|
||||
val icon = if (gridView) Icons.AutoMirrored.Default.ViewList else Icons.Default.GridView
|
||||
Icon(imageVector = icon, contentDescription = null)
|
||||
}
|
||||
IconButton(onClick = { expanded = !expanded }) {
|
||||
Icon(imageVector = Icons.Default.MoreVert, contentDescription = null)
|
||||
}
|
||||
val states = stringArrayResource(id = R.array.download_state)
|
||||
DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = stringResource(id = R.string.download_labels)) },
|
||||
onClick = {
|
||||
expanded = false
|
||||
coroutineScope.launch {
|
||||
sideSheetState.open()
|
||||
}
|
||||
},
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = stringResource(id = R.string.download_filter)) },
|
||||
onClick = {
|
||||
@@ -409,13 +427,6 @@ fun DownloadsScreen(navigator: DestinationsNavigator) {
|
||||
start = contentPadding.calculateStartPadding(layoutDirection) + marginH,
|
||||
end = contentPadding.calculateEndPadding(layoutDirection) + marginH,
|
||||
)
|
||||
val listState = rememberLazyListState()
|
||||
val reorderableState = rememberReorderableLazyColumnState(listState) { from, to ->
|
||||
val fromIndex = from.index - 1
|
||||
val toIndex = to.index - 1
|
||||
list.apply { add(toIndex, removeAt(fromIndex)) }
|
||||
view.performHapticFeedback(draggingHapticFeedback)
|
||||
}
|
||||
val searchBarConnection = remember {
|
||||
val topPaddingPx = with(density) { contentPadding.calculateTopPadding().roundToPx() }
|
||||
object : NestedScrollConnection {
|
||||
@@ -425,70 +436,100 @@ fun DownloadsScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
}
|
||||
}
|
||||
var fromIndex by remember { mutableIntStateOf(-1) }
|
||||
FastScrollLazyColumn(
|
||||
modifier = Modifier.nestedScroll(searchBarConnection).fillMaxSize(),
|
||||
state = listState,
|
||||
contentPadding = realPadding,
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.gallery_list_interval)),
|
||||
) {
|
||||
// Fix the first item's reorder animation
|
||||
item {}
|
||||
itemsIndexed(list, key = { _, item -> item.gid }) { index, info ->
|
||||
ReorderableItem(reorderableState, key = info.gid) {
|
||||
val checked = info.gid in checkedInfoMap
|
||||
CheckableItem(checked = checked) {
|
||||
DownloadCard(
|
||||
onClick = {
|
||||
if (selectMode) {
|
||||
if (checked) {
|
||||
checkedInfoMap.remove(info.gid)
|
||||
} else {
|
||||
checkedInfoMap[info.gid] = info
|
||||
}
|
||||
} else {
|
||||
coroutineScope.launchIO {
|
||||
EhDB.putHistoryInfo(info.galleryInfo)
|
||||
}
|
||||
context.navToReader(info.galleryInfo)
|
||||
}
|
||||
},
|
||||
onThumbClick = {
|
||||
navigator.navigate(info.galleryInfo.asDst())
|
||||
},
|
||||
onLongClick = {
|
||||
checkedInfoMap[info.gid] = info
|
||||
},
|
||||
onStart = {
|
||||
val intent = Intent(activity, DownloadService::class.java)
|
||||
intent.action = DownloadService.ACTION_START
|
||||
intent.putExtra(DownloadService.KEY_GALLERY_INFO, info.galleryInfo)
|
||||
ContextCompat.startForegroundService(activity, intent)
|
||||
},
|
||||
onStop = {
|
||||
coroutineScope.launchIO {
|
||||
DownloadManager.stopDownload(info.gid)
|
||||
}
|
||||
},
|
||||
onDragStarted = {
|
||||
fromIndex = index
|
||||
},
|
||||
onDragStopped = {
|
||||
if (fromIndex != -1) {
|
||||
val direction = (index - fromIndex).sign
|
||||
if (direction != 0) {
|
||||
val toItem = list[index - direction]
|
||||
coroutineScope.launchIO {
|
||||
val toUpdate = DownloadManager.moveDownload(info, toItem)
|
||||
EhDB.updateDownloadInfo(toUpdate)
|
||||
fun onItemClick(info: DownloadInfo) {
|
||||
coroutineScope.launchIO {
|
||||
EhDB.putHistoryInfo(info.galleryInfo)
|
||||
}
|
||||
context.navToReader(info.galleryInfo)
|
||||
}
|
||||
if (gridView) {
|
||||
val gridInterval = dimensionResource(R.dimen.gallery_grid_interval)
|
||||
val thumbColumns by Settings.thumbColumns.collectAsState()
|
||||
FastScrollLazyVerticalStaggeredGrid(
|
||||
columns = StaggeredGridCells.Fixed(thumbColumns),
|
||||
modifier = Modifier.nestedScroll(searchBarConnection).fillMaxSize(),
|
||||
verticalItemSpacing = gridInterval,
|
||||
horizontalArrangement = Arrangement.spacedBy(gridInterval),
|
||||
contentPadding = realPadding,
|
||||
) {
|
||||
items(list) {
|
||||
GalleryInfoGridItem(
|
||||
onClick = ::onItemClick.partially1(it),
|
||||
onLongClick = { navigator.navigate(it.galleryInfo.asDst()) },
|
||||
info = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val listState = rememberLazyListState()
|
||||
val reorderableState = rememberReorderableLazyColumnState(listState) { from, to ->
|
||||
val fromIndex = from.index - 1
|
||||
val toIndex = to.index - 1
|
||||
list.apply { add(toIndex, removeAt(fromIndex)) }
|
||||
view.performHapticFeedback(draggingHapticFeedback)
|
||||
}
|
||||
var fromIndex by remember { mutableIntStateOf(-1) }
|
||||
FastScrollLazyColumn(
|
||||
modifier = Modifier.nestedScroll(searchBarConnection).fillMaxSize(),
|
||||
state = listState,
|
||||
contentPadding = realPadding,
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.gallery_list_interval)),
|
||||
) {
|
||||
// Fix the first item's reorder animation
|
||||
item {}
|
||||
itemsIndexed(list, key = { _, item -> item.gid }) { index, info ->
|
||||
ReorderableItem(reorderableState, key = info.gid) {
|
||||
val checked = info.gid in checkedInfoMap
|
||||
CheckableItem(checked = checked) {
|
||||
DownloadCard(
|
||||
onClick = {
|
||||
if (selectMode) {
|
||||
if (checked) {
|
||||
checkedInfoMap.remove(info.gid)
|
||||
} else {
|
||||
checkedInfoMap[info.gid] = info
|
||||
}
|
||||
} else {
|
||||
onItemClick(info)
|
||||
}
|
||||
fromIndex = -1
|
||||
}
|
||||
},
|
||||
info = info,
|
||||
modifier = Modifier.height(height),
|
||||
)
|
||||
},
|
||||
onThumbClick = {
|
||||
navigator.navigate(info.galleryInfo.asDst())
|
||||
},
|
||||
onLongClick = {
|
||||
checkedInfoMap[info.gid] = info
|
||||
},
|
||||
onStart = {
|
||||
val intent = Intent(activity, DownloadService::class.java)
|
||||
intent.action = DownloadService.ACTION_START
|
||||
intent.putExtra(DownloadService.KEY_GALLERY_INFO, info.galleryInfo)
|
||||
ContextCompat.startForegroundService(activity, intent)
|
||||
},
|
||||
onStop = {
|
||||
coroutineScope.launchIO {
|
||||
DownloadManager.stopDownload(info.gid)
|
||||
}
|
||||
},
|
||||
onDragStarted = {
|
||||
fromIndex = index
|
||||
},
|
||||
onDragStopped = {
|
||||
if (fromIndex != -1) {
|
||||
val direction = (index - fromIndex).sign
|
||||
if (direction != 0) {
|
||||
val toItem = list[index - direction]
|
||||
coroutineScope.launchIO {
|
||||
val toUpdate = DownloadManager.moveDownload(info, toItem)
|
||||
EhDB.updateDownloadInfo(toUpdate)
|
||||
}
|
||||
}
|
||||
fromIndex = -1
|
||||
}
|
||||
},
|
||||
info = info,
|
||||
modifier = Modifier.height(height),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user