diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 1c385eabd5..95b37f1d41 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -26,6 +26,7 @@ import android.webkit.WebView import androidx.annotation.IdRes import androidx.appcompat.graphics.drawable.DrawerArrowDrawable import androidx.appcompat.widget.Toolbar +import androidx.core.animation.addListener import androidx.core.content.ContextCompat import androidx.core.graphics.ColorUtils import androidx.core.net.toUri @@ -79,6 +80,7 @@ import eu.kanade.tachiyomi.util.system.hasSideNavBar import eu.kanade.tachiyomi.util.system.isBottomTappable import eu.kanade.tachiyomi.util.system.launchUI import eu.kanade.tachiyomi.util.system.toast +import eu.kanade.tachiyomi.util.view.blurBehindWindow import eu.kanade.tachiyomi.util.view.doOnApplyWindowInsets import eu.kanade.tachiyomi.util.view.getItemView import eu.kanade.tachiyomi.util.view.snack @@ -750,9 +752,12 @@ open class MainActivity : BaseActivity(), DownloadServiceLi if (overflowDialog != null) return false val overflowDialog = OverflowDialog(this) this.overflowDialog = overflowDialog - overflowDialog.setOnDismissListener { - this.overflowDialog = null - } + overflowDialog.blurBehindWindow( + window, + onDismiss = { + this.overflowDialog = null + } + ) overflowDialog.show() } else -> return super.onOptionsItemSelected(item) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/OverflowDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/OverflowDialog.kt index 2953c0d5c8..2d34725ea7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/OverflowDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/OverflowDialog.kt @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.main import android.app.Dialog import android.graphics.Color +import android.os.Build import android.view.View import android.view.ViewGroup import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat @@ -25,7 +26,7 @@ class OverflowDialog(activity: MainActivity) : Dialog(activity, R.style.Overflow setContentView(binding.root) binding.touchOutside.setOnClickListener { - dismiss() + cancel() } val incogText = context.getString(R.string.incognito_mode) with(binding.incognitoModeItem) { @@ -83,7 +84,7 @@ class OverflowDialog(activity: MainActivity) : Dialog(activity, R.style.Overflow window.decorView.fitsSystemWindows = true window.decorView.systemUiVisibility = window.decorView.systemUiVisibility .rem(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { window.decorView.systemUiVisibility = window.decorView.systemUiVisibility .rem(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt index 8d99ca895a..120e3162bd 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt @@ -13,6 +13,8 @@ import android.content.Intent import android.content.res.Configuration import android.graphics.Color import android.graphics.Rect +import android.graphics.RenderEffect +import android.graphics.Shader import android.graphics.drawable.BitmapDrawable import android.os.Build import android.os.Bundle @@ -1465,9 +1467,11 @@ class MangaDetailsController : rightMargin = defMargin bottomMargin = defMargin + binding.recycler.paddingBottom } - val shortAnimationDuration = resources?.getInteger( - android.R.integer.config_shortAnimTime - ) ?: 0 + val shortAnimationDuration = ( + resources?.getInteger( + android.R.integer.config_shortAnimTime + ) ?: 0 + ).toLong() // TransitionSet for the full cover because using animation for this SUCKS val transitionSet = TransitionSet() @@ -1475,7 +1479,7 @@ class MangaDetailsController : transitionSet.addTransition(bound) val changeImageTransform = ChangeImageTransform() transitionSet.addTransition(changeImageTransform) - transitionSet.duration = shortAnimationDuration.toLong() + transitionSet.duration = shortAnimationDuration TransitionManager.beginDelayedTransition(binding.frameLayout, transitionSet) // AnimationSet for backdrop because idk how to use TransitionSet @@ -1483,7 +1487,18 @@ class MangaDetailsController : play( ObjectAnimator.ofFloat(fullBackdrop, View.ALPHA, 0f, 0.5f) ) - duration = shortAnimationDuration.toLong() + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + binding.swipeRefresh.setRenderEffect( + RenderEffect.createBlurEffect( + 15f, + 15f, + Shader.TileMode.MIRROR + ) + ) + } + + duration = shortAnimationDuration interpolator = DecelerateInterpolator() addListener( object : AnimatorListenerAdapter() { @@ -1524,13 +1539,16 @@ class MangaDetailsController : transitionSet2.addTransition(bound2) val changeImageTransform2 = ChangeImageTransform() transitionSet2.addTransition(changeImageTransform2) - transitionSet2.duration = shortAnimationDuration.toLong() + transitionSet2.duration = shortAnimationDuration TransitionManager.beginDelayedTransition(binding.frameLayout, transitionSet2) // Animation to remove backdrop and hide the full cover currentAnimator = AnimatorSet().apply { play(ObjectAnimator.ofFloat(fullBackdrop, View.ALPHA, 0f)) - duration = shortAnimationDuration.toLong() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + binding.swipeRefresh.setRenderEffect(null) + } + duration = shortAnimationDuration interpolator = DecelerateInterpolator() if (!activity.isInNightMode()) { 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 628fd3fdb6..fe27bd0103 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 @@ -2,32 +2,42 @@ package eu.kanade.tachiyomi.util.view +import android.animation.ValueAnimator import android.annotation.SuppressLint import android.app.Activity +import android.app.Dialog +import android.content.DialogInterface import android.content.res.ColorStateList import android.graphics.Color import android.graphics.Point +import android.graphics.RenderEffect +import android.graphics.Shader import android.os.Build import android.view.Gravity import android.view.MenuItem import android.view.View import android.view.ViewGroup import android.view.ViewTreeObserver +import android.view.Window import android.view.WindowInsets import android.widget.Button import android.widget.FrameLayout import android.widget.TextView import androidx.annotation.ColorRes +import androidx.annotation.FloatRange import androidx.annotation.IdRes import androidx.annotation.Px +import androidx.annotation.RequiresApi import androidx.appcompat.view.menu.MenuBuilder import androidx.appcompat.widget.PopupMenu +import androidx.core.animation.addListener import androidx.core.content.ContextCompat import androidx.core.graphics.ColorUtils import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsAnimationCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.forEach +import androidx.interpolator.view.animation.FastOutLinearInInterpolator import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearSmoothScroller import androidx.recyclerview.widget.RecyclerView @@ -45,7 +55,6 @@ import eu.kanade.tachiyomi.util.system.contextCompatColor import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.pxToDp import eu.kanade.tachiyomi.widget.AutofitRecyclerView -import uy.kohesive.injekt.api.get import kotlin.math.max import kotlin.math.pow import kotlin.math.roundToInt @@ -412,3 +421,54 @@ inline fun View.popupMenu( popup.show() return popup } + +fun Dialog.blurBehindWindow( + window: Window?, + blurAmount: Float = 20f, + onShow: DialogInterface.OnShowListener? = null, + onDismiss: DialogInterface.OnDismissListener? = null, + onCancel: DialogInterface.OnCancelListener? = null +) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + setOnShowListener { + onShow?.onShow(it) + window?.decorView?.animateBlur(1f, blurAmount, 50)?.start() + } + setOnDismissListener { + onDismiss?.onDismiss(it) + window?.decorView?.animateBlur(blurAmount, 1f, 50, true)?.start() + } + setOnCancelListener { + onCancel?.onCancel(it) + window?.decorView?.animateBlur(blurAmount, 1f, 50, true)?.start() + } + } +} + +@RequiresApi(31) +fun View.animateBlur( + @FloatRange(from = 0.1) from: Float, + @FloatRange(from = 0.1) to: Float, + duration: Long, + removeBlurAtEnd: Boolean = false +): ValueAnimator { + return ValueAnimator.ofFloat(from, to).apply { + interpolator = FastOutLinearInInterpolator() + this.duration = duration + addUpdateListener { animator -> + val amount = animator.animatedValue as Float + try { + setRenderEffect( + RenderEffect.createBlurEffect(amount, amount, Shader.TileMode.CLAMP) + ) + } catch (_: Exception) {} + } + if (removeBlurAtEnd) { + addListener( + onEnd = { + setRenderEffect(null) + } + ) + } + } +}