Remove some dead code
(cherry picked from commit b0dc20e00ce7c4cc33742fa3d4ae9d55503a25a4) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiAppBarLayout.kt
This commit is contained in:
parent
d8ba1774cb
commit
bcf6904363
@ -270,9 +270,6 @@ dependencies {
|
||||
implementation(libs.bundles.voyager)
|
||||
implementation(libs.wheelpicker)
|
||||
|
||||
// FlowBinding
|
||||
implementation(libs.flowbinding.android)
|
||||
|
||||
// Logging
|
||||
implementation(libs.logcat)
|
||||
|
||||
|
@ -37,9 +37,6 @@ import eu.kanade.tachiyomi.widget.materialdialogs.setTextInput
|
||||
import exh.util.dropBlank
|
||||
import exh.util.trimOrNull
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import reactivecircus.flowbinding.android.view.clicks
|
||||
|
||||
@Composable
|
||||
fun EditMangaDialog(
|
||||
@ -190,9 +187,7 @@ private fun onViewCreated(manga: Manga, context: Context, binding: EditMangaDial
|
||||
}
|
||||
binding.mangaGenresTags.clearFocus()
|
||||
|
||||
binding.resetTags.clicks()
|
||||
.onEach { resetTags(manga, binding, scope) }
|
||||
.launchIn(scope)
|
||||
binding.resetTags.setOnClickListener { resetTags(manga, binding, scope) }
|
||||
}
|
||||
|
||||
private fun resetTags(manga: Manga, binding: EditMangaDialogBinding, scope: CoroutineScope) {
|
||||
@ -235,7 +230,7 @@ private fun ChipGroup.setChips(items: List<String>, scope: CoroutineScope) {
|
||||
setTint(context.getResourceColor(R.attr.colorAccent))
|
||||
}
|
||||
|
||||
clicks().onEach {
|
||||
setOnClickListener {
|
||||
var newTag: String? = null
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.add_tag)
|
||||
@ -247,7 +242,7 @@ private fun ChipGroup.setChips(items: List<String>, scope: CoroutineScope) {
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
}.launchIn(scope)
|
||||
}
|
||||
}
|
||||
addView(addTagChip)
|
||||
}
|
||||
|
@ -16,6 +16,8 @@ import android.graphics.drawable.RippleDrawable
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.Gravity
|
||||
import android.view.HapticFeedbackConstants
|
||||
import android.view.KeyEvent
|
||||
@ -27,8 +29,10 @@ import android.view.Window
|
||||
import android.view.WindowManager
|
||||
import android.view.animation.Animation
|
||||
import android.view.animation.AnimationUtils
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.RelativeLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.graphics.ColorUtils
|
||||
@ -105,20 +109,22 @@ import exh.source.isEhBasedSource
|
||||
import exh.util.defaultReaderType
|
||||
import exh.util.floor
|
||||
import exh.util.mangaType
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.conflate
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import logcat.LogPriority
|
||||
import nucleus.factory.RequiresPresenter
|
||||
import nucleus.view.NucleusAppCompatActivity
|
||||
import reactivecircus.flowbinding.android.view.clicks
|
||||
import reactivecircus.flowbinding.android.widget.checkedChanges
|
||||
import reactivecircus.flowbinding.android.widget.textChanges
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
@ -432,6 +438,36 @@ class ReaderActivity :
|
||||
}
|
||||
}
|
||||
|
||||
// SY -->
|
||||
fun TextView.textChanges(): Flow<CharSequence> = callbackFlow {
|
||||
val listener = object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) = Unit
|
||||
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||
trySend(s)
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable) = Unit
|
||||
}
|
||||
|
||||
addTextChangedListener(listener)
|
||||
awaitClose { removeTextChangedListener(listener) }
|
||||
}
|
||||
.conflate()
|
||||
.onStart { emit(text) }
|
||||
|
||||
fun CompoundButton.checkedChanges(): Flow<Boolean> = callbackFlow {
|
||||
val listener = CompoundButton.OnCheckedChangeListener { _, isChecked ->
|
||||
trySend(isChecked)
|
||||
}
|
||||
setOnCheckedChangeListener(listener)
|
||||
|
||||
awaitClose { setOnCheckedChangeListener(null) }
|
||||
}
|
||||
.conflate()
|
||||
.onStart { emit(isChecked) }
|
||||
// SY <--
|
||||
|
||||
/**
|
||||
* Initializes the reader menu. It sets up click listeners and the initial visibility.
|
||||
*/
|
||||
@ -494,30 +530,26 @@ class ReaderActivity :
|
||||
|
||||
// SY -->
|
||||
listOf(binding.leftChapter, binding.aboveChapter).forEach {
|
||||
it.clicks()
|
||||
.onEach {
|
||||
if (viewer != null) {
|
||||
if (viewer is R2LPagerViewer) {
|
||||
loadNextChapter()
|
||||
} else {
|
||||
loadPreviousChapter()
|
||||
}
|
||||
it.setOnClickListener {
|
||||
if (viewer != null) {
|
||||
if (viewer is R2LPagerViewer) {
|
||||
loadNextChapter()
|
||||
} else {
|
||||
loadPreviousChapter()
|
||||
}
|
||||
}
|
||||
.launchIn(lifecycleScope)
|
||||
}
|
||||
}
|
||||
listOf(binding.rightChapter, binding.belowChapter).forEach {
|
||||
it.clicks()
|
||||
.onEach {
|
||||
if (viewer != null) {
|
||||
if (viewer is R2LPagerViewer) {
|
||||
loadPreviousChapter()
|
||||
} else {
|
||||
loadNextChapter()
|
||||
}
|
||||
it.setOnClickListener {
|
||||
if (viewer != null) {
|
||||
if (viewer is R2LPagerViewer) {
|
||||
loadPreviousChapter()
|
||||
} else {
|
||||
loadNextChapter()
|
||||
}
|
||||
}
|
||||
.launchIn(lifecycleScope)
|
||||
}
|
||||
}
|
||||
|
||||
initBottomShortcuts()
|
||||
@ -713,12 +745,10 @@ class ReaderActivity :
|
||||
}
|
||||
|
||||
fun initDropdownMenu() {
|
||||
binding.expandEhButton.clicks()
|
||||
.onEach {
|
||||
ehUtilsVisible = !ehUtilsVisible
|
||||
setEhUtilsVisibility(ehUtilsVisible)
|
||||
}
|
||||
.launchIn(lifecycleScope)
|
||||
binding.expandEhButton.setOnClickListener {
|
||||
ehUtilsVisible = !ehUtilsVisible
|
||||
setEhUtilsVisibility(ehUtilsVisible)
|
||||
}
|
||||
|
||||
binding.ehAutoscrollFreq.setText(
|
||||
readerPreferences.autoscrollInterval().get().let {
|
||||
@ -763,105 +793,95 @@ class ReaderActivity :
|
||||
}
|
||||
.launchIn(lifecycleScope)
|
||||
|
||||
binding.ehAutoscrollHelp.clicks()
|
||||
.onEach {
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.eh_autoscroll_help)
|
||||
.setMessage(R.string.eh_autoscroll_help_message)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
.launchIn(lifecycleScope)
|
||||
binding.ehAutoscrollHelp.setOnClickListener {
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.eh_autoscroll_help)
|
||||
.setMessage(R.string.eh_autoscroll_help_message)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
binding.ehRetryAll.clicks()
|
||||
.onEach {
|
||||
var retried = 0
|
||||
binding.ehRetryAll.setOnClickListener {
|
||||
var retried = 0
|
||||
|
||||
presenter.viewerChaptersRelay.value
|
||||
.currChapter
|
||||
.pages
|
||||
?.forEachIndexed { _, page ->
|
||||
var shouldQueuePage = false
|
||||
if (page.status == Page.ERROR) {
|
||||
shouldQueuePage = true
|
||||
} /*else if (page.status == Page.LOAD_PAGE ||
|
||||
page.status == Page.DOWNLOAD_IMAGE) {
|
||||
// Do nothing
|
||||
}*/
|
||||
presenter.viewerChaptersRelay.value
|
||||
.currChapter
|
||||
.pages
|
||||
?.forEachIndexed { _, page ->
|
||||
var shouldQueuePage = false
|
||||
if (page.status == Page.ERROR) {
|
||||
shouldQueuePage = true
|
||||
} /*else if (page.status == Page.LOAD_PAGE ||
|
||||
page.status == Page.DOWNLOAD_IMAGE) {
|
||||
// Do nothing
|
||||
}*/
|
||||
|
||||
if (shouldQueuePage) {
|
||||
page.status = Page.QUEUE
|
||||
} else {
|
||||
return@forEachIndexed
|
||||
}
|
||||
|
||||
// If we are using EHentai/ExHentai, get a new image URL
|
||||
presenter.manga?.let { m ->
|
||||
val src = sourceManager.get(m.source)
|
||||
if (src?.isEhBasedSource() == true) {
|
||||
page.imageUrl = null
|
||||
}
|
||||
}
|
||||
|
||||
val loader = page.chapter.pageLoader
|
||||
if (page.index == exhCurrentpage()?.index && loader is HttpPageLoader) {
|
||||
loader.boostPage(page)
|
||||
} else {
|
||||
loader?.retryPage(page)
|
||||
}
|
||||
|
||||
retried++
|
||||
}
|
||||
|
||||
toast(resources.getQuantityString(R.plurals.eh_retry_toast, retried, retried))
|
||||
}
|
||||
.launchIn(lifecycleScope)
|
||||
|
||||
binding.ehRetryAllHelp.clicks()
|
||||
.onEach {
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.eh_retry_all_help)
|
||||
.setMessage(R.string.eh_retry_all_help_message)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
.launchIn(lifecycleScope)
|
||||
|
||||
binding.ehBoostPage.clicks()
|
||||
.onEach {
|
||||
viewer ?: return@onEach
|
||||
val curPage = exhCurrentpage() ?: run {
|
||||
toast(R.string.eh_boost_page_invalid)
|
||||
return@onEach
|
||||
}
|
||||
|
||||
if (curPage.status == Page.ERROR) {
|
||||
toast(R.string.eh_boost_page_errored)
|
||||
} else if (curPage.status == Page.LOAD_PAGE || curPage.status == Page.DOWNLOAD_IMAGE) {
|
||||
toast(R.string.eh_boost_page_downloading)
|
||||
} else if (curPage.status == Page.READY) {
|
||||
toast(R.string.eh_boost_page_downloaded)
|
||||
} else {
|
||||
val loader = (presenter.viewerChaptersRelay.value.currChapter.pageLoader as? HttpPageLoader)
|
||||
if (loader != null) {
|
||||
loader.boostPage(curPage)
|
||||
toast(R.string.eh_boost_boosted)
|
||||
if (shouldQueuePage) {
|
||||
page.status = Page.QUEUE
|
||||
} else {
|
||||
toast(R.string.eh_boost_invalid_loader)
|
||||
return@forEachIndexed
|
||||
}
|
||||
|
||||
// If we are using EHentai/ExHentai, get a new image URL
|
||||
presenter.manga?.let { m ->
|
||||
val src = sourceManager.get(m.source)
|
||||
if (src?.isEhBasedSource() == true) {
|
||||
page.imageUrl = null
|
||||
}
|
||||
}
|
||||
|
||||
val loader = page.chapter.pageLoader
|
||||
if (page.index == exhCurrentpage()?.index && loader is HttpPageLoader) {
|
||||
loader.boostPage(page)
|
||||
} else {
|
||||
loader?.retryPage(page)
|
||||
}
|
||||
|
||||
retried++
|
||||
}
|
||||
|
||||
toast(resources.getQuantityString(R.plurals.eh_retry_toast, retried, retried))
|
||||
}
|
||||
|
||||
binding.ehRetryAllHelp.setOnClickListener {
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.eh_retry_all_help)
|
||||
.setMessage(R.string.eh_retry_all_help_message)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
binding.ehBoostPage.setOnClickListener {
|
||||
viewer ?: return@setOnClickListener
|
||||
val curPage = exhCurrentpage() ?: run {
|
||||
toast(R.string.eh_boost_page_invalid)
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
if (curPage.status == Page.ERROR) {
|
||||
toast(R.string.eh_boost_page_errored)
|
||||
} else if (curPage.status == Page.LOAD_PAGE || curPage.status == Page.DOWNLOAD_IMAGE) {
|
||||
toast(R.string.eh_boost_page_downloading)
|
||||
} else if (curPage.status == Page.READY) {
|
||||
toast(R.string.eh_boost_page_downloaded)
|
||||
} else {
|
||||
val loader = (presenter.viewerChaptersRelay.value.currChapter.pageLoader as? HttpPageLoader)
|
||||
if (loader != null) {
|
||||
loader.boostPage(curPage)
|
||||
toast(R.string.eh_boost_boosted)
|
||||
} else {
|
||||
toast(R.string.eh_boost_invalid_loader)
|
||||
}
|
||||
}
|
||||
.launchIn(lifecycleScope)
|
||||
}
|
||||
|
||||
binding.ehBoostPageHelp.clicks()
|
||||
.onEach {
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.eh_boost_page_help)
|
||||
.setMessage(R.string.eh_boost_page_help_message)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
.launchIn(lifecycleScope)
|
||||
binding.ehBoostPageHelp.setOnClickListener {
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.eh_boost_page_help)
|
||||
.setMessage(R.string.eh_boost_page_help_message)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun exhCurrentpage(): ReaderPage? {
|
||||
|
@ -1,8 +1,6 @@
|
||||
package eu.kanade.tachiyomi.util.system
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.app.KeyguardManager
|
||||
import android.app.Notification
|
||||
import android.app.NotificationManager
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
@ -24,11 +22,9 @@ import android.util.TypedValue
|
||||
import android.view.Display
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.graphics.alpha
|
||||
@ -75,34 +71,6 @@ fun Context.copyToClipboard(label: String, content: String) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create a notification builder.
|
||||
*
|
||||
* @param id the channel id.
|
||||
* @param block the function that will execute inside the builder.
|
||||
* @return a notification to be displayed or updated.
|
||||
*/
|
||||
fun Context.notificationBuilder(channelId: String, block: (NotificationCompat.Builder.() -> Unit)? = null): NotificationCompat.Builder {
|
||||
val builder = NotificationCompat.Builder(this, channelId)
|
||||
.setColor(getColor(R.color.accent_blue))
|
||||
if (block != null) {
|
||||
builder.block()
|
||||
}
|
||||
return builder
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create a notification.
|
||||
*
|
||||
* @param id the channel id.
|
||||
* @param block the function that will execute inside the builder.
|
||||
* @return a notification to be displayed or updated.
|
||||
*/
|
||||
fun Context.notification(channelId: String, block: (NotificationCompat.Builder.() -> Unit)?): Notification {
|
||||
val builder = notificationBuilder(channelId, block)
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the give permission is granted.
|
||||
*
|
||||
@ -146,12 +114,6 @@ fun Context.hasPermission(permission: String) = ContextCompat.checkSelfPermissio
|
||||
val getDisplayMaxHeightInPx: Int
|
||||
get() = Resources.getSystem().displayMetrics.let { max(it.heightPixels, it.widthPixels) }
|
||||
|
||||
/**
|
||||
* Converts to dp.
|
||||
*/
|
||||
val Int.pxToDp: Int
|
||||
get() = (this / Resources.getSystem().displayMetrics.density).toInt()
|
||||
|
||||
/**
|
||||
* Converts to px.
|
||||
*/
|
||||
@ -182,12 +144,6 @@ val Context.wifiManager: WifiManager
|
||||
val Context.powerManager: PowerManager
|
||||
get() = getSystemService()!!
|
||||
|
||||
val Context.keyguardManager: KeyguardManager
|
||||
get() = getSystemService()!!
|
||||
|
||||
val Context.inputMethodManager: InputMethodManager
|
||||
get() = getSystemService()!!
|
||||
|
||||
val Context.displayCompat: Display?
|
||||
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
display
|
||||
|
@ -1,7 +1,11 @@
|
||||
package eu.kanade.tachiyomi.util.system
|
||||
|
||||
import android.app.Notification
|
||||
import android.content.Context
|
||||
import androidx.core.app.NotificationChannelCompat
|
||||
import androidx.core.app.NotificationChannelGroupCompat
|
||||
import androidx.core.app.NotificationCompat
|
||||
import eu.kanade.tachiyomi.R
|
||||
|
||||
/**
|
||||
* Helper method to build a notification channel group.
|
||||
@ -36,3 +40,31 @@ fun buildNotificationChannel(
|
||||
builder.block()
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create a notification builder.
|
||||
*
|
||||
* @param id the channel id.
|
||||
* @param block the function that will execute inside the builder.
|
||||
* @return a notification to be displayed or updated.
|
||||
*/
|
||||
fun Context.notificationBuilder(channelId: String, block: (NotificationCompat.Builder.() -> Unit)? = null): NotificationCompat.Builder {
|
||||
val builder = NotificationCompat.Builder(this, channelId)
|
||||
.setColor(getColor(R.color.accent_blue))
|
||||
if (block != null) {
|
||||
builder.block()
|
||||
}
|
||||
return builder
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create a notification.
|
||||
*
|
||||
* @param id the channel id.
|
||||
* @param block the function that will execute inside the builder.
|
||||
* @return a notification to be displayed or updated.
|
||||
*/
|
||||
fun Context.notification(channelId: String, block: (NotificationCompat.Builder.() -> Unit)?): Notification {
|
||||
val builder = notificationBuilder(channelId, block)
|
||||
return builder.build()
|
||||
}
|
||||
|
@ -1,19 +1,9 @@
|
||||
package eu.kanade.tachiyomi.util.view
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Animatable
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.widget.ImageView
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import coil.ImageLoader
|
||||
import coil.imageLoader
|
||||
import coil.load
|
||||
import coil.request.ImageRequest
|
||||
import coil.target.ImageViewTarget
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.system.animatorDurationScale
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
|
||||
/**
|
||||
@ -29,33 +19,3 @@ fun ImageView.setVectorCompat(@DrawableRes drawable: Int, @AttrRes tint: Int? =
|
||||
}
|
||||
setImageDrawable(vector)
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the image referenced by [data] and set it on this [ImageView],
|
||||
* and if the image is animated, this will also disable that animation
|
||||
* if [Context.animatorDurationScale] is 0
|
||||
*/
|
||||
fun ImageView.loadAutoPause(
|
||||
data: Any?,
|
||||
loader: ImageLoader = context.imageLoader,
|
||||
builder: ImageRequest.Builder.() -> Unit = {},
|
||||
) {
|
||||
load(data, loader) {
|
||||
placeholder(ColorDrawable(context.getColor(R.color.cover_placeholder)))
|
||||
error(R.drawable.cover_error)
|
||||
|
||||
// Build the original request so we can add on our success listener
|
||||
val originalListener = apply(builder).build().listener
|
||||
listener(
|
||||
onSuccess = { request, metadata ->
|
||||
(request.target as? ImageViewTarget)?.drawable.let {
|
||||
if (it is Animatable && context.animatorDurationScale == 0f) it.stop()
|
||||
}
|
||||
originalListener?.onSuccess(request, metadata)
|
||||
},
|
||||
onStart = { request -> originalListener?.onStart(request) },
|
||||
onCancel = { request -> originalListener?.onCancel(request) },
|
||||
onError = { request, throwable -> originalListener?.onError(request, throwable) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ import android.view.Gravity
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.annotation.MenuRes
|
||||
@ -29,15 +28,11 @@ import androidx.compose.runtime.CompositionContext
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.descendants
|
||||
import androidx.core.view.forEach
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
import eu.kanade.tachiyomi.util.system.inputMethodManager
|
||||
|
||||
inline fun ComposeView.setComposeContent(crossinline content: @Composable () -> Unit) {
|
||||
consumeWindowInsets = false
|
||||
@ -70,24 +65,6 @@ inline fun ComponentActivity.setComposeContent(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a snackbar in this view.
|
||||
*
|
||||
* @param message the message to show.
|
||||
* @param length the duration of the snack.
|
||||
* @param f a function to execute in the snack, allowing for example to define a custom action.
|
||||
*/
|
||||
inline fun View.snack(
|
||||
message: String,
|
||||
length: Int = 10_000,
|
||||
f: Snackbar.() -> Unit = {},
|
||||
): Snackbar {
|
||||
val snack = Snackbar.make(this, message, length)
|
||||
snack.f()
|
||||
snack.show()
|
||||
return snack
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a tooltip shown on long press.
|
||||
*
|
||||
@ -173,20 +150,6 @@ inline fun View.popupMenu(
|
||||
return popup
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this ViewGroup's first child of specified class
|
||||
*/
|
||||
inline fun <reified T> ViewGroup.findChild(): T? {
|
||||
return children.find { it is T } as? T
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this ViewGroup's first descendant of specified class
|
||||
*/
|
||||
inline fun <reified T> ViewGroup.findDescendant(): T? {
|
||||
return descendants.find { it is T } as? T
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a deep copy of the provided [Drawable]
|
||||
*/
|
||||
@ -210,7 +173,3 @@ fun View?.isVisibleOnScreen(): Boolean {
|
||||
val screen = Rect(0, 0, Resources.getSystem().displayMetrics.widthPixels, Resources.getSystem().displayMetrics.heightPixels)
|
||||
return actualPosition.intersect(screen)
|
||||
}
|
||||
|
||||
fun View.hideKeyboard() {
|
||||
context.inputMethodManager.hideSoftInputFromWindow(windowToken, 0)
|
||||
}
|
||||
|
@ -1,214 +0,0 @@
|
||||
@file:Suppress("PackageDirectoryMismatch")
|
||||
|
||||
package com.google.android.material.appbar
|
||||
|
||||
import android.animation.AnimatorSet
|
||||
import android.animation.ValueAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.AttributeSet
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.FloatRange
|
||||
import androidx.core.graphics.withTranslation
|
||||
import androidx.lifecycle.coroutineScope
|
||||
import androidx.lifecycle.findViewTreeLifecycleOwner
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import dev.chrisbanes.insetter.applyInsetter
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.view.findChild
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import reactivecircus.flowbinding.android.view.HierarchyChangeEvent
|
||||
import reactivecircus.flowbinding.android.view.hierarchyChangeEvents
|
||||
|
||||
/**
|
||||
* [AppBarLayout] with our own lift state handler and custom title alpha.
|
||||
*
|
||||
* Inside this package to access some package-private methods.
|
||||
*/
|
||||
class TachiyomiAppBarLayout @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
) : AppBarLayout(context, attrs) {
|
||||
|
||||
private var lifted = true
|
||||
|
||||
private val toolbar by lazy { findViewById<MaterialToolbar>(R.id.toolbar) }
|
||||
|
||||
@FloatRange(from = 0.0, to = 1.0)
|
||||
var titleTextAlpha = 1F
|
||||
set(value) {
|
||||
field = value
|
||||
titleTextView?.alpha = field
|
||||
}
|
||||
|
||||
private var titleTextView: TextView? = null
|
||||
set(value) {
|
||||
field = value
|
||||
field?.alpha = titleTextAlpha
|
||||
}
|
||||
|
||||
private var animatorSet: AnimatorSet? = null
|
||||
|
||||
private var statusBarForegroundAnimator: ValueAnimator? = null
|
||||
private var currentOffset = 0
|
||||
|
||||
var isTransparentWhenNotLifted = false
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
updateStates()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disabled. Lift on scroll is handled manually with [eu.kanade.tachiyomi.widget.TachiyomiCoordinatorLayout]
|
||||
*/
|
||||
override fun isLiftOnScroll(): Boolean = false
|
||||
|
||||
override fun isLifted(): Boolean = lifted
|
||||
|
||||
override fun setLifted(lifted: Boolean): Boolean {
|
||||
return if (this.lifted != lifted) {
|
||||
this.lifted = lifted
|
||||
updateStates()
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override fun setLiftedState(lifted: Boolean, force: Boolean): Boolean = false
|
||||
|
||||
override fun draw(canvas: Canvas) {
|
||||
super.draw(canvas)
|
||||
canvas.withTranslation(y = -currentOffset.toFloat()) {
|
||||
statusBarForeground?.draw(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
|
||||
super.onLayout(changed, l, t, r, b)
|
||||
statusBarForeground?.setBounds(0, 0, width, paddingTop)
|
||||
}
|
||||
|
||||
override fun onOffsetChanged(offset: Int) {
|
||||
currentOffset = offset
|
||||
super.onOffsetChanged(offset)
|
||||
|
||||
// Show status bar foreground when offset
|
||||
val foreground = (statusBarForeground as? MaterialShapeDrawable) ?: return
|
||||
val start = foreground.alpha
|
||||
val end = if (offset != 0) 255 else 0
|
||||
|
||||
statusBarForegroundAnimator?.cancel()
|
||||
if (animatorSet?.isRunning == true) {
|
||||
foreground.alpha = end
|
||||
return
|
||||
}
|
||||
if (start != end) {
|
||||
statusBarForegroundAnimator = ValueAnimator.ofInt(start, end).apply {
|
||||
duration = resources.getInteger(R.integer.app_bar_elevation_anim_duration).toLong()
|
||||
interpolator = LINEAR_INTERPOLATOR
|
||||
addUpdateListener {
|
||||
foreground.alpha = it.animatedValue as Int
|
||||
}
|
||||
start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
toolbar.background.alpha = 0 // Use app bar background
|
||||
|
||||
titleTextView = toolbar.findChild<TextView>()
|
||||
findViewTreeLifecycleOwner()?.lifecycle?.coroutineScope?.let { scope ->
|
||||
toolbar.hierarchyChangeEvents()
|
||||
.onEach {
|
||||
when (it) {
|
||||
is HierarchyChangeEvent.ChildAdded -> {
|
||||
if (it.child is TextView) {
|
||||
titleTextView = it.child as TextView
|
||||
}
|
||||
}
|
||||
is HierarchyChangeEvent.ChildRemoved -> {
|
||||
if (it.child == titleTextView) {
|
||||
titleTextView = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.launchIn(scope)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setStatusBarForeground(drawable: Drawable?) {
|
||||
super.setStatusBarForeground(drawable)
|
||||
setWillNotDraw(statusBarForeground == null)
|
||||
}
|
||||
|
||||
@SuppressLint("Recycle")
|
||||
private fun updateStates() {
|
||||
val animators = mutableListOf<ValueAnimator>()
|
||||
|
||||
val fromElevation = elevation
|
||||
val toElevation = if (lifted) {
|
||||
resources.getDimension(R.dimen.design_appbar_elevation)
|
||||
} else {
|
||||
0F
|
||||
}
|
||||
if (fromElevation != toElevation) {
|
||||
ValueAnimator.ofFloat(fromElevation, toElevation).apply {
|
||||
addUpdateListener {
|
||||
elevation = it.animatedValue as Float
|
||||
(statusBarForeground as? MaterialShapeDrawable)?.elevation = it.animatedValue as Float
|
||||
}
|
||||
animators.add(this)
|
||||
}
|
||||
}
|
||||
|
||||
val transparent = if (lifted) false else isTransparentWhenNotLifted
|
||||
val fromAlpha = (background as? MaterialShapeDrawable)?.alpha ?: background.alpha
|
||||
val toAlpha = if (transparent) 0 else 255
|
||||
if (fromAlpha != toAlpha) {
|
||||
ValueAnimator.ofInt(fromAlpha, toAlpha).apply {
|
||||
addUpdateListener {
|
||||
val value = it.animatedValue as Int
|
||||
background.alpha = value
|
||||
}
|
||||
animators.add(this)
|
||||
}
|
||||
}
|
||||
|
||||
if (animators.isNotEmpty()) {
|
||||
animatorSet?.cancel()
|
||||
animatorSet = AnimatorSet().apply {
|
||||
duration = resources.getInteger(R.integer.app_bar_elevation_anim_duration).toLong()
|
||||
interpolator = LINEAR_INTERPOLATOR
|
||||
playTogether(*animators.toTypedArray())
|
||||
start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
statusBarForeground = MaterialShapeDrawable.createWithElevationOverlay(context)
|
||||
applyInsetter {
|
||||
type(navigationBars = true) {
|
||||
margin(horizontal = true)
|
||||
}
|
||||
type(statusBars = true) {
|
||||
padding(top = true)
|
||||
}
|
||||
ignoreVisibility(true)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LINEAR_INTERPOLATOR = LinearInterpolator()
|
||||
}
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
package eu.kanade.tachiyomi.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.coordinatorlayout.R
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.doOnLayout
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.customview.view.AbsSavedState
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import eu.kanade.tachiyomi.util.system.isTabletUi
|
||||
import eu.kanade.tachiyomi.util.view.findChild
|
||||
|
||||
/**
|
||||
* [CoordinatorLayout] with its own app bar lift state handler.
|
||||
* This parent view checks for the app bar lift state from the following:
|
||||
*
|
||||
* 1. When nested scroll detected, lift state will be decided from the nested
|
||||
* scroll target. (See [onNestedScroll])
|
||||
*
|
||||
* With those conditions, this view expects the following direct child:
|
||||
*
|
||||
* 1. An [AppBarLayout].
|
||||
*/
|
||||
class TachiyomiCoordinatorLayout @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = R.attr.coordinatorLayoutStyle,
|
||||
) : CoordinatorLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
private var appBarLayout: AppBarLayout? = null
|
||||
private var tabLayout: TabLayout? = null
|
||||
|
||||
override fun onNestedScroll(
|
||||
target: View,
|
||||
dxConsumed: Int,
|
||||
dyConsumed: Int,
|
||||
dxUnconsumed: Int,
|
||||
dyUnconsumed: Int,
|
||||
type: Int,
|
||||
consumed: IntArray,
|
||||
) {
|
||||
super.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type, consumed)
|
||||
// Disable elevation overlay when tabs are visible
|
||||
if (context.isTabletUi().not()) {
|
||||
if (target is ComposeView) {
|
||||
val scrollCondition = if (type == ViewCompat.TYPE_NON_TOUCH) {
|
||||
dyUnconsumed >= 0
|
||||
} else {
|
||||
dyConsumed != 0 || dyUnconsumed >= 0
|
||||
}
|
||||
appBarLayout?.isLifted = scrollCondition && tabLayout?.isVisible == false
|
||||
} else {
|
||||
appBarLayout?.isLifted = (dyConsumed != 0 || dyUnconsumed >= 0) && tabLayout?.isVisible == false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
appBarLayout = findChild()
|
||||
tabLayout = appBarLayout?.findChild()
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
appBarLayout = null
|
||||
tabLayout = null
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(): Parcelable? {
|
||||
val superState = super.onSaveInstanceState()
|
||||
return if (superState != null) {
|
||||
SavedState(superState).also {
|
||||
it.appBarLifted = appBarLayout?.isLifted ?: false
|
||||
}
|
||||
} else {
|
||||
superState
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRestoreInstanceState(state: Parcelable?) {
|
||||
if (state is SavedState) {
|
||||
super.onRestoreInstanceState(state.superState)
|
||||
doOnLayout {
|
||||
appBarLayout?.isLifted = state.appBarLifted
|
||||
}
|
||||
} else {
|
||||
super.onRestoreInstanceState(state)
|
||||
}
|
||||
}
|
||||
|
||||
internal class SavedState : AbsSavedState {
|
||||
var appBarLifted = false
|
||||
|
||||
constructor(superState: Parcelable) : super(superState)
|
||||
|
||||
constructor(source: Parcel, loader: ClassLoader?) : super(source, loader) {
|
||||
appBarLifted = source.readByte().toInt() == 1
|
||||
}
|
||||
|
||||
override fun writeToParcel(out: Parcel, flags: Int) {
|
||||
super.writeToParcel(out, flags)
|
||||
out.writeByte((if (appBarLifted) 1 else 0).toByte())
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val CREATOR: Parcelable.ClassLoaderCreator<SavedState> = object : Parcelable.ClassLoaderCreator<SavedState> {
|
||||
override fun createFromParcel(source: Parcel, loader: ClassLoader): SavedState {
|
||||
return SavedState(source, loader)
|
||||
}
|
||||
|
||||
override fun createFromParcel(source: Parcel): SavedState {
|
||||
return SavedState(source, null)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<SavedState> {
|
||||
return newArray(size)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
package eu.kanade.tachiyomi.widget
|
||||
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
|
||||
/**
|
||||
* [AppBarLayout.ScrollingViewBehavior] that lets the app bar overlaps the scrolling child.
|
||||
*/
|
||||
class TachiyomiScrollingViewBehavior : AppBarLayout.ScrollingViewBehavior() {
|
||||
|
||||
var shouldHeaderOverlap = false
|
||||
|
||||
override fun shouldHeaderOverlapScrollingChild(): Boolean {
|
||||
return shouldHeaderOverlap
|
||||
}
|
||||
}
|
@ -39,4 +39,4 @@ workmanager = ["work-runtime", "guava"]
|
||||
[plugins]
|
||||
application = { id = "com.android.application", version.ref = "agp_version" }
|
||||
library = { id = "com.android.library", version.ref = "agp_version" }
|
||||
test = { id = "com.android.test", version.ref = "agp_version"}
|
||||
test = { id = "com.android.test", version.ref = "agp_version" }
|
||||
|
@ -65,8 +65,6 @@ insetter = "dev.chrisbanes.insetter:insetter:0.6.1"
|
||||
cascade = "me.saket.cascade:cascade-compose:2.0.0-beta1"
|
||||
wheelpicker = "com.github.commandiron:WheelPickerCompose:1.0.11"
|
||||
|
||||
flowbinding-android = "io.github.reactivecircus.flowbinding:flowbinding-android:1.2.0"
|
||||
|
||||
logcat = "com.squareup.logcat:logcat:0.1"
|
||||
|
||||
acra-http = "ch.acra:acra-http:5.9.7"
|
||||
|
Loading…
x
Reference in New Issue
Block a user