Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75bec4314e | ||
|
|
b767c041b9 |
No files matched your search
@@ -64,7 +64,7 @@ android {
|
||||
minSdk = 28
|
||||
targetSdk = 34
|
||||
versionCode = 180043
|
||||
versionName = "1.8.9.2"
|
||||
versionName = "1.8.9.0-gms"
|
||||
resourceConfigurations.addAll(
|
||||
listOf(
|
||||
"zh",
|
||||
@@ -216,7 +216,7 @@ dependencies {
|
||||
val accompanist_version = "0.31.6-rc"
|
||||
implementation("com.google.accompanist:accompanist-themeadapter-material3:$accompanist_version")
|
||||
implementation("com.google.accompanist:accompanist-webview:$accompanist_version")
|
||||
implementation("com.google.android.material:material:1.11.0-alpha02")
|
||||
implementation("com.google.android.material:material:1.11.0-alpha01")
|
||||
|
||||
val splitties = "3.0.0"
|
||||
implementation("com.louiscad.splitties:splitties-appctx:$splitties")
|
||||
@@ -250,7 +250,8 @@ dependencies {
|
||||
implementation("io.ktor:ktor-io-jvm:$ktor")
|
||||
implementation("io.ktor:ktor-utils-jvm:$ktor")
|
||||
|
||||
implementation("org.chromium.net:cronet-embedded:113.5672.61")
|
||||
implementation("org.chromium.net:cronet-api:113.5672.61")
|
||||
implementation("com.google.android.gms:play-services-cronet:18.0.1")
|
||||
|
||||
val serialization = "1.5.1"
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-cbor:$serialization")
|
||||
|
||||
@@ -11,6 +11,7 @@ import androidx.navigation.NavOptions
|
||||
|
||||
@SuppressLint("PrivateResource")
|
||||
fun onNavDestinationSelected2(item: MenuItem, navController: NavController): Boolean {
|
||||
if (navController.currentDestination?.matchDestination(item.itemId) == true) return true
|
||||
val builder = NavOptions.Builder().setLaunchSingleTop(true)
|
||||
if (
|
||||
navController.currentDestination!!.parent!!.findNode(item.itemId)
|
||||
|
||||
@@ -25,6 +25,7 @@ import androidx.lifecycle.coroutineScope
|
||||
import coil.ImageLoaderFactory
|
||||
import coil.decode.ImageDecoderDecoder
|
||||
import coil.util.DebugLogger
|
||||
import com.google.android.gms.net.CronetProviderInstaller
|
||||
import com.hippo.ehviewer.client.EhCookieStore
|
||||
import com.hippo.ehviewer.client.EhTagDatabase
|
||||
import com.hippo.ehviewer.client.data.GalleryDetail
|
||||
@@ -77,6 +78,7 @@ class EhApplication : Application(), ImageLoaderFactory {
|
||||
handler?.uncaughtException(t, e)
|
||||
}
|
||||
super.onCreate()
|
||||
CronetProviderInstaller.installProvider(appCtx)
|
||||
System.loadLibrary("ehviewer")
|
||||
System.loadLibrary("ehviewer_rust")
|
||||
ReadableTime.initialize(this)
|
||||
|
||||
@@ -70,8 +70,8 @@ data class ListUrlBuilder(
|
||||
mPrev = index?.takeUnless { isNext }
|
||||
}
|
||||
|
||||
fun setJumpTo(jumpTo: Int) {
|
||||
mJumpTo = jumpTo.takeUnless { it == 0 }?.toString()
|
||||
fun setJumpTo(jumpTo: String?) {
|
||||
mJumpTo = jumpTo
|
||||
}
|
||||
|
||||
var keyword: String?
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import com.hippo.ehviewer.EhDB
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.client.data.BaseGalleryInfo
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo.Companion.LOCAL_FAVORITED
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo.Companion.NOT_FAVORITED
|
||||
import com.hippo.ehviewer.client.exception.EhException
|
||||
import com.hippo.ehviewer.client.exception.ParseException
|
||||
|
||||
object GalleryListParser {
|
||||
suspend fun parse(body: String) = runCatching {
|
||||
@@ -18,21 +15,13 @@ object GalleryListParser {
|
||||
it.generateSLang()
|
||||
}
|
||||
}
|
||||
}.getOrElse {
|
||||
if (body.contains("<p>You do not have any watched tags")) {
|
||||
throw EhException(R.string.gallery_list_empty_hit_subscription)
|
||||
}
|
||||
if (body.contains("No hits found</p>")) {
|
||||
throw EhException(R.string.gallery_list_empty_hit)
|
||||
}
|
||||
throw ParseException("Can't parse gallery list", body, it)
|
||||
}
|
||||
}.getOrDefault(GalleryListResult())
|
||||
}
|
||||
|
||||
class GalleryListResult(
|
||||
val prev: String?,
|
||||
val next: String?,
|
||||
val galleryInfoList: ArrayList<BaseGalleryInfo>,
|
||||
val prev: String? = null,
|
||||
val next: String? = null,
|
||||
val galleryInfoList: ArrayList<BaseGalleryInfo> = arrayListOf(),
|
||||
)
|
||||
|
||||
private external fun parseGalleryInfoList(e: String): GalleryListResult
|
||||
@@ -20,8 +20,6 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Download
|
||||
@@ -30,12 +28,7 @@ import androidx.compose.material.icons.filled.Favorite
|
||||
import androidx.compose.material.icons.filled.FavoriteBorder
|
||||
import androidx.compose.material.icons.filled.HeartBroken
|
||||
import androidx.compose.material.icons.filled.MenuBook
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.hippo.ehviewer.EhDB
|
||||
@@ -62,7 +55,6 @@ import com.hippo.ehviewer.util.requestPermission
|
||||
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.withIOContext
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
@@ -320,6 +312,7 @@ suspend fun DialogState.doGalleryInfoAction(info: BaseGalleryInfo, context: Cont
|
||||
1 -> withUIContext {
|
||||
if (downloaded) {
|
||||
confirmRemoveDownload(info)
|
||||
DownloadManager.deleteDownload(info.gid)
|
||||
} else {
|
||||
CommonOperations.startDownload(this@with, info, false)
|
||||
}
|
||||
@@ -349,28 +342,12 @@ suspend fun DialogState.doGalleryInfoAction(info: BaseGalleryInfo, context: Cont
|
||||
|
||||
private const val MAX_FAVNOTE_CHAR = 200
|
||||
|
||||
suspend fun DialogState.confirmRemoveDownload(info: GalleryInfo, onDismiss: () -> Unit = {}) {
|
||||
var checked by mutableStateOf(Settings.removeImageFiles)
|
||||
awaitPermissionOrCancel(
|
||||
confirmText = android.R.string.ok,
|
||||
dismissText = android.R.string.cancel,
|
||||
title = R.string.download_remove_dialog_title,
|
||||
onDismiss = onDismiss,
|
||||
text = {
|
||||
Column {
|
||||
Text(text = stringResource(id = R.string.download_remove_dialog_message, info.title.orEmpty()))
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Checkbox(checked = checked, onCheckedChange = { checked = it })
|
||||
Text(text = stringResource(id = R.string.download_remove_dialog_check_text))
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
Settings.removeImageFiles = checked
|
||||
withIOContext {
|
||||
DownloadManager.deleteDownload(info.gid, checked)
|
||||
}
|
||||
}
|
||||
suspend fun DialogState.confirmRemoveDownload(info: GalleryInfo) = awaitPermissionOrCancel(
|
||||
confirmText = android.R.string.ok,
|
||||
dismissText = android.R.string.cancel,
|
||||
title = R.string.download_remove_dialog_title,
|
||||
text = { Text(text = stringResource(id = R.string.download_remove_dialog_message, info.title.orEmpty())) },
|
||||
)
|
||||
|
||||
suspend fun DialogState.showMoveDownloadLabel(info: GalleryInfo) {
|
||||
val defaultLabel = appCtx.getString(R.string.default_download_label_name)
|
||||
|
||||
@@ -197,7 +197,7 @@ class MainActivity : EhActivity() {
|
||||
// Trick: Tweak NavigationUI to disable multiple backstack
|
||||
binding.navView.setNavigationItemSelectedListener {
|
||||
val navigationView = binding.navView
|
||||
val handled = navigationView.checkedItem?.itemId == it.itemId || onNavDestinationSelected2(it, navController)
|
||||
val handled = onNavDestinationSelected2(it, navController)
|
||||
if (handled) {
|
||||
val parent = navigationView.parent
|
||||
if (parent is Openable) {
|
||||
@@ -398,10 +398,6 @@ class MainActivity : EhActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
fun clearNavCheckedItem() {
|
||||
binding.navView.setCheckedItem(R.id.nav_stub)
|
||||
}
|
||||
|
||||
fun showTip(@StringRes id: Int, length: Int, useToast: Boolean = false) {
|
||||
showTip(getString(id), length, useToast)
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -59,6 +58,7 @@ import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_DRAG
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
||||
import arrow.core.partially1
|
||||
import coil.compose.AsyncImage
|
||||
@@ -84,7 +84,6 @@ 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.legacy.AutoStaggeredGridLayoutManager
|
||||
import com.hippo.ehviewer.ui.legacy.BaseDialogBuilder
|
||||
import com.hippo.ehviewer.ui.legacy.CheckBoxDialogBuilder
|
||||
@@ -99,7 +98,6 @@ import com.hippo.ehviewer.ui.main.requestOf
|
||||
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.LongList
|
||||
import com.hippo.ehviewer.util.sendTo
|
||||
@@ -139,8 +137,6 @@ class DownloadsScene :
|
||||
private lateinit var mLabels: MutableList<String>
|
||||
private var mType = -1
|
||||
|
||||
private val dialogState = DialogState()
|
||||
|
||||
private fun initLabels() {
|
||||
context ?: return
|
||||
val listLabel = downloadManager.labelList
|
||||
@@ -251,7 +247,6 @@ class DownloadsScene :
|
||||
savedInstanceState: Bundle?,
|
||||
): View {
|
||||
_binding = SceneDownloadBinding.inflate(inflater, container!!)
|
||||
container.addView(ComposeView(inflater.context).apply { setMD3Content { dialogState.Intercept() } })
|
||||
binding.run {
|
||||
setLiftOnScrollTargetView(recyclerView)
|
||||
mViewTransition = ViewTransition(content, tip)
|
||||
@@ -293,6 +288,11 @@ class DownloadsScene :
|
||||
recyclerView.layoutManager = layoutManager
|
||||
recyclerView.clipToPadding = false
|
||||
recyclerView.clipChildren = false
|
||||
// Cancel change animation
|
||||
val itemAnimator = recyclerView.itemAnimator
|
||||
if (itemAnimator is SimpleItemAnimator) {
|
||||
itemAnimator.supportsChangeAnimations = false
|
||||
}
|
||||
val interval = resources.getDimensionPixelOffset(R.dimen.gallery_list_interval)
|
||||
val decoration = object : RecyclerView.ItemDecoration() {
|
||||
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
|
||||
@@ -313,7 +313,7 @@ class DownloadsScene :
|
||||
}
|
||||
override fun isLongPressDragEnabled() = false
|
||||
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
|
||||
return makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN, ItemTouchHelper.LEFT)
|
||||
return makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0)
|
||||
}
|
||||
override fun onMove(
|
||||
recyclerView: RecyclerView,
|
||||
@@ -336,16 +336,7 @@ class DownloadsScene :
|
||||
}
|
||||
return true
|
||||
}
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||
val position = viewHolder.bindingAdapterPosition
|
||||
val info = mList!![position]
|
||||
lifecycleScope.launchUI {
|
||||
dialogState.confirmRemoveDownload(info) {
|
||||
mAdapter!!.notifyItemChanged(position)
|
||||
}
|
||||
mAdapter!!.notifyItemRemoved(position)
|
||||
}
|
||||
}
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {}
|
||||
})
|
||||
mItemTouchHelper!!.attachToRecyclerView(recyclerView)
|
||||
fastScroller.attachToRecyclerView(recyclerView)
|
||||
|
||||
@@ -51,8 +51,6 @@ import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import androidx.paging.cachedIn
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.datepicker.CalendarConstraints
|
||||
import com.google.android.material.datepicker.CalendarConstraints.DateValidator
|
||||
import com.google.android.material.datepicker.CompositeDateValidator
|
||||
@@ -80,7 +78,6 @@ import com.hippo.ehviewer.ui.legacy.HandlerDrawable
|
||||
import com.hippo.ehviewer.ui.legacy.ViewTransition
|
||||
import com.hippo.ehviewer.ui.legacy.WindowInsetsAnimationHelper
|
||||
import com.hippo.ehviewer.ui.setMD3Content
|
||||
import com.hippo.ehviewer.ui.tools.DialogState
|
||||
import com.hippo.ehviewer.util.ExceptionUtils
|
||||
import com.hippo.ehviewer.util.SimpleHandler
|
||||
import com.hippo.ehviewer.util.getValue
|
||||
@@ -91,7 +88,6 @@ import eu.kanade.tachiyomi.util.lang.launchUI
|
||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import moe.tarsin.coroutines.runSuspendCatching
|
||||
import rikka.core.res.resolveColor
|
||||
@@ -164,10 +160,6 @@ class FavoritesScene : SearchBarScene() {
|
||||
}
|
||||
}
|
||||
|
||||
private val dialogState = DialogState()
|
||||
|
||||
override val fabLayout get() = binding.fabLayout
|
||||
|
||||
private fun onItemClick(position: Int) {
|
||||
// Skip if in search mode
|
||||
if (!tracker.isInCustomChoice) {
|
||||
@@ -199,7 +191,6 @@ class FavoritesScene : SearchBarScene() {
|
||||
}
|
||||
if (keyword.isNullOrEmpty()) {
|
||||
setSearchBarHint(getString(R.string.favorites_title, favCatName))
|
||||
setSearchBarText(null)
|
||||
} else {
|
||||
setSearchBarHint(getString(R.string.favorites_title_2, favCatName, keyword))
|
||||
}
|
||||
@@ -231,7 +222,6 @@ class FavoritesScene : SearchBarScene() {
|
||||
savedInstanceState: Bundle?,
|
||||
): View {
|
||||
_binding = SceneFavoritesBinding.inflate(inflater, container!!)
|
||||
container.addView(ComposeView(inflater.context).apply { setMD3Content { dialogState.Intercept() } })
|
||||
setOnApplySearch {
|
||||
if (!tracker.isInCustomChoice) {
|
||||
switchFav(urlBuilder.favCat, it)
|
||||
@@ -338,8 +328,7 @@ class FavoritesScene : SearchBarScene() {
|
||||
val transition = ViewTransition(binding.refreshLayout, binding.progress, binding.tip)
|
||||
val empty = getString(R.string.gallery_list_empty_hit)
|
||||
adapter.addLoadStateListener {
|
||||
viewLifecycleOwner.lifecycleScope.launchUI {
|
||||
ensureActive()
|
||||
lifecycleScope.launchUI {
|
||||
when (val state = it.refresh) {
|
||||
is LoadState.Loading -> {
|
||||
showSearchBar()
|
||||
@@ -392,36 +381,6 @@ class FavoritesScene : SearchBarScene() {
|
||||
}
|
||||
switchFav(Settings.recentFavCat)
|
||||
}
|
||||
val itemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.Callback() {
|
||||
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
|
||||
return makeMovementFlags(0, ItemTouchHelper.LEFT)
|
||||
}
|
||||
override fun onMove(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder,
|
||||
target: RecyclerView.ViewHolder,
|
||||
) = false
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||
val position = viewHolder.bindingAdapterPosition
|
||||
val info = mAdapter!!.peek(position)!!
|
||||
lifecycleScope.launchIO {
|
||||
dialogState.awaitPermissionOrCancel(
|
||||
confirmText = R.string.delete,
|
||||
dismissText = android.R.string.cancel,
|
||||
title = R.string.delete_favorites_dialog_title,
|
||||
onDismiss = { mAdapter!!.notifyItemChanged(position) },
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.delete_favorites_dialog_message, 1))
|
||||
}
|
||||
if (urlBuilder.favCat == FavListUrlBuilder.FAV_CAT_LOCAL) {
|
||||
EhDB.removeLocalFavorites(info)
|
||||
} else {
|
||||
EhEngine.modifyFavorites(info.gid, info.token)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
itemTouchHelper.attachToRecyclerView(binding.recyclerView)
|
||||
allowEmptySearch = false
|
||||
return binding.root
|
||||
}
|
||||
@@ -480,16 +439,6 @@ class FavoritesScene : SearchBarScene() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSearchViewExpanded() {
|
||||
super.onSearchViewExpanded()
|
||||
selectSearchFab(true)
|
||||
}
|
||||
|
||||
override fun onSearchViewHidden() {
|
||||
super.onSearchViewHidden()
|
||||
selectActionFab(true)
|
||||
}
|
||||
|
||||
private fun showGoToDialog() {
|
||||
context ?: return
|
||||
val local = LocalDateTime.of(2007, 3, 21, 0, 0)
|
||||
@@ -579,3 +528,5 @@ class FavoritesScene : SearchBarScene() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val ANIMATE_TIME = 300L
|
||||
@@ -68,6 +68,7 @@ import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
import androidx.compose.material3.FilledTonalIconToggleButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LargeTopAppBar
|
||||
@@ -158,7 +159,6 @@ import com.hippo.ehviewer.ui.setMD3Content
|
||||
import com.hippo.ehviewer.ui.tools.CrystalCard
|
||||
import com.hippo.ehviewer.ui.tools.DialogState
|
||||
import com.hippo.ehviewer.ui.tools.FilledTertiaryIconButton
|
||||
import com.hippo.ehviewer.ui.tools.FilledTertiaryIconToggleButton
|
||||
import com.hippo.ehviewer.ui.tools.GalleryDetailRating
|
||||
import com.hippo.ehviewer.util.AppHelper
|
||||
import com.hippo.ehviewer.util.ExceptionUtils
|
||||
@@ -712,7 +712,7 @@ class GalleryDetailScene : BaseScene() {
|
||||
stringResource(id = R.string.not_favorited)
|
||||
}
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
FilledTertiaryIconToggleButton(
|
||||
FilledTonalIconToggleButton(
|
||||
checked = favored,
|
||||
onCheckedChange = { modifyFavourite() },
|
||||
) {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.hippo.ehviewer.ui.scene
|
||||
|
||||
import android.animation.Animator
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
@@ -65,12 +66,10 @@ import com.hippo.ehviewer.client.EhUrl
|
||||
import com.hippo.ehviewer.client.EhUtils
|
||||
import com.hippo.ehviewer.client.data.BaseGalleryInfo
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder.Companion.MODE_IMAGE_SEARCH
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder.Companion.MODE_NORMAL
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder.Companion.MODE_SUBSCRIPTION
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder.Companion.MODE_TAG
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder.Companion.MODE_TOPLIST
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder.Companion.MODE_UPLOADER
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder.Companion.MODE_WHATS_HOT
|
||||
import com.hippo.ehviewer.client.exception.CloudflareBypassException
|
||||
import com.hippo.ehviewer.client.exception.EhException
|
||||
@@ -99,6 +98,7 @@ import com.hippo.ehviewer.ui.tools.DialogState
|
||||
import com.hippo.ehviewer.updater.AppUpdater
|
||||
import com.hippo.ehviewer.util.AnimationUtils
|
||||
import com.hippo.ehviewer.util.ExceptionUtils
|
||||
import com.hippo.ehviewer.util.SimpleAnimatorListener
|
||||
import com.hippo.ehviewer.util.getParcelableCompat
|
||||
import com.hippo.ehviewer.util.getValue
|
||||
import com.hippo.ehviewer.util.lazyMut
|
||||
@@ -108,7 +108,6 @@ import eu.kanade.tachiyomi.util.lang.launchUI
|
||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.parcelize.Parcelize
|
||||
@@ -131,10 +130,10 @@ class VMStorage1 : ViewModel() {
|
||||
if (urlBuilder.mode == MODE_TOPLIST) {
|
||||
// TODO: Since we know total pages, let pager support jump
|
||||
val key = (params.key ?: urlBuilder.mJumpTo ?: "0").toInt()
|
||||
val prev = (key - 1).takeIf { it > 0 }
|
||||
val next = (key + 1).takeIf { it < TOPLIST_PAGES }
|
||||
val prev = if (key != 0) key - 1 else null
|
||||
val next = if (key != 199) key + 1 else null
|
||||
runSuspendCatching {
|
||||
urlBuilder.setJumpTo(key)
|
||||
urlBuilder.setJumpTo(key.toString())
|
||||
EhEngine.getGalleryList(urlBuilder.build())
|
||||
}.onFailure {
|
||||
return@withIOContext LoadResult.Error(it)
|
||||
@@ -157,7 +156,7 @@ class VMStorage1 : ViewModel() {
|
||||
}
|
||||
}
|
||||
val r = runSuspendCatching {
|
||||
if (MODE_IMAGE_SEARCH == urlBuilder.mode) {
|
||||
if (ListUrlBuilder.MODE_IMAGE_SEARCH == urlBuilder.mode) {
|
||||
EhEngine.imageSearch(
|
||||
File(urlBuilder.imagePath!!),
|
||||
urlBuilder.isUseSimilarityScan,
|
||||
@@ -193,15 +192,23 @@ class GalleryListScene : SearchBarScene() {
|
||||
}
|
||||
private var _binding: SceneGalleryListBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
private val mSearchFabAnimatorListener = object : SimpleAnimatorListener() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
mSearchFab.visibility = View.INVISIBLE
|
||||
}
|
||||
}
|
||||
private val mActionFabAnimatorListener = object : SimpleAnimatorListener() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
binding.fabLayout.primaryFab?.visibility = View.INVISIBLE
|
||||
}
|
||||
}
|
||||
private var fabAnimator: ViewPropertyAnimator? = null
|
||||
private var mViewTransition: ViewTransition? = null
|
||||
private var mAdapter: GalleryAdapter? = null
|
||||
lateinit var mQuickSearchList: MutableList<QuickSearch>
|
||||
private var mHideActionFabSlop = 0
|
||||
private var mShowActionFab = true
|
||||
private var mState = State.NORMAL
|
||||
|
||||
override val fabLayout get() = binding.fabLayout
|
||||
|
||||
override fun getMenuResId(): Int {
|
||||
return R.menu.scene_gallery_list_searchbar_menu
|
||||
}
|
||||
@@ -216,6 +223,7 @@ class GalleryListScene : SearchBarScene() {
|
||||
ACTION_LIST_URL_BUILDER -> args?.getParcelableCompat<ListUrlBuilder>(KEY_LIST_URL_BUILDER)?.copy() ?: ListUrlBuilder()
|
||||
else -> throw IllegalStateException("Wrong KEY_ACTION:${args?.getString(KEY_ACTION)} when handle args!")
|
||||
}
|
||||
args?.getString(KEY_GOTO)?.let { mUrlBuilder.mNext = it }
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -258,7 +266,10 @@ class GalleryListScene : SearchBarScene() {
|
||||
val category = mUrlBuilder.category
|
||||
val mode = mUrlBuilder.mode
|
||||
val isPopular = mode == MODE_WHATS_HOT
|
||||
mIsTopList = mode == MODE_TOPLIST
|
||||
val isTopList = mode == MODE_TOPLIST
|
||||
if (isTopList != mIsTopList) {
|
||||
mIsTopList = isTopList
|
||||
}
|
||||
|
||||
// Update fab visibility
|
||||
binding.fabLayout.setSecondaryFabVisibilityAt(0, !isPopular)
|
||||
@@ -284,14 +295,6 @@ class GalleryListScene : SearchBarScene() {
|
||||
title = resources.getString(R.string.search)
|
||||
}
|
||||
setSearchBarHint(title)
|
||||
|
||||
when (mode) {
|
||||
MODE_NORMAL -> if (category != EhUtils.NONE || !keyword.isNullOrEmpty()) {
|
||||
mainActivity?.clearNavCheckedItem()
|
||||
}
|
||||
MODE_TAG, MODE_UPLOADER, MODE_IMAGE_SEARCH -> mainActivity?.clearNavCheckedItem()
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private val dialogState = DialogState()
|
||||
@@ -365,9 +368,9 @@ class GalleryListScene : SearchBarScene() {
|
||||
binding.refreshLayout.setOnRefreshListener { mAdapter?.refresh() }
|
||||
val transition = ViewTransition(binding.refreshLayout, binding.progress, binding.tip)
|
||||
val empty = getString(R.string.gallery_list_empty_hit)
|
||||
val noWatch = getString(R.string.gallery_list_empty_hit_subscription)
|
||||
adapter.addLoadStateListener {
|
||||
viewLifecycleOwner.lifecycleScope.launchUI {
|
||||
ensureActive()
|
||||
lifecycleScope.launchUI {
|
||||
when (val state = it.refresh) {
|
||||
is LoadState.Loading -> {
|
||||
showSearchBar()
|
||||
@@ -394,7 +397,11 @@ class GalleryListScene : SearchBarScene() {
|
||||
delay(500)
|
||||
binding.refreshLayout.isRefreshing = false
|
||||
if (mAdapter?.itemCount == 0) {
|
||||
binding.tip.text = empty
|
||||
if (mUrlBuilder.mode == MODE_SUBSCRIPTION) {
|
||||
binding.tip.text = noWatch
|
||||
} else {
|
||||
binding.tip.text = empty
|
||||
}
|
||||
transition.showView(2)
|
||||
} else {
|
||||
transition.showView(0)
|
||||
@@ -445,14 +452,12 @@ class GalleryListScene : SearchBarScene() {
|
||||
override fun onClickSecondaryFab(view: FabLayout, fab: FloatingActionButton, position: Int) {
|
||||
when (position) {
|
||||
0 -> showGoToDialog()
|
||||
1 -> { // First
|
||||
1 -> {
|
||||
mUrlBuilder.setIndex(null, true)
|
||||
mUrlBuilder.mJumpTo = null
|
||||
mAdapter?.refresh()
|
||||
}
|
||||
2 -> { // Last
|
||||
2 -> {
|
||||
if (mIsTopList) {
|
||||
mUrlBuilder.mJumpTo = "${TOPLIST_PAGES - 1}"
|
||||
mAdapter?.refresh()
|
||||
} else {
|
||||
mUrlBuilder.setIndex("1", false)
|
||||
@@ -476,6 +481,7 @@ class GalleryListScene : SearchBarScene() {
|
||||
}
|
||||
binding.fabLayout.primaryFab!!.setImageDrawable(actionFabDrawable)
|
||||
addAboveSnackView(binding.fabLayout)
|
||||
mSearchFab.setOnClickListener { onApplySearch() }
|
||||
|
||||
// Update list url builder
|
||||
onUpdateUrlBuilder()
|
||||
@@ -514,7 +520,7 @@ class GalleryListScene : SearchBarScene() {
|
||||
val context = context ?: return
|
||||
|
||||
// Can't add image search as quick search
|
||||
if (MODE_IMAGE_SEARCH == mUrlBuilder.mode) {
|
||||
if (ListUrlBuilder.MODE_IMAGE_SEARCH == mUrlBuilder.mode) {
|
||||
showTip(R.string.image_search_not_quick_search, LENGTH_LONG)
|
||||
return
|
||||
}
|
||||
@@ -638,9 +644,9 @@ class GalleryListScene : SearchBarScene() {
|
||||
private fun showGoToDialog() {
|
||||
mAdapter ?: return
|
||||
if (mIsTopList) {
|
||||
val page = mUrlBuilder.mJumpTo?.toIntOrNull() ?: 0
|
||||
val pages = 200
|
||||
val title = getString(R.string.go_to)
|
||||
val hint = getString(R.string.go_to_hint, page + 1, TOPLIST_PAGES)
|
||||
val hint = getString(R.string.go_to_hint, 1, 200)
|
||||
lifecycleScope.launch {
|
||||
val text = dialogState.awaitInputText(
|
||||
title = title,
|
||||
@@ -653,13 +659,13 @@ class GalleryListScene : SearchBarScene() {
|
||||
}.onFailure {
|
||||
return@awaitInputText getString(R.string.error_invalid_number)
|
||||
}.getOrThrow()
|
||||
if (goTo !in 0..<TOPLIST_PAGES) {
|
||||
if (goTo < 0 || goTo >= pages) {
|
||||
getString(R.string.error_out_of_range)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}.trim().toInt() - 1
|
||||
mUrlBuilder.setJumpTo(text)
|
||||
mUrlBuilder.setJumpTo(text.toString())
|
||||
mAdapter?.refresh()
|
||||
}
|
||||
} else {
|
||||
@@ -732,6 +738,70 @@ class GalleryListScene : SearchBarScene() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectSearchFab(animation: Boolean) {
|
||||
_binding ?: return
|
||||
mShowActionFab = false
|
||||
if (animation) {
|
||||
val fab: View? = binding.fabLayout.primaryFab
|
||||
val delay: Long
|
||||
if (View.INVISIBLE == fab!!.visibility) {
|
||||
delay = 0L
|
||||
} else {
|
||||
delay = ANIMATE_TIME
|
||||
binding.fabLayout.setExpanded(expanded = false, animation = true)
|
||||
fab.animate().scaleX(0.0f).scaleY(0.0f).setListener(mActionFabAnimatorListener)
|
||||
.setDuration(ANIMATE_TIME).setStartDelay(0L)
|
||||
.setInterpolator(AnimationUtils.SLOW_FAST_INTERPOLATOR).start()
|
||||
}
|
||||
mSearchFab.visibility = View.VISIBLE
|
||||
mSearchFab.rotation = -45.0f
|
||||
mSearchFab.animate().scaleX(1.0f).scaleY(1.0f).rotation(0.0f).setListener(null)
|
||||
.setDuration(ANIMATE_TIME).setStartDelay(delay)
|
||||
.setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR).start()
|
||||
} else {
|
||||
binding.fabLayout.setExpanded(expanded = false, animation = false)
|
||||
val fab: View? = binding.fabLayout.primaryFab
|
||||
fab!!.visibility = View.INVISIBLE
|
||||
fab.scaleX = 0.0f
|
||||
fab.scaleY = 0.0f
|
||||
mSearchFab.visibility = View.VISIBLE
|
||||
mSearchFab.scaleX = 1.0f
|
||||
mSearchFab.scaleY = 1.0f
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectActionFab(animation: Boolean) {
|
||||
_binding ?: return
|
||||
mShowActionFab = true
|
||||
if (animation) {
|
||||
val delay: Long
|
||||
if (View.INVISIBLE == mSearchFab.visibility) {
|
||||
delay = 0L
|
||||
} else {
|
||||
delay = ANIMATE_TIME
|
||||
mSearchFab.animate().scaleX(0.0f).scaleY(0.0f)
|
||||
.setListener(mSearchFabAnimatorListener)
|
||||
.setDuration(ANIMATE_TIME).setStartDelay(0L)
|
||||
.setInterpolator(AnimationUtils.SLOW_FAST_INTERPOLATOR).start()
|
||||
}
|
||||
val fab: View? = binding.fabLayout.primaryFab
|
||||
fab!!.visibility = View.VISIBLE
|
||||
fab.rotation = -45.0f
|
||||
fab.animate().scaleX(1.0f).scaleY(1.0f).rotation(0.0f).setListener(null)
|
||||
.setDuration(ANIMATE_TIME).setStartDelay(delay)
|
||||
.setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR).start()
|
||||
} else {
|
||||
binding.fabLayout.setExpanded(expanded = false, animation = false)
|
||||
val fab: View? = binding.fabLayout.primaryFab
|
||||
fab!!.visibility = View.VISIBLE
|
||||
fab.scaleX = 1.0f
|
||||
fab.scaleY = 1.0f
|
||||
mSearchFab.visibility = View.INVISIBLE
|
||||
mSearchFab.scaleX = 0.0f
|
||||
mSearchFab.scaleY = 0.0f
|
||||
}
|
||||
}
|
||||
|
||||
private fun setState(state: State) {
|
||||
setState(state, true)
|
||||
}
|
||||
@@ -793,35 +863,24 @@ class GalleryListScene : SearchBarScene() {
|
||||
private fun onApplySearch(query: String?) {
|
||||
_binding ?: return
|
||||
lifecycleScope.launchIO {
|
||||
val builder = ListUrlBuilder()
|
||||
val oldMode = mUrlBuilder.mode
|
||||
if (mState == State.SEARCH || mState == State.SEARCH_SHOW_LIST) {
|
||||
try {
|
||||
binding.searchLayout.formatListUrlBuilder(builder, query)
|
||||
binding.searchLayout.formatListUrlBuilder(mUrlBuilder, query)
|
||||
} catch (e: EhException) {
|
||||
showTip(e.message, LENGTH_LONG)
|
||||
return@launchIO
|
||||
}
|
||||
} else {
|
||||
val oldMode = mUrlBuilder.mode
|
||||
// If it's MODE_SUBSCRIPTION, keep it
|
||||
val newMode = if (oldMode == MODE_SUBSCRIPTION) MODE_SUBSCRIPTION else MODE_NORMAL
|
||||
builder.mode = newMode
|
||||
builder.keyword = query
|
||||
mUrlBuilder.reset()
|
||||
mUrlBuilder.mode = newMode
|
||||
mUrlBuilder.keyword = query
|
||||
}
|
||||
withUIContext {
|
||||
when (oldMode) {
|
||||
MODE_TOPLIST, MODE_WHATS_HOT -> {
|
||||
// Wait for search view to hide
|
||||
delay(300)
|
||||
navAnimated(R.id.galleryListScene, builder.toStartArgs())
|
||||
}
|
||||
|
||||
else -> {
|
||||
mUrlBuilder = builder
|
||||
onUpdateUrlBuilder()
|
||||
mAdapter?.refresh()
|
||||
}
|
||||
}
|
||||
onUpdateUrlBuilder()
|
||||
mAdapter?.refresh()
|
||||
setState(State.NORMAL)
|
||||
}
|
||||
}
|
||||
@@ -840,23 +899,26 @@ class GalleryListScene : SearchBarScene() {
|
||||
|
||||
private inner class QsDrawerAdapter(private val mInflater: LayoutInflater) :
|
||||
RecyclerView.Adapter<QsDrawerHolder>() {
|
||||
private val keywords = intArrayOf(11, 12, 13, 15)
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QsDrawerHolder {
|
||||
val holder = QsDrawerHolder(ItemDrawerListBinding.inflate(mInflater, parent, false))
|
||||
holder.itemView.setOnClickListener {
|
||||
if (!mIsTopList) {
|
||||
if (!mIsTopList) {
|
||||
holder.itemView.setOnClickListener {
|
||||
val q = mQuickSearchList[holder.bindingAdapterPosition]
|
||||
// Navigate to galleryListScene to clear nav checked item
|
||||
navAnimated(R.id.galleryListScene, ListUrlBuilder().apply { set(q) }.toStartArgs())
|
||||
} else {
|
||||
mUrlBuilder.keyword = keywords[holder.bindingAdapterPosition].toString()
|
||||
mUrlBuilder.mJumpTo = null
|
||||
mUrlBuilder.set(q)
|
||||
onUpdateUrlBuilder()
|
||||
mAdapter?.refresh()
|
||||
setState(State.NORMAL)
|
||||
closeSideSheet()
|
||||
}
|
||||
} else {
|
||||
val keywords = intArrayOf(11, 12, 13, 15)
|
||||
holder.itemView.setOnClickListener {
|
||||
mUrlBuilder.keyword = keywords[holder.bindingAdapterPosition].toString()
|
||||
onUpdateUrlBuilder()
|
||||
mAdapter?.refresh()
|
||||
setState(State.NORMAL)
|
||||
closeSideSheet()
|
||||
}
|
||||
setState(State.NORMAL)
|
||||
closeSideSheet()
|
||||
}
|
||||
return holder
|
||||
}
|
||||
@@ -986,6 +1048,7 @@ class GalleryListScene : SearchBarScene() {
|
||||
|
||||
companion object {
|
||||
const val KEY_ACTION = "action"
|
||||
const val KEY_GOTO = "goto"
|
||||
const val ACTION_HOMEPAGE = "action_homepage"
|
||||
const val ACTION_SUBSCRIPTION = "action_subscription"
|
||||
const val ACTION_WHATS_HOT = "action_whats_hot"
|
||||
@@ -993,6 +1056,7 @@ class GalleryListScene : SearchBarScene() {
|
||||
const val ACTION_LIST_URL_BUILDER = "action_list_url_builder"
|
||||
const val KEY_LIST_URL_BUILDER = "list_url_builder"
|
||||
const val KEY_STATE = "state"
|
||||
private const val ANIMATE_TIME = 300L
|
||||
fun ListUrlBuilder.toStartArgs() = bundleOf(
|
||||
KEY_ACTION to ACTION_LIST_URL_BUILDER,
|
||||
KEY_LIST_URL_BUILDER to this,
|
||||
@@ -1000,8 +1064,6 @@ class GalleryListScene : SearchBarScene() {
|
||||
}
|
||||
}
|
||||
|
||||
private const val TOPLIST_PAGES = 200
|
||||
|
||||
@Parcelize
|
||||
enum class State : Parcelable {
|
||||
NORMAL, SIMPLE_SEARCH, SEARCH, SEARCH_SHOW_LIST
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.hippo.ehviewer.ui.scene
|
||||
|
||||
import android.animation.Animator
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
@@ -41,10 +40,7 @@ import com.hippo.ehviewer.dao.Search
|
||||
import com.hippo.ehviewer.dao.SearchDao
|
||||
import com.hippo.ehviewer.databinding.SceneSearchbarBinding
|
||||
import com.hippo.ehviewer.ui.legacy.BaseDialogBuilder
|
||||
import com.hippo.ehviewer.ui.legacy.FabLayout
|
||||
import com.hippo.ehviewer.ui.setMD3Content
|
||||
import com.hippo.ehviewer.util.AnimationUtils
|
||||
import com.hippo.ehviewer.util.SimpleAnimatorListener
|
||||
import com.jamal.composeprefs3.ui.ifNotNullThen
|
||||
import com.jamal.composeprefs3.ui.ifTrueThen
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
@@ -61,18 +57,6 @@ abstract class SearchBarScene : BaseScene(), ToolBarScene {
|
||||
private val mSearchDatabase = searchDatabase.searchDao()
|
||||
private var onApplySearch: (String) -> Unit = {}
|
||||
protected val mSearchFab get() = binding.searchFab
|
||||
protected abstract val fabLayout: FabLayout
|
||||
protected var mShowActionFab = true
|
||||
protected val mActionFabAnimatorListener = object : SimpleAnimatorListener() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
fabLayout.primaryFab?.visibility = View.INVISIBLE
|
||||
}
|
||||
}
|
||||
private val mSearchFabAnimatorListener = object : SimpleAnimatorListener() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
mSearchFab.visibility = View.INVISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
_binding = SceneSearchbarBinding.inflate(inflater, container, false)
|
||||
@@ -115,7 +99,6 @@ abstract class SearchBarScene : BaseScene(), ToolBarScene {
|
||||
onSearchViewHidden()
|
||||
}
|
||||
}
|
||||
mSearchFab.setOnClickListener { onApplySearch() }
|
||||
onCreateViewWithToolbar(inflater, binding.root, savedInstanceState)
|
||||
// This has to be placed after onCreateViewWithToolbar() since
|
||||
// callbacks are invoked in the reverse order in which they are added
|
||||
@@ -164,70 +147,6 @@ abstract class SearchBarScene : BaseScene(), ToolBarScene {
|
||||
privLockModeStart = null
|
||||
}
|
||||
|
||||
fun selectSearchFab(animation: Boolean) {
|
||||
_binding ?: return
|
||||
mShowActionFab = false
|
||||
if (animation) {
|
||||
val fab: View? = fabLayout.primaryFab
|
||||
val delay: Long
|
||||
if (View.INVISIBLE == fab!!.visibility) {
|
||||
delay = 0L
|
||||
} else {
|
||||
delay = ANIMATE_TIME
|
||||
fabLayout.setExpanded(expanded = false, animation = true)
|
||||
fab.animate().scaleX(0.0f).scaleY(0.0f).setListener(mActionFabAnimatorListener)
|
||||
.setDuration(ANIMATE_TIME).setStartDelay(0L)
|
||||
.setInterpolator(AnimationUtils.SLOW_FAST_INTERPOLATOR).start()
|
||||
}
|
||||
mSearchFab.visibility = View.VISIBLE
|
||||
mSearchFab.rotation = -45.0f
|
||||
mSearchFab.animate().scaleX(1.0f).scaleY(1.0f).rotation(0.0f).setListener(null)
|
||||
.setDuration(ANIMATE_TIME).setStartDelay(delay)
|
||||
.setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR).start()
|
||||
} else {
|
||||
fabLayout.setExpanded(expanded = false, animation = false)
|
||||
val fab = fabLayout.primaryFab!!
|
||||
fab.visibility = View.INVISIBLE
|
||||
fab.scaleX = 0.0f
|
||||
fab.scaleY = 0.0f
|
||||
mSearchFab.visibility = View.VISIBLE
|
||||
mSearchFab.scaleX = 1.0f
|
||||
mSearchFab.scaleY = 1.0f
|
||||
}
|
||||
}
|
||||
|
||||
fun selectActionFab(animation: Boolean) {
|
||||
_binding ?: return
|
||||
mShowActionFab = true
|
||||
if (animation) {
|
||||
val delay: Long
|
||||
if (View.INVISIBLE == mSearchFab.visibility) {
|
||||
delay = 0L
|
||||
} else {
|
||||
delay = ANIMATE_TIME
|
||||
mSearchFab.animate().scaleX(0.0f).scaleY(0.0f)
|
||||
.setListener(mSearchFabAnimatorListener)
|
||||
.setDuration(ANIMATE_TIME).setStartDelay(0L)
|
||||
.setInterpolator(AnimationUtils.SLOW_FAST_INTERPOLATOR).start()
|
||||
}
|
||||
val fab = fabLayout.primaryFab!!
|
||||
fab.visibility = View.VISIBLE
|
||||
fab.rotation = -45.0f
|
||||
fab.animate().scaleX(1.0f).scaleY(1.0f).rotation(0.0f).setListener(null)
|
||||
.setDuration(ANIMATE_TIME).setStartDelay(delay)
|
||||
.setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR).start()
|
||||
} else {
|
||||
fabLayout.setExpanded(expanded = false, animation = false)
|
||||
val fab = fabLayout.primaryFab!!
|
||||
fab.visibility = View.VISIBLE
|
||||
fab.scaleX = 1.0f
|
||||
fab.scaleY = 1.0f
|
||||
mSearchFab.visibility = View.INVISIBLE
|
||||
mSearchFab.scaleX = 0.0f
|
||||
mSearchFab.scaleY = 0.0f
|
||||
}
|
||||
}
|
||||
|
||||
fun setSearchBarHint(hint: String?) {
|
||||
binding.toolbar.hint = hint
|
||||
}
|
||||
@@ -410,5 +329,3 @@ fun wrapTagKeyword(keyword: String, translate: Boolean = false): String {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const val ANIMATE_TIME = 300L
|
||||
@@ -193,27 +193,6 @@ fun EhScreen() {
|
||||
summary = stringResource(id = R.string.settings_my_tags_summary),
|
||||
) { navController.navigate(MYTAGS_SCREEN) }
|
||||
}
|
||||
var defaultFavSlot by Settings::defaultFavSlot.observed
|
||||
val disabled = stringResource(id = R.string.disabled_nav)
|
||||
val localFav = stringResource(id = R.string.local_favorites)
|
||||
val summary = when (defaultFavSlot) {
|
||||
-1 -> localFav
|
||||
in 0..9 -> Settings.favCat[defaultFavSlot]
|
||||
else -> stringResource(id = R.string.default_favorites_warning)
|
||||
}
|
||||
val items = if (signin) {
|
||||
arrayOf(disabled, localFav, *Settings.favCat)
|
||||
} else {
|
||||
arrayOf(disabled, localFav)
|
||||
}
|
||||
Preference(
|
||||
title = stringResource(id = R.string.default_favorites_collection),
|
||||
summary = summary,
|
||||
) {
|
||||
coroutineScope.launch {
|
||||
defaultFavSlot = dialogState.showSelectItem(*items, title = R.string.default_favorites_collection) - 2
|
||||
}
|
||||
}
|
||||
SimpleMenuPreferenceInt(
|
||||
title = stringResource(id = rikka.core.R.string.dark_theme),
|
||||
entry = R.array.night_mode_entries,
|
||||
@@ -265,6 +244,27 @@ fun EhScreen() {
|
||||
entryValueRes = R.array.thumb_resolution_entry_values,
|
||||
value = thumbResolution,
|
||||
)
|
||||
var defaultFavSlot by Settings::defaultFavSlot.observed
|
||||
val disabled = stringResource(id = R.string.disabled_nav)
|
||||
val localFav = stringResource(id = R.string.local_favorites)
|
||||
val summary = when (defaultFavSlot) {
|
||||
-1 -> localFav
|
||||
in 0..9 -> Settings.favCat[defaultFavSlot]
|
||||
else -> stringResource(id = R.string.default_favorites_warning)
|
||||
}
|
||||
val items = if (signin) {
|
||||
arrayOf(disabled, localFav, *Settings.favCat)
|
||||
} else {
|
||||
arrayOf(disabled, localFav)
|
||||
}
|
||||
Preference(
|
||||
title = stringResource(id = R.string.default_favorites_collection),
|
||||
summary = summary,
|
||||
) {
|
||||
coroutineScope.launch {
|
||||
defaultFavSlot = dialogState.showSelectItem(*items, title = R.string.default_favorites_collection) - 2
|
||||
}
|
||||
}
|
||||
SwitchPreference(
|
||||
title = stringResource(id = R.string.settings_eh_force_eh_thumb),
|
||||
summary = stringResource(id = R.string.settings_eh_force_eh_thumb_summary),
|
||||
|
||||
@@ -3,10 +3,12 @@ package com.hippo.ehviewer.ui.tools
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
@@ -146,25 +148,18 @@ class DialogState {
|
||||
@StringRes confirmText: Int? = null,
|
||||
@StringRes dismissText: Int? = null,
|
||||
@StringRes title: Int? = null,
|
||||
onDismiss: () -> Unit = {},
|
||||
text: (@Composable () -> Unit)? = null,
|
||||
) {
|
||||
return dialog { cont ->
|
||||
AlertDialog(
|
||||
onDismissRequest = {
|
||||
onDismiss()
|
||||
cont.cancel()
|
||||
},
|
||||
onDismissRequest = { cont.cancel() },
|
||||
confirmButton = {
|
||||
TextButton(onClick = { cont.resume(Unit) }) {
|
||||
Text(text = stringResource(id = confirmText ?: android.R.string.ok))
|
||||
}
|
||||
},
|
||||
dismissButton = dismissText.ifNotNullThen {
|
||||
TextButton(onClick = {
|
||||
onDismiss()
|
||||
cont.cancel()
|
||||
}) {
|
||||
TextButton(onClick = { cont.cancel() }) {
|
||||
Text(text = stringResource(id = dismissText!!))
|
||||
}
|
||||
},
|
||||
@@ -200,22 +195,12 @@ class DialogState {
|
||||
vararg items: String,
|
||||
@StringRes title: Int,
|
||||
): Int = showNoButton {
|
||||
Column(modifier = Modifier.padding(vertical = 8.dp)) {
|
||||
Text(
|
||||
text = stringResource(id = title),
|
||||
modifier = Modifier.padding(horizontal = 24.dp, vertical = 8.dp),
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
)
|
||||
LazyColumn {
|
||||
itemsIndexed(items) { index, text ->
|
||||
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.tertiary) {
|
||||
Text(
|
||||
text = text,
|
||||
modifier = Modifier.clickable { dismissWith(index) }.fillMaxWidth()
|
||||
.padding(horizontal = 24.dp, vertical = 16.dp),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
}
|
||||
Column(modifier = Modifier.padding(24.dp)) {
|
||||
Text(text = stringResource(id = title), style = MaterialTheme.typography.headlineSmall)
|
||||
Spacer(modifier = Modifier.size(16.dp))
|
||||
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.tertiary) {
|
||||
items.forEachIndexed { index, text ->
|
||||
Text(text = text, modifier = Modifier.clickable { dismissWith(index) }.fillMaxWidth().padding(vertical = 8.dp), style = MaterialTheme.typography.titleMedium)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.hippo.ehviewer.util
|
||||
|
||||
import com.google.android.gms.net.CronetProviderInstaller
|
||||
import com.hippo.ehviewer.Settings
|
||||
|
||||
val isCronetSupported: Boolean
|
||||
get() = Settings.enableQuic && isAtLeastQ
|
||||
get() = Settings.enableQuic && isAtLeastQ && CronetProviderInstaller.isInstalled()
|
||||
Reference in new issue
Block a user