mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-30 22:07:57 +01:00 
			
		
		
		
	Edge-to-edge manga details view (#5613)
* Prepare for edge-to-edge MangaController * Fix derpy liftToScroll with our own implementation * Edge-to-edge MangaController Except when legacy blue theme is used. * Save app bar lift state for controller backstack * Fix expanded cover position after the view recycled * Handle overlap changes when incognito mode disabled * Tablet fixes * Revert "Handle overlap changes when incognito mode disabled" This reverts commit 1f492449 Breaks on rotation changes. * Fix MangaController's swipe refresh position * All controllers are now doing lift app bar on scroll by default They are already doing that before so this pretty much just a cleanups. * TachiyomiCoordinatorLayout: Support ViewPager for app bar lift state check I'm willing to revert this if this minute detail solution is deemed too hacky xD * Fix app bar not lifted when scrolled without fling * Save app bar lift state across configuration changes * Fix MangaController's swipe refresh position after configuration change * TachiyomiCoordinatorLayout: Update ViewPager reference when controller is changed
This commit is contained in:
		| @@ -7,6 +7,7 @@ import androidx.core.net.toUri | ||||
| import com.bluelinelabs.conductor.Controller | ||||
| import com.bluelinelabs.conductor.Router | ||||
| import com.bluelinelabs.conductor.RouterTransaction | ||||
| import eu.kanade.tachiyomi.ui.main.MainActivity | ||||
| import eu.kanade.tachiyomi.util.system.toast | ||||
|  | ||||
| fun Router.popControllerWithTag(tag: String): Boolean { | ||||
| @@ -41,3 +42,10 @@ fun Controller.openInBrowser(url: String) { | ||||
|         activity?.toast(e.message) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Returns [MainActivity]'s app bar height | ||||
|  */ | ||||
| fun Controller.getMainAppBarHeight(): Int { | ||||
|     return (activity as? MainActivity)?.binding?.appbar?.measuredHeight ?: 0 | ||||
| } | ||||
|   | ||||
| @@ -1,3 +1,3 @@ | ||||
| package eu.kanade.tachiyomi.ui.base.controller | ||||
| 
 | ||||
| interface NoToolbarElevationController | ||||
| interface NoAppBarElevationController | ||||
| @@ -1,3 +0,0 @@ | ||||
| package eu.kanade.tachiyomi.ui.base.controller | ||||
|  | ||||
| interface ToolbarLiftOnScrollController | ||||
| @@ -34,7 +34,6 @@ import eu.kanade.tachiyomi.source.ConfigurableSource | ||||
| import eu.kanade.tachiyomi.source.Source | ||||
| import eu.kanade.tachiyomi.source.getPreferenceKey | ||||
| import eu.kanade.tachiyomi.ui.base.controller.NucleusController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.ToolbarLiftOnScrollController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.openInBrowser | ||||
| import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction | ||||
| import eu.kanade.tachiyomi.util.preference.DSL | ||||
| @@ -49,8 +48,7 @@ import uy.kohesive.injekt.injectLazy | ||||
|  | ||||
| @SuppressLint("RestrictedApi") | ||||
| class ExtensionDetailsController(bundle: Bundle? = null) : | ||||
|     NucleusController<ExtensionDetailControllerBinding, ExtensionDetailsPresenter>(bundle), | ||||
|     ToolbarLiftOnScrollController { | ||||
|     NucleusController<ExtensionDetailControllerBinding, ExtensionDetailsPresenter>(bundle) { | ||||
|  | ||||
|     private val preferences: PreferencesHelper by injectLazy() | ||||
|  | ||||
|   | ||||
| @@ -245,12 +245,7 @@ class LibraryController( | ||||
|         } | ||||
|         tabsVisibilitySubscription?.unsubscribe() | ||||
|         tabsVisibilitySubscription = tabsVisibilityRelay.subscribe { visible -> | ||||
|             val tabAnimator = (activity as? MainActivity)?.tabAnimator | ||||
|             if (visible) { | ||||
|                 tabAnimator?.expand() | ||||
|             } else { | ||||
|                 tabAnimator?.collapse() | ||||
|             } | ||||
|             tabs.isVisible = visible | ||||
|         } | ||||
|         mangaCountVisibilitySubscription?.unsubscribe() | ||||
|         mangaCountVisibilitySubscription = mangaCountVisibilityRelay.subscribe { | ||||
|   | ||||
| @@ -34,6 +34,7 @@ import eu.kanade.tachiyomi.BuildConfig | ||||
| import eu.kanade.tachiyomi.Migrations | ||||
| import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.data.notification.NotificationReceiver | ||||
| import eu.kanade.tachiyomi.data.preference.PreferenceValues | ||||
| import eu.kanade.tachiyomi.data.preference.asImmediateFlow | ||||
| import eu.kanade.tachiyomi.data.updater.AppUpdateChecker | ||||
| import eu.kanade.tachiyomi.data.updater.AppUpdateResult | ||||
| @@ -42,10 +43,9 @@ import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi | ||||
| import eu.kanade.tachiyomi.ui.base.activity.BaseViewBindingActivity | ||||
| import eu.kanade.tachiyomi.ui.base.controller.DialogController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.FabController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.NoAppBarElevationController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.RootController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.TabbedController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.ToolbarLiftOnScrollController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction | ||||
| import eu.kanade.tachiyomi.ui.browse.BrowseController | ||||
| import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController | ||||
| @@ -61,6 +61,7 @@ import eu.kanade.tachiyomi.ui.setting.SettingsMainController | ||||
| import eu.kanade.tachiyomi.util.lang.launchIO | ||||
| import eu.kanade.tachiyomi.util.lang.launchUI | ||||
| import eu.kanade.tachiyomi.util.system.dpToPx | ||||
| import eu.kanade.tachiyomi.util.system.isTablet | ||||
| import eu.kanade.tachiyomi.util.system.toast | ||||
| import eu.kanade.tachiyomi.util.view.setNavigationBarTransparentCompat | ||||
| import eu.kanade.tachiyomi.widget.HideBottomNavigationOnScrollBehavior | ||||
| @@ -85,7 +86,6 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     lateinit var tabAnimator: ViewHeightAnimator | ||||
|     private var bottomNavAnimator: ViewHeightAnimator? = null | ||||
|  | ||||
|     private var isConfirmingExit: Boolean = false | ||||
| @@ -93,6 +93,11 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() { | ||||
|  | ||||
|     private var fixedViewsToBottom = mutableMapOf<View, AppBarLayout.OnOffsetChangedListener>() | ||||
|  | ||||
|     /** | ||||
|      * App bar lift state for backstack | ||||
|      */ | ||||
|     private val backstackLiftState = mutableMapOf<String, Boolean>() | ||||
|  | ||||
|     // To be checked by splash screen. If true then splash screen will be removed. | ||||
|     var ready = false | ||||
|  | ||||
| @@ -117,11 +122,6 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() { | ||||
|  | ||||
|         // Draw edge-to-edge | ||||
|         WindowCompat.setDecorFitsSystemWindows(window, false) | ||||
|         binding.appbar.applyInsetter { | ||||
|             type(navigationBars = true, statusBars = true) { | ||||
|                 padding(left = true, top = true, right = true) | ||||
|             } | ||||
|         } | ||||
|         binding.fabLayout.rootFab.applyInsetter { | ||||
|             type(navigationBars = true) { | ||||
|                 margin() | ||||
| @@ -140,8 +140,6 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() { | ||||
|         } | ||||
|         setSplashScreenExitAnimation(splashScreen) | ||||
|  | ||||
|         tabAnimator = ViewHeightAnimator(binding.tabs, 0L) | ||||
|  | ||||
|         if (binding.bottomNav != null) { | ||||
|             bottomNavAnimator = ViewHeightAnimator(binding.bottomNav!!) | ||||
|  | ||||
| @@ -218,7 +216,7 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() { | ||||
|                     container: ViewGroup, | ||||
|                     handler: ControllerChangeHandler | ||||
|                 ) { | ||||
|                     syncActivityViewWithController(to, from) | ||||
|                     syncActivityViewWithController(to, from, isPush) | ||||
|                 } | ||||
|  | ||||
|                 override fun onChangeCompleted( | ||||
| @@ -504,7 +502,7 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() { | ||||
|         router.setRoot(controller.withFadeTransaction().tag(id.toString())) | ||||
|     } | ||||
|  | ||||
|     private fun syncActivityViewWithController(to: Controller?, from: Controller? = null) { | ||||
|     private fun syncActivityViewWithController(to: Controller?, from: Controller? = null, isPush: Boolean = true) { | ||||
|         if (from is DialogController || to is DialogController) { | ||||
|             return | ||||
|         } | ||||
| @@ -529,12 +527,11 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() { | ||||
|             from.cleanupTabs(binding.tabs) | ||||
|         } | ||||
|         if (to is TabbedController) { | ||||
|             tabAnimator.expand() | ||||
|             to.configureTabs(binding.tabs) | ||||
|         } else { | ||||
|             tabAnimator.collapse() | ||||
|             binding.tabs.setupWithViewPager(null) | ||||
|         } | ||||
|         binding.tabs.isVisible = to is TabbedController | ||||
|  | ||||
|         if (from is FabController) { | ||||
|             binding.fabLayout.rootFab.isVisible = false | ||||
| @@ -545,16 +542,32 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() { | ||||
|             to.configureFab(binding.fabLayout.rootFab) | ||||
|         } | ||||
|  | ||||
|         when (to) { | ||||
|             is NoToolbarElevationController -> { | ||||
|                 binding.appbar.disableElevation() | ||||
|             } | ||||
|             is ToolbarLiftOnScrollController -> { | ||||
|                 binding.appbar.enableElevation(true) | ||||
|             } | ||||
|             else -> { | ||||
|                 binding.appbar.enableElevation(false) | ||||
|         if (!isTablet()) { | ||||
|             // Save lift state | ||||
|             if (isPush) { | ||||
|                 if (router.backstackSize > 1) { | ||||
|                     // Save lift state | ||||
|                     from?.let { | ||||
|                         backstackLiftState[it.instanceId] = binding.appbar.isLifted | ||||
|                     } | ||||
|                 } else { | ||||
|                     backstackLiftState.clear() | ||||
|                 } | ||||
|                 binding.appbar.isLifted = false | ||||
|             } else { | ||||
|                 to?.let { | ||||
|                     binding.appbar.isLifted = backstackLiftState.getOrElse(it.instanceId) { false } | ||||
|                 } | ||||
|                 from?.let { | ||||
|                     backstackLiftState.remove(it.instanceId) | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             binding.root.isLiftAppBarOnScroll = to !is NoAppBarElevationController | ||||
|  | ||||
|             binding.appbar.isTransparentWhenNotLifted = to is MangaController && | ||||
|                 preferences.appTheme().get() != PreferenceValues.AppTheme.BLUE | ||||
|             binding.controllerContainer.overlapHeader = to is MangaController | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -13,16 +13,21 @@ import android.view.Menu | ||||
| import android.view.MenuInflater | ||||
| import android.view.MenuItem | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import android.widget.TextView | ||||
| import androidx.annotation.FloatRange | ||||
| import androidx.appcompat.app.AppCompatActivity | ||||
| import androidx.appcompat.view.ActionMode | ||||
| import androidx.core.os.bundleOf | ||||
| import androidx.core.view.WindowInsetsCompat | ||||
| import androidx.core.view.children | ||||
| import androidx.core.view.doOnLayout | ||||
| import androidx.core.view.isVisible | ||||
| import androidx.core.view.updateLayoutParams | ||||
| import androidx.recyclerview.widget.ConcatAdapter | ||||
| import androidx.recyclerview.widget.LinearLayoutManager | ||||
| import androidx.recyclerview.widget.RecyclerView | ||||
| import androidx.swiperefreshlayout.widget.SwipeRefreshLayout | ||||
| import coil.imageLoader | ||||
| import coil.request.ImageRequest | ||||
| import com.bluelinelabs.conductor.ControllerChangeHandler | ||||
| @@ -51,7 +56,7 @@ import eu.kanade.tachiyomi.source.SourceManager | ||||
| import eu.kanade.tachiyomi.source.online.HttpSource | ||||
| import eu.kanade.tachiyomi.ui.base.controller.FabController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.NucleusController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.ToolbarLiftOnScrollController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.getMainAppBarHeight | ||||
| import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction | ||||
| import eu.kanade.tachiyomi.ui.browse.migration.search.SearchController | ||||
| import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController | ||||
| @@ -89,6 +94,7 @@ import eu.kanade.tachiyomi.util.view.snack | ||||
| import kotlinx.coroutines.flow.launchIn | ||||
| import kotlinx.coroutines.flow.onEach | ||||
| import reactivecircus.flowbinding.recyclerview.scrollEvents | ||||
| import reactivecircus.flowbinding.recyclerview.scrollStateChanges | ||||
| import reactivecircus.flowbinding.swiperefreshlayout.refreshes | ||||
| import timber.log.Timber | ||||
| import uy.kohesive.injekt.Injekt | ||||
| @@ -99,7 +105,6 @@ import kotlin.math.min | ||||
|  | ||||
| class MangaController : | ||||
|     NucleusController<MangaControllerBinding, MangaPresenter>, | ||||
|     ToolbarLiftOnScrollController, | ||||
|     FabController, | ||||
|     ActionMode.Callback, | ||||
|     FlexibleAdapter.OnItemClickListener, | ||||
| @@ -254,6 +259,37 @@ class MangaController : | ||||
|                     updateToolbarTitleAlpha() | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             it.scrollStateChanges() | ||||
|                 .onEach { _ -> | ||||
|                     // Disable swipe refresh when view is not at the top | ||||
|                     val firstPos = (it.layoutManager as LinearLayoutManager) | ||||
|                         .findFirstCompletelyVisibleItemPosition() | ||||
|                     binding.swipeRefresh.isEnabled = firstPos <= 0 | ||||
|                 } | ||||
|                 .launchIn(viewScope) | ||||
|  | ||||
|             binding.fastScroller.doOnLayout { scroller -> | ||||
|                 scroller.updateLayoutParams<ViewGroup.MarginLayoutParams> { | ||||
|                     topMargin = getMainAppBarHeight() | ||||
|                 } | ||||
|                 scroller.applyInsetter { | ||||
|                     type(navigationBars = true) { | ||||
|                         margin() | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             binding.swipeRefresh.doOnLayout { swipeRefresh -> | ||||
|                 swipeRefresh as SwipeRefreshLayout | ||||
|                 swipeRefresh.setOnApplyWindowInsetsListener { _, windowInsets -> | ||||
|                     val topStatusBarInset = WindowInsetsCompat.toWindowInsetsCompat(windowInsets) | ||||
|                         .getInsets(WindowInsetsCompat.Type.statusBars()) | ||||
|                         .top | ||||
|                     swipeRefresh.setProgressViewEndTarget(false, getMainAppBarHeight() + topStatusBarInset) | ||||
|                     windowInsets | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         // Tablet layout | ||||
|         binding.infoRecycler?.let { | ||||
|   | ||||
| @@ -3,7 +3,9 @@ package eu.kanade.tachiyomi.ui.manga.info | ||||
| import android.view.LayoutInflater | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import androidx.constraintlayout.widget.ConstraintLayout | ||||
| import androidx.core.view.isVisible | ||||
| import androidx.core.view.updateLayoutParams | ||||
| import androidx.recyclerview.widget.RecyclerView | ||||
| import coil.loadAny | ||||
| import coil.target.ImageViewTarget | ||||
| @@ -16,6 +18,7 @@ import eu.kanade.tachiyomi.source.Source | ||||
| import eu.kanade.tachiyomi.source.SourceManager | ||||
| import eu.kanade.tachiyomi.source.model.SManga | ||||
| import eu.kanade.tachiyomi.source.online.HttpSource | ||||
| import eu.kanade.tachiyomi.ui.base.controller.getMainAppBarHeight | ||||
| import eu.kanade.tachiyomi.ui.manga.MangaController | ||||
| import eu.kanade.tachiyomi.util.system.copyToClipboard | ||||
| import eu.kanade.tachiyomi.util.view.setChips | ||||
| @@ -47,6 +50,7 @@ class MangaInfoHeaderAdapter( | ||||
|  | ||||
|     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HeaderViewHolder { | ||||
|         binding = MangaInfoHeaderBinding.inflate(LayoutInflater.from(parent.context), parent, false) | ||||
|         updateCoverPosition() | ||||
|         return HeaderViewHolder(binding.root) | ||||
|     } | ||||
|  | ||||
| @@ -75,6 +79,15 @@ class MangaInfoHeaderAdapter( | ||||
|         notifyDataSetChanged() | ||||
|     } | ||||
|  | ||||
|     private fun updateCoverPosition() { | ||||
|         val appBarHeight = controller.getMainAppBarHeight() | ||||
|         binding.mangaCover.updateLayoutParams<ViewGroup.MarginLayoutParams> { | ||||
|             topMargin += appBarHeight | ||||
|         } | ||||
|         binding.root.getConstraintSet(R.id.end) | ||||
|             ?.setMargin(R.id.manga_cover, ConstraintLayout.LayoutParams.TOP, appBarHeight) | ||||
|     } | ||||
|  | ||||
|     inner class HeaderViewHolder(private val view: View) : RecyclerView.ViewHolder(view) { | ||||
|         fun bind() { | ||||
|             // For rounded corners | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import eu.kanade.tachiyomi.BuildConfig | ||||
| import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.data.updater.AppUpdateChecker | ||||
| import eu.kanade.tachiyomi.data.updater.AppUpdateResult | ||||
| import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.NoAppBarElevationController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.openInBrowser | ||||
| import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction | ||||
| import eu.kanade.tachiyomi.ui.more.licenses.LicensesController | ||||
| @@ -25,7 +25,7 @@ import java.text.SimpleDateFormat | ||||
| import java.util.Locale | ||||
| import java.util.TimeZone | ||||
|  | ||||
| class AboutController : SettingsController(), NoToolbarElevationController { | ||||
| class AboutController : SettingsController(), NoAppBarElevationController { | ||||
|  | ||||
|     private val updateChecker by lazy { AppUpdateChecker() } | ||||
|  | ||||
|   | ||||
| @@ -9,7 +9,7 @@ import androidx.preference.PreferenceScreen | ||||
| import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.data.download.DownloadManager | ||||
| import eu.kanade.tachiyomi.data.download.DownloadService | ||||
| import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.NoAppBarElevationController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.RootController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction | ||||
| import eu.kanade.tachiyomi.ui.category.CategoryController | ||||
| @@ -41,7 +41,7 @@ import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys | ||||
| class MoreController : | ||||
|     SettingsController(), | ||||
|     RootController, | ||||
|     NoToolbarElevationController { | ||||
|     NoAppBarElevationController { | ||||
|  | ||||
|     private val downloadManager: DownloadManager by injectLazy() | ||||
|     private var isDownloading: Boolean = false | ||||
|   | ||||
| @@ -9,6 +9,7 @@ import android.view.Gravity | ||||
| import android.view.Menu | ||||
| import android.view.MenuItem | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import android.widget.TextView | ||||
| import androidx.annotation.MenuRes | ||||
| import androidx.annotation.StringRes | ||||
| @@ -16,8 +17,11 @@ import androidx.appcompat.view.menu.MenuBuilder | ||||
| import androidx.appcompat.widget.PopupMenu | ||||
| import androidx.appcompat.widget.TooltipCompat | ||||
| import androidx.core.content.ContextCompat | ||||
| import androidx.core.view.children | ||||
| import androidx.core.view.descendants | ||||
| import androidx.core.view.forEach | ||||
| import androidx.recyclerview.widget.RecyclerView | ||||
| import androidx.viewpager.widget.ViewPager | ||||
| import com.google.android.material.card.MaterialCardView | ||||
| import com.google.android.material.chip.Chip | ||||
| import com.google.android.material.chip.ChipGroup | ||||
| @@ -214,3 +218,40 @@ fun RecyclerView.onAnimationsFinished(callback: (RecyclerView) -> Unit) = post( | ||||
|         } | ||||
|     } | ||||
| ) | ||||
|  | ||||
| /** | ||||
|  * 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 the active child view of a ViewPager according to the LayoutParams | ||||
|  */ | ||||
| fun ViewPager.getActivePageView(): View? { | ||||
|     if (null == adapter || adapter?.count == 0 || childCount == 0) { | ||||
|         return null | ||||
|     } | ||||
|  | ||||
|     val positionField = ViewPager.LayoutParams::class.java.getDeclaredField("position") | ||||
|     positionField.isAccessible = true | ||||
|     return children.find { child -> | ||||
|         val layoutParams = child.layoutParams as ViewPager.LayoutParams | ||||
|         try { | ||||
|             if (!layoutParams.isDecor && positionField.getInt(layoutParams) == currentItem) { | ||||
|                 return@find true | ||||
|             } | ||||
|         } catch (e: NoSuchFieldException) { | ||||
|         } catch (e: IllegalAccessException) { | ||||
|         } | ||||
|         false | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,47 +1,87 @@ | ||||
| package eu.kanade.tachiyomi.widget | ||||
|  | ||||
| import android.animation.ObjectAnimator | ||||
| import android.animation.StateListAnimator | ||||
| import android.animation.ValueAnimator | ||||
| import android.content.Context | ||||
| import android.util.AttributeSet | ||||
| import com.google.android.material.R | ||||
| import com.google.android.material.animation.AnimationUtils | ||||
| import com.google.android.material.appbar.AppBarLayout | ||||
| import com.google.android.material.appbar.MaterialToolbar | ||||
| import eu.kanade.tachiyomi.R | ||||
|  | ||||
| class ElevationAppBarLayout @JvmOverloads constructor( | ||||
|     context: Context, | ||||
|     attrs: AttributeSet? = null | ||||
| ) : AppBarLayout(context, attrs) { | ||||
|  | ||||
|     private var origStateAnimator: StateListAnimator? = null | ||||
|     private var lifted = true | ||||
|     private var transparent = false | ||||
|  | ||||
|     init { | ||||
|         origStateAnimator = stateListAnimator | ||||
|     private val toolbar by lazy { findViewById<MaterialToolbar>(R.id.toolbar) } | ||||
|  | ||||
|     private var elevationAnimator: ValueAnimator? = null | ||||
|     private var backgroundAlphaAnimator: ValueAnimator? = null | ||||
|  | ||||
|     var isTransparentWhenNotLifted = false | ||||
|         set(value) { | ||||
|             if (field != value) { | ||||
|                 field = value | ||||
|                 updateBackgroundAlpha() | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     /** | ||||
|      * Disabled. Lift on scroll is handled manually with [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 | ||||
|             val from = elevation | ||||
|             val to = if (lifted) { | ||||
|                 resources.getDimension(R.dimen.design_appbar_elevation) | ||||
|             } else { | ||||
|                 0F | ||||
|             } | ||||
|  | ||||
|             elevationAnimator?.cancel() | ||||
|             elevationAnimator = ValueAnimator.ofFloat(from, to).apply { | ||||
|                 duration = resources.getInteger(R.integer.app_bar_elevation_anim_duration).toLong() | ||||
|                 interpolator = AnimationUtils.LINEAR_INTERPOLATOR | ||||
|                 addUpdateListener { | ||||
|                     elevation = it.animatedValue as Float | ||||
|                 } | ||||
|                 start() | ||||
|             } | ||||
|  | ||||
|             updateBackgroundAlpha() | ||||
|             true | ||||
|         } else { | ||||
|             false | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun enableElevation(liftOnScroll: Boolean) { | ||||
|         setElevation(liftOnScroll) | ||||
|     } | ||||
|     private fun updateBackgroundAlpha() { | ||||
|         val newTransparent = if (lifted) false else isTransparentWhenNotLifted | ||||
|         if (transparent != newTransparent) { | ||||
|             transparent = newTransparent | ||||
|             val fromAlpha = if (transparent) 255 else 0 | ||||
|             val toAlpha = if (transparent) 0 else 255 | ||||
|  | ||||
|     private fun setElevation(liftOnScroll: Boolean) { | ||||
|         stateListAnimator = origStateAnimator | ||||
|         isLiftOnScroll = liftOnScroll | ||||
|     } | ||||
|  | ||||
|     fun disableElevation() { | ||||
|         stateListAnimator = StateListAnimator().apply { | ||||
|             val objAnimator = ObjectAnimator.ofFloat(this, "elevation", 0f) | ||||
|  | ||||
|             // Enabled and collapsible, but not collapsed means not elevated | ||||
|             addState( | ||||
|                 intArrayOf(android.R.attr.enabled, R.attr.state_collapsible, -R.attr.state_collapsed), | ||||
|                 objAnimator | ||||
|             ) | ||||
|  | ||||
|             // Default enabled state | ||||
|             addState(intArrayOf(android.R.attr.enabled), objAnimator) | ||||
|  | ||||
|             // Disabled state | ||||
|             addState(IntArray(0), objAnimator) | ||||
|             backgroundAlphaAnimator?.cancel() | ||||
|             backgroundAlphaAnimator = ValueAnimator.ofInt(fromAlpha, toAlpha).apply { | ||||
|                 duration = resources.getInteger(R.integer.app_bar_elevation_anim_duration).toLong() | ||||
|                 interpolator = AnimationUtils.LINEAR_INTERPOLATOR | ||||
|                 addUpdateListener { | ||||
|                     val alpha = it.animatedValue as Int | ||||
|                     background.alpha = alpha | ||||
|                     toolbar?.background?.alpha = alpha | ||||
|                     statusBarForeground?.alpha = alpha | ||||
|                 } | ||||
|                 start() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.widget | ||||
|  | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import androidx.viewpager.widget.ViewPager | ||||
| import com.nightlynexus.viewstatepageradapter.ViewStatePagerAdapter | ||||
| import java.util.Stack | ||||
|  | ||||
| @@ -22,7 +23,11 @@ abstract class RecyclerViewPagerAdapter : ViewStatePagerAdapter() { | ||||
|     protected open fun recycleView(view: View, position: Int) {} | ||||
|  | ||||
|     override fun createView(container: ViewGroup, position: Int): View { | ||||
|         val view = if (pool.isNotEmpty()) pool.pop() else createView(container) | ||||
|         val view = if (pool.isNotEmpty()) { | ||||
|             pool.pop().setViewPagerPositionParam(position) | ||||
|         } else { | ||||
|             createView(container) | ||||
|         } | ||||
|         bindView(view, position) | ||||
|         return view | ||||
|     } | ||||
| @@ -31,4 +36,25 @@ abstract class RecyclerViewPagerAdapter : ViewStatePagerAdapter() { | ||||
|         recycleView(view, position) | ||||
|         if (recycle) pool.push(view) | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Making sure that this ViewPager child view has the correct "position" layout param | ||||
|      * after being recycled. | ||||
|      */ | ||||
|     private fun View.setViewPagerPositionParam(position: Int): View { | ||||
|         val params = layoutParams | ||||
|         if (params is ViewPager.LayoutParams) { | ||||
|             if (!params.isDecor) { | ||||
|                 try { | ||||
|                     val positionField = ViewPager.LayoutParams::class.java.getDeclaredField("position") | ||||
|                     positionField.isAccessible = true | ||||
|                     positionField.setInt(params, position) | ||||
|                     layoutParams = params | ||||
|                 } catch (e: NoSuchFieldException) { | ||||
|                 } catch (e: IllegalAccessException) { | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return this | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,38 @@ | ||||
| package eu.kanade.tachiyomi.widget | ||||
|  | ||||
| import android.content.Context | ||||
| import android.util.AttributeSet | ||||
| import androidx.coordinatorlayout.widget.CoordinatorLayout | ||||
| import com.bluelinelabs.conductor.ChangeHandlerFrameLayout | ||||
|  | ||||
| /** | ||||
|  * [ChangeHandlerFrameLayout] with the ability to draw behind the header sibling in [CoordinatorLayout]. | ||||
|  * The layout behavior of this view is set to [TachiyomiScrollingViewBehavior] and should not be changed. | ||||
|  */ | ||||
| class TachiyomiChangeHandlerFrameLayout( | ||||
|     context: Context, | ||||
|     attrs: AttributeSet | ||||
| ) : ChangeHandlerFrameLayout(context, attrs), CoordinatorLayout.AttachedBehavior { | ||||
|  | ||||
|     /** | ||||
|      * If true, this view will draw behind the header sibling. | ||||
|      * | ||||
|      * @see TachiyomiScrollingViewBehavior.shouldHeaderOverlap | ||||
|      */ | ||||
|     var overlapHeader = false | ||||
|         set(value) { | ||||
|             if (field != value) { | ||||
|                 field = value | ||||
|                 (layoutParams as? CoordinatorLayout.LayoutParams)?.behavior = behavior.apply { | ||||
|                     shouldHeaderOverlap = value | ||||
|                 } | ||||
|                 if (!value) { | ||||
|                     // The behavior doesn't reset translationY when shouldHeaderOverlap is false | ||||
|                     translationY = 0F | ||||
|                 } | ||||
|                 forceLayout() | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     override fun getBehavior() = TachiyomiScrollingViewBehavior() | ||||
| } | ||||
| @@ -0,0 +1,177 @@ | ||||
| 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 android.view.ViewGroup | ||||
| import androidx.coordinatorlayout.R | ||||
| import androidx.coordinatorlayout.widget.CoordinatorLayout | ||||
| import androidx.core.view.doOnLayout | ||||
| import androidx.customview.view.AbsSavedState | ||||
| import androidx.lifecycle.coroutineScope | ||||
| import androidx.lifecycle.findViewTreeLifecycleOwner | ||||
| import androidx.recyclerview.widget.RecyclerView | ||||
| import androidx.viewpager.widget.ViewPager | ||||
| import com.bluelinelabs.conductor.ChangeHandlerFrameLayout | ||||
| import com.google.android.material.appbar.AppBarLayout | ||||
| import eu.kanade.tachiyomi.util.system.isTablet | ||||
| import eu.kanade.tachiyomi.util.view.findChild | ||||
| import eu.kanade.tachiyomi.util.view.findDescendant | ||||
| import eu.kanade.tachiyomi.util.view.getActivePageView | ||||
| import kotlinx.coroutines.flow.launchIn | ||||
| import kotlinx.coroutines.flow.onEach | ||||
| import reactivecircus.flowbinding.android.view.HierarchyChangeEvent | ||||
| import reactivecircus.flowbinding.android.view.hierarchyChangeEvents | ||||
|  | ||||
| /** | ||||
|  * [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]) | ||||
|  * | ||||
|  * 2. When a descendant ViewPager active page is changed and the page contains RecyclerView, | ||||
|  * lift state will be decided from the said RecyclerView. (See [pageChangeListener]) | ||||
|  * | ||||
|  * | ||||
|  * With those conditions, this view expects the following direct child: | ||||
|  * | ||||
|  * 1. An [AppBarLayout]. | ||||
|  * | ||||
|  * 2. A [ChangeHandlerFrameLayout] that contains an optional [ViewPager]. | ||||
|  */ | ||||
| class TachiyomiCoordinatorLayout @JvmOverloads constructor( | ||||
|     context: Context, | ||||
|     attrs: AttributeSet? = null, | ||||
|     defStyleAttr: Int = R.attr.coordinatorLayoutStyle | ||||
| ) : CoordinatorLayout(context, attrs, defStyleAttr) { | ||||
|  | ||||
|     /** | ||||
|      * Keep lifted state and do nothing on tablet UI | ||||
|      */ | ||||
|     private val isTablet = context.isTablet() | ||||
|  | ||||
|     private var appBarLayout: AppBarLayout? = null | ||||
|     private var viewPager: ViewPager? = null | ||||
|         set(value) { | ||||
|             field?.removeOnPageChangeListener(pageChangeListener) | ||||
|             field = value | ||||
|             field?.addOnPageChangeListener(pageChangeListener) | ||||
|         } | ||||
|  | ||||
|     private val pageChangeListener = object : ViewPager.SimpleOnPageChangeListener() { | ||||
|         override fun onPageScrollStateChanged(state: Int) { | ||||
|             // Wait until idle to make sure all the views laid out properly before checked | ||||
|             if (canLiftAppBarOnScroll && state == ViewPager.SCROLL_STATE_IDLE) { | ||||
|                 appBarLayout?.isLifted = (viewPager?.getActivePageView() as? ViewGroup) | ||||
|                     ?.findDescendant<RecyclerView>() | ||||
|                     ?.canScrollVertically(-1) ?: false | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * If true, [AppBarLayout] child will be lifted on nested scroll. | ||||
|      */ | ||||
|     var isLiftAppBarOnScroll = true | ||||
|  | ||||
|     /** | ||||
|      * Internal check | ||||
|      */ | ||||
|     private val canLiftAppBarOnScroll | ||||
|         get() = !isTablet && isLiftAppBarOnScroll | ||||
|  | ||||
|     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) | ||||
|         if (canLiftAppBarOnScroll) { | ||||
|             appBarLayout?.isLifted = dyConsumed != 0 || dyUnconsumed >= 0 | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun onAttachedToWindow() { | ||||
|         super.onAttachedToWindow() | ||||
|         appBarLayout = findChild() | ||||
|         viewPager = findChild<ChangeHandlerFrameLayout>()?.findDescendant() | ||||
|  | ||||
|         // Updates ViewPager reference when controller is changed | ||||
|         findViewTreeLifecycleOwner()?.lifecycle?.coroutineScope?.let { scope -> | ||||
|             findChild<ChangeHandlerFrameLayout>()?.hierarchyChangeEvents() | ||||
|                 ?.onEach { | ||||
|                     if (it is HierarchyChangeEvent.ChildRemoved) { | ||||
|                         viewPager = (it.parent as? ViewGroup)?.findDescendant() | ||||
|                     } | ||||
|                 } | ||||
|                 ?.launchIn(scope) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun onDetachedFromWindow() { | ||||
|         super.onDetachedFromWindow() | ||||
|         appBarLayout = null | ||||
|         viewPager = 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) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,15 @@ | ||||
| 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 | ||||
|     } | ||||
| } | ||||
| @@ -1,5 +1,5 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
| <eu.kanade.tachiyomi.widget.TachiyomiCoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:id="@+id/root_coordinator" | ||||
| @@ -15,6 +15,7 @@ | ||||
|             android:id="@+id/appbar" | ||||
|             android:layout_width="0dp" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:fitsSystemWindows="true" | ||||
|             app:layout_constraintEnd_toEndOf="parent" | ||||
|             app:layout_constraintStart_toStartOf="parent" | ||||
|             app:layout_constraintTop_toTopOf="parent"> | ||||
| @@ -88,7 +89,7 @@ | ||||
|             app:layout_constraintStart_toEndOf="@+id/side_nav" | ||||
|             app:layout_constraintTop_toBottomOf="@+id/incognito_mode" /> | ||||
|  | ||||
|         <com.bluelinelabs.conductor.ChangeHandlerFrameLayout | ||||
|         <eu.kanade.tachiyomi.widget.TachiyomiChangeHandlerFrameLayout | ||||
|             android:id="@+id/controller_container" | ||||
|             android:layout_width="0dp" | ||||
|             android:layout_height="0dp" | ||||
| @@ -103,4 +104,4 @@ | ||||
|         android:id="@+id/fab_layout" | ||||
|         layout="@layout/main_activity_fab" /> | ||||
|  | ||||
| </androidx.coordinatorlayout.widget.CoordinatorLayout> | ||||
| </eu.kanade.tachiyomi.widget.TachiyomiCoordinatorLayout> | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
| <eu.kanade.tachiyomi.widget.TachiyomiCoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:id="@+id/root_coordinator" | ||||
| @@ -7,11 +7,18 @@ | ||||
|     android:layout_height="match_parent" | ||||
|     android:orientation="vertical"> | ||||
|  | ||||
|     <eu.kanade.tachiyomi.widget.TachiyomiChangeHandlerFrameLayout | ||||
|         android:id="@+id/controller_container" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent" /> | ||||
|  | ||||
|     <eu.kanade.tachiyomi.widget.ElevationAppBarLayout | ||||
|         android:id="@+id/appbar" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:fitsSystemWindows="true"> | ||||
|         android:fitsSystemWindows="true" | ||||
|         app:elevation="0dp" | ||||
|         app:statusBarForeground="?attr/colorToolbar"> | ||||
|  | ||||
|         <com.google.android.material.appbar.MaterialToolbar | ||||
|             android:id="@+id/toolbar" | ||||
| @@ -23,7 +30,8 @@ | ||||
|         <com.google.android.material.tabs.TabLayout | ||||
|             android:id="@+id/tabs" | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="wrap_content" /> | ||||
|             android:layout_height="wrap_content" | ||||
|             android:visibility="gone" /> | ||||
|  | ||||
|         <FrameLayout | ||||
|             android:id="@+id/downloaded_only" | ||||
| @@ -63,12 +71,6 @@ | ||||
|  | ||||
|     </eu.kanade.tachiyomi.widget.ElevationAppBarLayout> | ||||
|  | ||||
|     <com.bluelinelabs.conductor.ChangeHandlerFrameLayout | ||||
|         android:id="@+id/controller_container" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent" | ||||
|         app:layout_behavior="@string/appbar_scrolling_view_behavior" /> | ||||
|  | ||||
|     <include | ||||
|         android:id="@+id/fab_layout" | ||||
|         layout="@layout/main_activity_fab" /> | ||||
| @@ -83,4 +85,4 @@ | ||||
|         app:menu="@menu/main_nav" | ||||
|         tools:ignore="KeyboardInaccessibleWidget" /> | ||||
|  | ||||
| </androidx.coordinatorlayout.widget.CoordinatorLayout> | ||||
| </eu.kanade.tachiyomi.widget.TachiyomiCoordinatorLayout> | ||||
|   | ||||
| @@ -25,17 +25,18 @@ | ||||
|     <View | ||||
|         android:id="@+id/backdrop_overlay" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="160dp" | ||||
|         android:layout_height="0dp" | ||||
|         android:background="@drawable/manga_info_gradient" | ||||
|         android:backgroundTint="?android:attr/colorBackground" | ||||
|         app:layout_constraintBottom_toBottomOf="@+id/backdrop" /> | ||||
|         app:layout_constraintBottom_toBottomOf="@+id/backdrop" | ||||
|         app:layout_constraintTop_toTopOf="parent" /> | ||||
|  | ||||
|     <ImageView | ||||
|         android:id="@+id/manga_cover" | ||||
|         android:layout_width="100dp" | ||||
|         android:layout_height="0dp" | ||||
|         android:layout_marginStart="16dp" | ||||
|         android:layout_marginTop="48dp" | ||||
|         android:layout_marginTop="8dp" | ||||
|         android:background="@drawable/rounded_rectangle" | ||||
|         android:contentDescription="@string/description_cover" | ||||
|         android:maxWidth="100dp" | ||||
|   | ||||
| @@ -36,7 +36,7 @@ | ||||
|  | ||||
|         <!-- Themes --> | ||||
|         <item name="android:windowLightStatusBar">@bool/lightStatusBar</item> | ||||
|         <item name="android:statusBarColor">?attr/colorSurface</item> | ||||
|         <item name="android:statusBarColor">@android:color/transparent</item> | ||||
|         <item name="android:navigationBarColor">@color/surface_amoled</item> | ||||
|         <item name="android:navigationBarDividerColor" tools:targetApi="o_mr1">@null</item> | ||||
|         <item name="android:enforceNavigationBarContrast" tools:targetApi="Q">false</item> | ||||
| @@ -186,7 +186,6 @@ | ||||
|         <!-- Status/Navigation bar --> | ||||
|         <item name="android:windowLightStatusBar" tools:targetApi="m">?attr/lightSystemBarsOnPrimary</item> | ||||
|         <item name="android:windowLightNavigationBar" tools:targetApi="o_mr1">?attr/lightSystemBarsOnPrimary</item> | ||||
|         <item name="android:statusBarColor">?attr/colorPrimary</item> | ||||
|         <item name="android:navigationBarColor">?attr/colorPrimary</item> | ||||
|     </style> | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user