From bcf6904363a5e67f2d193b5ae6846aefce89c78e Mon Sep 17 00:00:00 2001 From: arkon Date: Fri, 2 Dec 2022 22:48:08 -0500 Subject: [PATCH] Remove some dead code (cherry picked from commit b0dc20e00ce7c4cc33742fa3d4ae9d55503a25a4) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiAppBarLayout.kt --- app/build.gradle.kts | 3 - .../tachiyomi/ui/manga/EditMangaDialog.kt | 11 +- .../tachiyomi/ui/reader/ReaderActivity.kt | 256 ++++++++++-------- .../util/system/ContextExtensions.kt | 44 --- .../util/system/NotificationExtensions.kt | 32 +++ .../util/view/ImageViewExtensions.kt | 40 --- .../tachiyomi/util/view/ViewExtensions.kt | 41 --- .../tachiyomi/widget/TachiyomiAppBarLayout.kt | 214 --------------- .../widget/TachiyomiCoordinatorLayout.kt | 130 --------- .../widget/TachiyomiScrollingViewBehavior.kt | 15 - gradle/androidx.versions.toml | 2 +- gradle/libs.versions.toml | 2 - 12 files changed, 174 insertions(+), 616 deletions(-) delete mode 100755 app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiAppBarLayout.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiCoordinatorLayout.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiScrollingViewBehavior.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 515230dd2..93332e870 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -270,9 +270,6 @@ dependencies { implementation(libs.bundles.voyager) implementation(libs.wheelpicker) - // FlowBinding - implementation(libs.flowbinding.android) - // Logging implementation(libs.logcat) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/EditMangaDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/EditMangaDialog.kt index 27f233a52..d0f08a3c6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/EditMangaDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/EditMangaDialog.kt @@ -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, 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, scope: CoroutineScope) { } .setNegativeButton(android.R.string.cancel, null) .show() - }.launchIn(scope) + } } addView(addTagChip) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt index 355bc1098..faeb1d66d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt @@ -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 = 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 = 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? { diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt index b6edb4a0f..77f351aea 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt @@ -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 diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/NotificationExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/NotificationExtensions.kt index 70e44d645..19ef75a8b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/NotificationExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/NotificationExtensions.kt @@ -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() +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/view/ImageViewExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/view/ImageViewExtensions.kt index 3f6623cea..dfeb7281e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/view/ImageViewExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/view/ImageViewExtensions.kt @@ -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) }, - ) - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt index 66cc88503..14b0f76d3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt @@ -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 ViewGroup.findChild(): T? { - return children.find { it is T } as? T -} - -/** - * Returns this ViewGroup's first descendant of specified class - */ -inline fun 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) -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiAppBarLayout.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiAppBarLayout.kt deleted file mode 100755 index eb89e027f..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiAppBarLayout.kt +++ /dev/null @@ -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(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() - 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() - - 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() - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiCoordinatorLayout.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiCoordinatorLayout.kt deleted file mode 100644 index 2ad8326f3..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiCoordinatorLayout.kt +++ /dev/null @@ -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 = object : Parcelable.ClassLoaderCreator { - 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 { - return newArray(size) - } - } - } - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiScrollingViewBehavior.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiScrollingViewBehavior.kt deleted file mode 100644 index a9ec76cef..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiScrollingViewBehavior.kt +++ /dev/null @@ -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 - } -} diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 2b415d048..6381ba4ec 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -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" } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2e67ae215..1a5240b91 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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"