Support adding favorite notes

This commit is contained in:
FooIbar authored and tarsin committed 2023-07-04 21:20:24 +08:00
1 parent 325909401c
commit 4b66f58fd3
8 files changed
+92 -53

No files matched your search

@@ -59,7 +59,6 @@ object Settings : DataStorePreferences(null) {
var favLocalCount by intPref("fav_local", 0)
var favCloudCount by intPref("fav_cloud", 0)
var recentFavCat by intPref("recent_fav_cat", FavListUrlBuilder.FAV_CAT_ALL)
var defaultFavSlot by intPref("default_favorite_2", -2) // -1 for local, 0 - 9 for cloud favorite, other for no default fav slot
var securityDelay by intPref("require_unlock_delay", 0)
var clipboardTextHashCode by intPref("clipboard_text_hash_code", 0)
var listThumbSize by intPref("list_tile_size", 40)
@@ -239,7 +239,7 @@ object EhEngine {
gid: Long,
token: String?,
dstCat: Int = -1,
note: String? = null,
note: String = "",
) {
val catStr: String = when (dstCat) {
-1 -> "favdel"
@@ -250,7 +250,7 @@ object EhEngine {
return ehRequest(url, url, EhUrl.origin) {
formBody {
add("favcat", catStr)
add("favnote", note ?: "")
add("favnote", note)
// apply=Add+to+Favorites is not necessary, just use apply=Apply+Changes all the time
add("apply", "Apply Changes")
add("update", "1")
@@ -54,6 +54,7 @@ import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.util.lang.launchNow
import eu.kanade.tachiyomi.util.lang.withUIContext
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import moe.tarsin.coroutines.runSuspendCatching
@@ -65,6 +66,7 @@ object CommonOperations {
startDownload(activity!!, listOf(galleryInfo), forceDefault)
}
@OptIn(DelicateCoroutinesApi::class)
fun startDownload(
activity: MainActivity,
galleryInfos: List<GalleryInfo>,
@@ -130,7 +132,7 @@ object CommonOperations {
val list = DownloadManager.labelList
val items = mutableListOf<String>()
items.add(activity.getString(R.string.default_download_label_name))
items.addAll(list.mapNotNull { it.label })
items.addAll(list.map { it.label })
ListCheckBoxDialogBuilder(
activity,
items,
@@ -193,8 +195,8 @@ suspend fun keepNoMediaFileStatus() {
}
suspend fun DialogState.addToFavorites(galleryInfo: GalleryInfo): Boolean {
val localFaved = EhDB.containLocalFavorites(galleryInfo.gid)
val localFav = if (localFaved) {
val localFavorited = EhDB.containLocalFavorites(galleryInfo.gid)
val localFav = if (localFavorited) {
Icons.Default.Favorite
} else {
Icons.Default.FavoriteBorder
@@ -206,58 +208,58 @@ suspend fun DialogState.addToFavorites(galleryInfo: GalleryInfo): Boolean {
Icons.Default.FavoriteBorder
} to name
}.toTypedArray()
val slot = showSelectItemWithIcon(localFav, *items, title = R.string.add_favorites_dialog_title) - 1
val newFavoriteName = Settings.favCat.getOrNull(slot)
return doAddToFavorites(galleryInfo, slot, newFavoriteName, localFaved = localFaved)
val isFavorited = galleryInfo.favoriteSlot != NOT_FAVORITED
val (slot, note) = if (isFavorited) {
val remove = Icons.Default.HeartBroken to appCtx.getString(R.string.remove_from_favourites)
showSelectItemWithIconAndTextField(remove, localFav, *items, title = R.string.add_favorites_dialog_title, hint = R.string.favorite_note, maxChar = MAX_FAVNOTE_CHAR)
} else {
showSelectItemWithIconAndTextField(localFav, *items, title = R.string.add_favorites_dialog_title, hint = R.string.favorite_note, maxChar = MAX_FAVNOTE_CHAR)
}
return doAddToFavorites(galleryInfo, if (isFavorited) slot - 2 else slot - 1, note, localFavorited)
}
private suspend fun doAddToFavorites(
galleryInfo: GalleryInfo,
slot: Int = NOT_FAVORITED,
newFavoriteName: String? = null,
note: String? = null,
localFaved: Boolean = true,
note: String = "",
localFavorited: Boolean = true,
): Boolean {
val add = when (slot) {
NOT_FAVORITED -> {
EhDB.removeLocalFavorites(galleryInfo.gid)
NOT_FAVORITED -> { // Remove from cloud favorites first
if (galleryInfo.favoriteSlot > LOCAL_FAVORITED) {
EhEngine.addFavorites(galleryInfo.gid, galleryInfo.token)
} else if (localFavorited) {
EhDB.removeLocalFavorites(galleryInfo.gid)
}
false
}
LOCAL_FAVORITED -> {
if (localFaved) {
if (localFavorited) {
EhDB.removeLocalFavorites(galleryInfo.gid)
} else {
EhDB.putLocalFavorites(galleryInfo)
}
!localFaved
!localFavorited
}
in 0..9 -> {
if (galleryInfo.favoriteSlot == slot) {
EhEngine.addFavorites(galleryInfo.gid, galleryInfo.token)
false
} else {
EhEngine.addFavorites(galleryInfo.gid, galleryInfo.token, slot, note)
true
}
EhEngine.addFavorites(galleryInfo.gid, galleryInfo.token, slot, note)
true
}
else -> throw EhException("Invalid favorite slot!")
}
if (add) {
if (slot != LOCAL_FAVORITED || galleryInfo.favoriteSlot == NOT_FAVORITED) { // Cloud favorites have priority
if (add) { // Cloud favorites have priority
if (slot != LOCAL_FAVORITED || galleryInfo.favoriteSlot == NOT_FAVORITED) {
galleryInfo.favoriteSlot = slot
galleryInfo.favoriteName = newFavoriteName
galleryInfo.favoriteName = Settings.favCat.getOrNull(slot)
withUIContext {
FavouriteStatusRouter.modifyFavourites(galleryInfo.gid, slot)
}
}
} else if (slot != LOCAL_FAVORITED || galleryInfo.favoriteSlot == LOCAL_FAVORITED) {
val newSlot = if (slot > LOCAL_FAVORITED && localFaved) LOCAL_FAVORITED else NOT_FAVORITED
val newSlot = if (galleryInfo.favoriteSlot > LOCAL_FAVORITED && localFavorited) LOCAL_FAVORITED else NOT_FAVORITED
galleryInfo.favoriteSlot = newSlot
galleryInfo.favoriteName = null
withUIContext {
@@ -329,6 +331,8 @@ suspend fun DialogState.doGalleryInfoAction(info: GalleryInfo, context: Context)
}
}
private const val MAX_FAVNOTE_CHAR = 200
suspend fun DialogState.confirmRemoveDownload(info: GalleryInfo): Boolean = show(
confirmText = android.R.string.ok,
dismissText = android.R.string.cancel,
@@ -338,7 +342,7 @@ suspend fun DialogState.confirmRemoveDownload(info: GalleryInfo): Boolean = show
suspend fun DialogState.showMoveDownloadLabel(info: GalleryInfo) {
val defaultLabel = appCtx.getString(R.string.default_download_label_name)
val labels = DownloadManager.labelList.mapNotNull { it.label }.toTypedArray()
val labels = DownloadManager.labelList.map { it.label }.toTypedArray()
val selected = showSelectItem(defaultLabel, *labels, title = R.string.download_move_dialog_title)
val downloadInfo = DownloadManager.getDownloadInfo(info.gid) ?: return
val label = if (selected == 0) null else labels[selected - 1]
@@ -21,7 +21,6 @@ import android.content.DialogInterface
import android.content.res.Resources
import android.os.Bundle
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.core.view.GravityCompat
@@ -322,25 +321,6 @@ class FavoritesScene :
): View {
val drawerBinding = DrawerListRvBinding.inflate(inflater, container, false)
drawerBinding.toolbar.setTitle(R.string.collections)
drawerBinding.toolbar.inflateMenu(R.menu.drawer_favorites)
drawerBinding.toolbar.setOnMenuItemClickListener { item: MenuItem ->
val id = item.itemId
if (id == R.id.action_default_favorites_slot) {
val items = arrayOfNulls<String>(12)
items[0] = getString(R.string.let_me_select)
items[1] = getString(R.string.local_favorites)
val favCat = Settings.favCat
System.arraycopy(favCat, 0, items, 2, 10)
BaseDialogBuilder(requireContext())
.setTitle(R.string.default_favorites_collection)
.setItems(items) { _, which ->
Settings.defaultFavSlot = which - 2
}
.show()
return@setOnMenuItemClickListener true
}
false
}
drawerBinding.recyclerViewDrawer.layoutManager = LinearLayoutManager(context)
mDrawerAdapter = FavDrawerAdapter(inflater)
drawerBinding.recyclerViewDrawer.adapter = mDrawerAdapter
@@ -16,6 +16,7 @@ private const val QuarterCircle = PI / 2
fun CircularLayout(
modifier: Modifier = Modifier,
radius: Dp,
placeFirstItemInCenter: Boolean = false,
content: @Composable () -> Unit,
) {
Layout(
@@ -25,13 +26,24 @@ fun CircularLayout(
val radiusPx = radius.toPx()
val itemConstraints = constraints.copy(minWidth = 0, minHeight = 0)
val placeables = measurables.map { measurable -> measurable.measure(itemConstraints) }
val theta = FullCircle / (placeables.count())
val outPlaceables = if (placeFirstItemInCenter) placeables.drop(1) else placeables
val theta = FullCircle / (outPlaceables.count())
layout(
width = constraints.minWidth,
height = constraints.minHeight,
) {
placeables.forEachIndexed { i, it ->
if (placeFirstItemInCenter) {
placeables[0].let {
val centerOffsetX = constraints.maxWidth / 2 - it.width / 2
val centerOffsetY = constraints.maxHeight / 2 - it.height / 2
it.place(
x = centerOffsetX,
y = centerOffsetY,
)
}
}
outPlaceables.forEachIndexed { i, it ->
val centerOffsetX = constraints.maxWidth / 2 - it.width / 2
val centerOffsetY = constraints.maxHeight / 2 - it.height / 2
val offsetX = radiusPx * cos(theta * i - QuarterCircle) + centerOffsetX
@@ -1,5 +1,6 @@
package com.hippo.ehviewer.ui.tools
import android.graphics.Typeface
import androidx.annotation.StringRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
@@ -13,15 +14,21 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.AlertDialogDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ShapeDefaults
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
@@ -31,8 +38,12 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
@@ -186,19 +197,50 @@ class DialogState {
}
}
suspend fun showSelectItemWithIcon(
suspend fun showSelectItemWithIconAndTextField(
vararg items: Pair<ImageVector, String>,
@StringRes title: Int,
): Int = showNoButton(false) {
@StringRes hint: Int,
maxChar: Int,
): Pair<Int, String> = showNoButton(false) {
Column {
Text(text = stringResource(id = title), modifier = Modifier.padding(horizontal = 16.dp).padding(top = 16.dp), style = MaterialTheme.typography.titleMedium)
CircularLayout(
radius = 112.dp,
modifier = Modifier.fillMaxWidth().aspectRatio(1F),
placeFirstItemInCenter = true,
) {
var note by remember { mutableStateOf("") }
TextField(
value = note,
onValueChange = { note = it },
modifier = Modifier.width(128.dp),
textStyle = TextStyle(fontFamily = FontFamily(Typeface.MONOSPACE)),
label = { Text(text = stringResource(id = hint)) },
trailingIcon = {
if (note.isNotEmpty()) {
IconButton(onClick = { note = "" }) {
Icon(imageVector = Icons.Default.Close, contentDescription = null)
}
}
},
supportingText = {
Text(
text = "${note.length} / $maxChar",
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.End,
)
},
maxLines = 6,
shape = ShapeDefaults.ExtraSmall,
colors = TextFieldDefaults.colors(
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
),
)
items.forEachIndexed { index, (icon, text) ->
Column(
modifier = Modifier.clip(IconWithTextCorner).clickable { dismissWith(index) },
modifier = Modifier.clip(IconWithTextCorner).clickable { dismissWith(index to note) },
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(imageVector = icon, contentDescription = null, tint = AlertDialogDefaults.iconContentColor)
@@ -414,6 +414,7 @@
<string name="move_favorites_dialog_title">移动收藏</string>
<string name="default_favorites_collection">默认收藏夹</string>
<string name="let_me_select">让我选择</string>
<string name="favorite_note">备注</string>
<string name="collections">收藏夹</string>
<string name="need_sign_in">需要登录</string>
<!-- Progress Scene -->
+1
View File
@@ -472,6 +472,7 @@
<string name="move_favorites_dialog_title">Move favorites</string>
<string name="default_favorites_collection">Default favorites collection</string>
<string name="let_me_select">Let me select</string>
<string name="favorite_note">Note</string>
<string name="collections">Collections</string>
<string name="need_sign_in">Signing in is required</string>
<!-- Progress Scene -->