Bottom sheet header bar view

This commit is contained in:
Koitharu
2022-07-24 18:36:35 +03:00
parent 802448cb5a
commit 36634414bc
21 changed files with 321 additions and 302 deletions

View File

@@ -0,0 +1,187 @@
package org.koitharu.kotatsu.base.ui.widgets
import android.animation.LayoutTransition
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.AttrRes
import androidx.appcompat.widget.Toolbar
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.withStyledAttributes
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isGone
import com.google.android.material.R as materialR
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.bottomsheet.BottomSheetBehavior
import java.util.*
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.databinding.LayoutSheetHeaderBinding
import org.koitharu.kotatsu.utils.ext.getAnimationDuration
import org.koitharu.kotatsu.utils.ext.getThemeDrawable
import org.koitharu.kotatsu.utils.ext.parents
class BottomSheetHeaderBar @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
@AttrRes defStyleAttr: Int = materialR.attr.appBarLayoutStyle,
) : AppBarLayout(context, attrs, defStyleAttr) {
private val binding = LayoutSheetHeaderBinding.inflate(LayoutInflater.from(context), this)
private val closeDrawable = context.getThemeDrawable(materialR.attr.actionModeCloseDrawable)
private val bottomSheetCallback = Callback()
private var bottomSheetBehavior: BottomSheetBehavior<*>? = null
private val locationBuffer = IntArray(2)
private val expansionListeners = LinkedList<OnExpansionChangeListener>()
val toolbar: MaterialToolbar
get() = binding.toolbar
init {
setBackgroundResource(R.drawable.sheet_toolbar_background)
layoutTransition = LayoutTransition().apply {
setDuration(context.getAnimationDuration(R.integer.config_tinyAnimTime))
}
context.withStyledAttributes(attrs, R.styleable.BottomSheetHeaderBar, defStyleAttr) {
binding.toolbar.title = getString(R.styleable.BottomSheetHeaderBar_title)
val menuResId = getResourceId(R.styleable.BottomSheetHeaderBar_menu, 0)
if (menuResId != 0) {
binding.toolbar.inflateMenu(menuResId)
}
}
binding.toolbar.setNavigationOnClickListener(bottomSheetCallback)
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
setBottomSheetBehavior(findParentBottomSheetBehavior())
}
override fun onDetachedFromWindow() {
setBottomSheetBehavior(null)
super.onDetachedFromWindow()
}
override fun addView(child: View?, index: Int) {
if (shouldAddView(child)) {
super.addView(child, index)
} else {
binding.toolbar.addView(child, index)
}
}
override fun addView(child: View?, width: Int, height: Int) {
if (shouldAddView(child)) {
super.addView(child, width, height)
} else {
binding.toolbar.addView(child, width, height)
}
}
override fun addView(child: View?, index: Int, params: ViewGroup.LayoutParams?) {
if (shouldAddView(child)) {
super.addView(child, index, params)
} else {
binding.toolbar.addView(child, index, convertLayoutParams(params))
}
}
fun setNavigationOnClickListener(onClickListener: OnClickListener) {
binding.toolbar.setNavigationOnClickListener(onClickListener)
}
fun addOnExpansionChangeListener(listener: OnExpansionChangeListener) {
expansionListeners.add(listener)
}
fun removeOnExpansionChangeListener(listener: OnExpansionChangeListener) {
expansionListeners.remove(listener)
}
private fun setBottomSheetBehavior(behavior: BottomSheetBehavior<*>?) {
bottomSheetBehavior?.removeBottomSheetCallback(bottomSheetCallback)
bottomSheetBehavior = behavior
if (behavior != null) {
onBottomSheetStateChanged(behavior.state)
behavior.addBottomSheetCallback(bottomSheetCallback)
}
}
private fun onBottomSheetStateChanged(newState: Int) {
val isExpanded = newState == BottomSheetBehavior.STATE_EXPANDED && isOnTopOfScreen()
if (isExpanded == binding.dragHandle.isGone) {
return
}
binding.toolbar.navigationIcon = (if (isExpanded) closeDrawable else null)
binding.dragHandle.isGone = isExpanded
expansionListeners.forEach { it.onExpansionStateChanged(this, isExpanded) }
}
private fun findParentBottomSheetBehavior(): BottomSheetBehavior<*>? {
for (p in parents) {
val layoutParams = (p as? View)?.layoutParams
if (layoutParams is CoordinatorLayout.LayoutParams) {
val behavior = layoutParams.behavior
if (behavior is BottomSheetBehavior<*>) {
return behavior
}
}
}
return null
}
private fun isOnTopOfScreen(): Boolean {
getLocationInWindow(locationBuffer)
val topInset = ViewCompat.getRootWindowInsets(this)
?.getInsets(WindowInsetsCompat.Type.systemBars())?.top ?: 0
val zeroTop = (layoutParams as? MarginLayoutParams)?.topMargin ?: 0
return (locationBuffer[1] - topInset) <= zeroTop
}
private fun dismissBottomSheet() {
bottomSheetBehavior?.state = BottomSheetBehavior.STATE_HIDDEN
}
private fun shouldAddView(child: View?): Boolean {
if (child == null) {
return true
}
val viewId = child.id
return viewId == R.id.dragHandle || viewId == R.id.toolbar
}
private fun convertLayoutParams(params: ViewGroup.LayoutParams?): Toolbar.LayoutParams? {
return when (params) {
null -> null
is MarginLayoutParams -> {
val lp = Toolbar.LayoutParams(params)
if (params is LayoutParams) {
lp.gravity = params.gravity
}
lp
}
else -> Toolbar.LayoutParams(params)
}
}
private inner class Callback : BottomSheetBehavior.BottomSheetCallback(), View.OnClickListener {
override fun onStateChanged(bottomSheet: View, newState: Int) {
onBottomSheetStateChanged(newState)
}
override fun onSlide(bottomSheet: View, slideOffset: Float) = Unit
override fun onClick(v: View?) {
dismissBottomSheet()
}
}
fun interface OnExpansionChangeListener {
fun onExpansionStateChanged(headerBar: BottomSheetHeaderBar, isExpanded: Boolean)
}
}

View File

@@ -44,7 +44,7 @@ class FavouriteCategoriesBottomSheet :
adapter = MangaCategoriesAdapter(this)
binding.recyclerViewCategories.adapter = adapter
binding.buttonDone.setOnClickListener(this)
binding.toolbar.setOnMenuItemClickListener(this)
binding.headerBar.toolbar.setOnMenuItemClickListener(this)
viewModel.content.observe(viewLifecycleOwner, this::onContentChanged)
viewModel.onError.observe(viewLifecycleOwner, ::onError)

View File

@@ -12,7 +12,6 @@ import org.koitharu.kotatsu.base.ui.BaseBottomSheet
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.databinding.SheetBaseBinding
import org.koitharu.kotatsu.utils.BottomSheetToolbarController
class LibraryCategoriesConfigSheet :
BaseBottomSheet<SheetBaseBinding>(),
@@ -27,14 +26,9 @@ class LibraryCategoriesConfigSheet :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.toolbar.setNavigationOnClickListener { dismiss() }
binding.toolbar.setTitle(R.string.favourites_categories)
binding.headerBar.toolbar.setTitle(R.string.favourites_categories)
binding.buttonDone.isVisible = true
binding.buttonDone.setOnClickListener(this)
behavior?.addBottomSheetCallback(BottomSheetToolbarController(binding.toolbar))
if (!resources.getBoolean(R.bool.is_tablet)) {
binding.toolbar.navigationIcon = null
}
val adapter = LibraryCategoriesConfigAdapter(this)
binding.recyclerView.adapter = adapter

View File

@@ -11,7 +11,6 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseBottomSheet
import org.koitharu.kotatsu.databinding.SheetFilterBinding
import org.koitharu.kotatsu.remotelist.ui.RemoteListViewModel
import org.koitharu.kotatsu.utils.BottomSheetToolbarController
class FilterBottomSheet :
BaseBottomSheet<SheetFilterBinding>(),
@@ -20,7 +19,7 @@ class FilterBottomSheet :
DialogInterface.OnKeyListener {
private val viewModel by sharedViewModel<RemoteListViewModel>(
owner = { requireParentFragment() }
owner = { requireParentFragment() },
)
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
@@ -35,11 +34,6 @@ class FilterBottomSheet :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.toolbar.setNavigationOnClickListener { dismiss() }
behavior?.addBottomSheetCallback(BottomSheetToolbarController(binding.toolbar))
if (!resources.getBoolean(R.bool.is_tablet)) {
binding.toolbar.navigationIcon = null
}
val adapter = FilterAdapter(viewModel)
binding.recyclerView.adapter = adapter
viewModel.filterItems.observe(viewLifecycleOwner, adapter::setItems)
@@ -67,7 +61,7 @@ class FilterBottomSheet :
override fun onKey(dialog: DialogInterface?, keyCode: Int, event: KeyEvent?): Boolean {
if (keyCode == KeyEvent.KEYCODE_BACK) {
val menuItem = binding.toolbar.menu.findItem(R.id.action_search) ?: return false
val menuItem = binding.headerBar.toolbar.menu.findItem(R.id.action_search) ?: return false
if (menuItem.isActionViewExpanded) {
if (event?.action == KeyEvent.ACTION_UP) {
menuItem.collapseActionView()
@@ -79,8 +73,8 @@ class FilterBottomSheet :
}
private fun initOptionsMenu() {
binding.toolbar.inflateMenu(R.menu.opt_filter)
val searchMenuItem = binding.toolbar.menu.findItem(R.id.action_search)
binding.headerBar.toolbar.inflateMenu(R.menu.opt_filter)
val searchMenuItem = binding.headerBar.toolbar.menu.findItem(R.id.action_search)
searchMenuItem.setOnActionExpandListener(this)
val searchView = searchMenuItem.actionView as SearchView
searchView.setOnQueryTextListener(this)
@@ -94,4 +88,4 @@ class FilterBottomSheet :
fun show(fm: FragmentManager) = FilterBottomSheet().show(fm, TAG)
}
}
}

View File

@@ -5,6 +5,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.FragmentManager
import kotlin.math.roundToInt
import org.koin.android.ext.android.get
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseBottomSheet
@@ -16,10 +17,8 @@ import org.koitharu.kotatsu.details.ui.adapter.ChaptersAdapter
import org.koitharu.kotatsu.details.ui.model.ChapterListItem
import org.koitharu.kotatsu.details.ui.model.toListItem
import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.utils.BottomSheetToolbarController
import org.koitharu.kotatsu.utils.RecyclerViewScrollCallback
import org.koitharu.kotatsu.utils.ext.withArgs
import kotlin.math.roundToInt
class ChaptersBottomSheet : BaseBottomSheet<SheetChaptersBinding>(), OnListItemClickListener<ChapterListItem> {
@@ -29,11 +28,6 @@ class ChaptersBottomSheet : BaseBottomSheet<SheetChaptersBinding>(), OnListItemC
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.toolbar.setNavigationOnClickListener { dismiss() }
behavior?.addBottomSheetCallback(BottomSheetToolbarController(binding.toolbar))
if (!resources.getBoolean(R.bool.is_tablet)) {
binding.toolbar.navigationIcon = null
}
val chapters = arguments?.getParcelable<ParcelableMangaChapters>(ARG_CHAPTERS)?.chapters
if (chapters.isNullOrEmpty()) {
dismissAllowingStateLoss()
@@ -90,9 +84,5 @@ class ChaptersBottomSheet : BaseBottomSheet<SheetChaptersBinding>(), OnListItemC
putParcelable(ARG_CHAPTERS, ParcelableMangaChapters(chapters))
putLong(ARG_CURRENT_ID, currentId)
}.show(fm, TAG)
private fun <T> List<T>.asArrayList(): ArrayList<T> {
return this as? ArrayList<T> ?: ArrayList(this)
}
}
}
}

View File

@@ -20,14 +20,15 @@ import org.koitharu.kotatsu.databinding.SheetReaderConfigBinding
import org.koitharu.kotatsu.reader.ui.PageSaveContract
import org.koitharu.kotatsu.reader.ui.ReaderViewModel
import org.koitharu.kotatsu.settings.SettingsActivity
import org.koitharu.kotatsu.utils.BottomSheetToolbarController
import org.koitharu.kotatsu.utils.ScreenOrientationHelper
import org.koitharu.kotatsu.utils.ext.viewLifecycleScope
import org.koitharu.kotatsu.utils.ext.withArgs
class ReaderConfigBottomSheet : BaseBottomSheet<SheetReaderConfigBinding>(),
class ReaderConfigBottomSheet :
BaseBottomSheet<SheetReaderConfigBinding>(),
CheckableButtonGroup.OnCheckedChangeListener,
ActivityResultCallback<Uri?>, View.OnClickListener {
ActivityResultCallback<Uri?>,
View.OnClickListener {
private val viewModel by sharedViewModel<ReaderViewModel>()
private val savePageRequest = registerForActivityResult(PageSaveContract(), this)
@@ -48,11 +49,6 @@ class ReaderConfigBottomSheet : BaseBottomSheet<SheetReaderConfigBinding>(),
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
observeScreenOrientation()
binding.toolbar.setNavigationOnClickListener { dismiss() }
behavior?.addBottomSheetCallback(BottomSheetToolbarController(binding.toolbar))
if (!resources.getBoolean(R.bool.is_tablet)) {
binding.toolbar.navigationIcon = null
}
binding.buttonStandard.isChecked = mode == ReaderMode.STANDARD
binding.buttonReversed.isChecked = mode == ReaderMode.REVERSED
binding.buttonWebtoon.isChecked = mode == ReaderMode.WEBTOON
@@ -61,7 +57,6 @@ class ReaderConfigBottomSheet : BaseBottomSheet<SheetReaderConfigBinding>(),
binding.buttonSavePage.setOnClickListener(this)
binding.buttonScreenRotate.setOnClickListener(this)
binding.buttonSettings.setOnClickListener(this)
}
override fun onClick(v: View) {

View File

@@ -4,16 +4,15 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.bottomsheet.BottomSheetBehavior
import org.koin.android.ext.android.get
import org.koin.androidx.viewmodel.ext.android.getViewModel
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseBottomSheet
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.base.ui.list.decor.SpacingItemDecoration
import org.koitharu.kotatsu.base.ui.widgets.BottomSheetHeaderBar
import org.koitharu.kotatsu.core.model.parcelable.ParcelableMangaPages
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.prefs.AppSettings
@@ -24,13 +23,13 @@ import org.koitharu.kotatsu.reader.domain.PageLoader
import org.koitharu.kotatsu.reader.ui.ReaderActivity
import org.koitharu.kotatsu.reader.ui.ReaderViewModel
import org.koitharu.kotatsu.reader.ui.thumbnails.adapter.PageThumbnailAdapter
import org.koitharu.kotatsu.utils.BottomSheetToolbarController
import org.koitharu.kotatsu.utils.ext.viewLifecycleScope
import org.koitharu.kotatsu.utils.ext.withArgs
class PagesThumbnailsSheet :
BaseBottomSheet<SheetPagesBinding>(),
OnListItemClickListener<MangaPage> {
OnListItemClickListener<MangaPage>,
BottomSheetHeaderBar.OnExpansionChangeListener {
private lateinit var thumbnails: List<PageThumbnail>
private var spanResolver: MangaListSpanResolver? = null
@@ -64,16 +63,10 @@ class PagesThumbnailsSheet :
super.onViewCreated(view, savedInstanceState)
val title = arguments?.getString(ARG_TITLE)
binding.toolbar.title = title
binding.toolbar.setNavigationOnClickListener { dismiss() }
binding.toolbar.subtitle = null
behavior?.addBottomSheetCallback(ToolbarController(binding.toolbar))
if (!resources.getBoolean(R.bool.is_tablet)) {
binding.toolbar.navigationIcon = null
} else {
binding.toolbar.subtitle =
resources.getQuantityString(R.plurals.pages, thumbnails.size, thumbnails.size)
with(binding.headerBar) {
toolbar.title = title
toolbar.subtitle = null
addOnExpansionChangeListener(this@PagesThumbnailsSheet)
}
with(binding.recyclerView) {
@@ -113,26 +106,23 @@ class PagesThumbnailsSheet :
}
}
override fun onExpansionStateChanged(headerBar: BottomSheetHeaderBar, isExpanded: Boolean) {
if (isExpanded) {
headerBar.toolbar.subtitle = resources.getQuantityString(
R.plurals.pages,
thumbnails.size,
thumbnails.size,
)
} else {
headerBar.toolbar.subtitle = null
}
}
private fun getPageLoader(): PageLoader {
val viewModel = (activity as? ReaderActivity)?.getViewModel<ReaderViewModel>()
return viewModel?.pageLoader ?: PageLoader().also { pageLoader = it }
}
private inner class ToolbarController(toolbar: Toolbar) : BottomSheetToolbarController(toolbar) {
override fun onStateChanged(bottomSheet: View, newState: Int) {
super.onStateChanged(bottomSheet, newState)
if (newState == BottomSheetBehavior.STATE_EXPANDED) {
toolbar.subtitle = resources.getQuantityString(
R.plurals.pages,
thumbnails.size,
thumbnails.size,
)
} else {
toolbar.subtitle = null
}
}
}
companion object {
private const val ARG_PAGES = "pages"

View File

@@ -21,7 +21,6 @@ import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerManga
import org.koitharu.kotatsu.scrobbling.ui.selector.adapter.ShikiMangaSelectionDecoration
import org.koitharu.kotatsu.scrobbling.ui.selector.adapter.ShikimoriSelectorAdapter
import org.koitharu.kotatsu.utils.BottomSheetToolbarController
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.withArgs
@@ -50,8 +49,6 @@ class ScrobblingSelectorBottomSheet :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.toolbar.setNavigationOnClickListener { dismiss() }
addBottomSheetCallback(BottomSheetToolbarController(binding.toolbar))
val listAdapter = ShikimoriSelectorAdapter(viewLifecycleOwner, get(), this)
val decoration = ShikiMangaSelectionDecoration(view.context)
with(binding.recyclerView) {
@@ -72,7 +69,7 @@ class ScrobblingSelectorBottomSheet :
dismiss()
}
viewModel.searchQuery.observe(viewLifecycleOwner) {
binding.toolbar.subtitle = it
binding.headerBar.toolbar.subtitle = it
}
}
@@ -107,7 +104,7 @@ class ScrobblingSelectorBottomSheet :
return false
}
viewModel.search(query)
binding.toolbar.menu.findItem(R.id.action_search)?.collapseActionView()
binding.headerBar.toolbar.menu.findItem(R.id.action_search)?.collapseActionView()
return true
}
@@ -115,7 +112,7 @@ class ScrobblingSelectorBottomSheet :
override fun onKey(dialog: DialogInterface?, keyCode: Int, event: KeyEvent?): Boolean {
if (keyCode == KeyEvent.KEYCODE_BACK) {
val menuItem = binding.toolbar.menu.findItem(R.id.action_search) ?: return false
val menuItem = binding.headerBar.toolbar.menu.findItem(R.id.action_search) ?: return false
if (menuItem.isActionViewExpanded) {
if (event?.action == KeyEvent.ACTION_UP) {
menuItem.collapseActionView()
@@ -134,8 +131,8 @@ class ScrobblingSelectorBottomSheet :
}
private fun initOptionsMenu() {
binding.toolbar.inflateMenu(R.menu.opt_shiki_selector)
val searchMenuItem = binding.toolbar.menu.findItem(R.id.action_search)
binding.headerBar.toolbar.inflateMenu(R.menu.opt_shiki_selector)
val searchMenuItem = binding.headerBar.toolbar.menu.findItem(R.id.action_search)
searchMenuItem.setOnActionExpandListener(this)
val searchView = searchMenuItem.actionView as SearchView
searchView.setOnQueryTextListener(this)
@@ -152,4 +149,4 @@ class ScrobblingSelectorBottomSheet :
putParcelable(MangaIntent.KEY_MANGA, ParcelableManga(manga, withChapters = false))
}.show(fm, TAG)
}
}
}

View File

@@ -1,22 +0,0 @@
package org.koitharu.kotatsu.utils
import android.view.View
import androidx.appcompat.widget.Toolbar
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.R as materialR
open class BottomSheetToolbarController(
protected val toolbar: Toolbar,
) : BottomSheetBehavior.BottomSheetCallback() {
override fun onStateChanged(bottomSheet: View, newState: Int) {
val isExpanded = newState == BottomSheetBehavior.STATE_EXPANDED && bottomSheet.top <= 0
if (isExpanded) {
toolbar.setNavigationIcon(materialR.drawable.abc_ic_clear_material)
} else {
toolbar.navigationIcon = null
}
}
override fun onSlide(bottomSheet: View, slideOffset: Float) = Unit
}

View File

@@ -10,7 +10,6 @@ import android.content.SyncResult
import android.content.pm.ResolveInfo
import android.database.SQLException
import android.graphics.Color
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkRequest
@@ -22,14 +21,16 @@ import android.view.ViewGroup
import android.view.ViewPropertyAnimator
import android.view.Window
import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.IntegerRes
import androidx.core.app.ActivityOptionsCompat
import androidx.core.content.ContextCompat.getSystemService
import androidx.core.view.children
import androidx.core.view.descendants
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.coroutineScope
import androidx.work.CoroutineWorker
import com.google.android.material.elevation.ElevationOverlayProvider
import kotlin.coroutines.resume
import kotlin.math.roundToLong
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.delay
@@ -43,7 +44,6 @@ import okio.IOException
import org.json.JSONException
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.utils.InternalResourceHelper
import kotlin.coroutines.resume
val Context.activityManager: ActivityManager?
get() = getSystemService(ACTIVITY_SERVICE) as? ActivityManager
@@ -122,7 +122,8 @@ fun SyncResult.onError(error: Throwable) {
when (error) {
is IOException -> stats.numIoExceptions++
is OperationApplicationException,
is SQLException -> databaseError = true
is SQLException,
-> databaseError = true
is JSONException -> stats.numParseExceptions++
else -> if (BuildConfig.DEBUG) throw error
}
@@ -150,6 +151,10 @@ fun ViewPropertyAnimator.applySystemAnimatorScale(context: Context): ViewPropert
this.duration = (this.duration * context.animatorDurationScale).toLong()
}
fun Context.getAnimationDuration(@IntegerRes resId: Int): Long {
return (resources.getInteger(resId) * animatorDurationScale).roundToLong()
}
inline fun <reified T> ViewGroup.findChild(): T? {
return children.find { it is T } as? T
}

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.bottomsheet.BottomSheetDragHandleView
android:id="@+id/dragHandle"
android:layout_width="match_parent"
android:layout_height="15dp"
android:minHeight="0dp"
android:paddingTop="12dp"
android:paddingBottom="0dp" />
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:theme="@style/ThemeOverlay.Kotatsu.MainToolbar"
tools:navigationIcon="?actionModeCloseDrawable"
tools:title="@string/options" />
</merge>

View File

@@ -7,42 +7,23 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:layout_width="36dp"
android:layout_height="3dp"
android:layout_gravity="center"
android:layout_marginTop="12dp"
android:alpha="0.2"
android:src="@drawable/tab_rounded_rectangle"
app:tint="?attr/colorOnSurfaceVariant"/>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
<org.koitharu.kotatsu.base.ui.widgets.BottomSheetHeaderBar
android:id="@+id/headerBar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@drawable/sheet_toolbar_background">
android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:navigationIcon="?actionModeCloseDrawable"
tools:title="@string/app_name">
<Button
android:id="@+id/button_done"
style="@style/Widget.Material3.Button.UnelevatedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginHorizontal="@dimen/toolbar_button_margin"
android:text="@string/done"
android:visibility="gone"
tools:visibility="visible" />
<Button
android:id="@+id/button_done"
style="@style/Widget.Material3.Button.UnelevatedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginHorizontal="@dimen/toolbar_button_margin"
android:text="@string/done"
android:visibility="gone"
tools:visibility="visible" />
</com.google.android.material.appbar.MaterialToolbar>
</com.google.android.material.appbar.AppBarLayout>
</org.koitharu.kotatsu.base.ui.widgets.BottomSheetHeaderBar>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"

View File

@@ -7,43 +7,17 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/toolbar_layout"
<org.koitharu.kotatsu.base.ui.widgets.BottomSheetHeaderBar
android:id="@+id/headerBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:layout_width="36dp"
android:layout_height="3dp"
android:layout_gravity="center"
android:layout_marginTop="12dp"
android:alpha="0.2"
android:src="@drawable/tab_rounded_rectangle"
app:tint="?attr/colorOnSurfaceVariant" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@drawable/sheet_toolbar_background">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:navigationIcon="?actionModeCloseDrawable"
app:title="@string/chapters" />
</com.google.android.material.appbar.AppBarLayout>
</LinearLayout>
app:title="@string/chapters" />
<org.koitharu.kotatsu.base.ui.list.fastscroll.FastScrollRecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/toolbar_layout"
android:layout_below="@id/headerBar"
android:orientation="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"

View File

@@ -8,22 +8,12 @@
android:orientation="vertical"
android:paddingBottom="@dimen/list_spacing">
<ImageView
android:layout_width="36dp"
android:layout_height="3dp"
android:layout_gravity="center"
android:layout_marginTop="12dp"
android:alpha="0.2"
android:src="@drawable/tab_rounded_rectangle"
app:tint="?attr/colorOnSurfaceVariant"/>
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
<org.koitharu.kotatsu.base.ui.widgets.BottomSheetHeaderBar
android:id="@+id/headerBar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_height="wrap_content"
app:menu="@menu/opt_categories_bs"
app:title="@string/add_to_favourites"
android:background="@drawable/sheet_toolbar_background">
app:title="@string/add_to_favourites">
<Button
android:id="@+id/button_done"
@@ -34,7 +24,7 @@
android:layout_marginHorizontal="@dimen/toolbar_button_margin"
android:text="@string/done" />
</com.google.android.material.appbar.MaterialToolbar>
</org.koitharu.kotatsu.base.ui.widgets.BottomSheetHeaderBar>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView_categories"

View File

@@ -7,29 +7,11 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:layout_width="36dp"
android:layout_height="3dp"
android:layout_gravity="center"
android:layout_marginTop="12dp"
android:alpha="0.2"
android:src="@drawable/tab_rounded_rectangle"
app:tint="?attr/colorOnSurfaceVariant" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
<org.koitharu.kotatsu.base.ui.widgets.BottomSheetHeaderBar
android:id="@+id/headerBar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@drawable/sheet_toolbar_background">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:navigationIcon="?actionModeCloseDrawable"
app:title="@string/filter" />
</com.google.android.material.appbar.AppBarLayout>
android:layout_height="wrap_content"
app:title="@string/filter" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"

View File

@@ -7,28 +7,10 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:layout_width="36dp"
android:layout_height="3dp"
android:layout_gravity="center"
android:layout_marginTop="12dp"
android:alpha="0.2"
android:src="@drawable/tab_rounded_rectangle"
app:tint="?attr/colorOnSurfaceVariant" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
<org.koitharu.kotatsu.base.ui.widgets.BottomSheetHeaderBar
android:id="@+id/headerBar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@drawable/sheet_toolbar_background">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:navigationIcon="?actionModeCloseDrawable" />
</com.google.android.material.appbar.AppBarLayout>
android:layout_height="wrap_content" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"

View File

@@ -7,29 +7,11 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:layout_width="36dp"
android:layout_height="3dp"
android:layout_gravity="center"
android:layout_marginTop="12dp"
android:alpha="0.2"
android:src="@drawable/tab_rounded_rectangle"
app:tint="?attr/colorOnSurfaceVariant" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
<org.koitharu.kotatsu.base.ui.widgets.BottomSheetHeaderBar
android:id="@+id/headerBar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@drawable/sheet_toolbar_background">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:navigationIcon="?actionModeCloseDrawable"
app:title="@string/options" />
</com.google.android.material.appbar.AppBarLayout>
android:layout_height="wrap_content"
app:title="@string/options" />
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"

View File

@@ -10,33 +10,26 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="36dp"
android:layout_height="3dp"
android:layout_gravity="center"
android:layout_marginTop="12dp"
android:layout_marginBottom="12dp"
android:alpha="0.2"
android:src="@drawable/tab_rounded_rectangle"
app:layout_constraintBottom_toTopOf="@+id/textView_title"
<com.google.android.material.bottomsheet.BottomSheetDragHandleView
android:id="@+id/dragHandle"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tint="?attr/colorOnSurfaceVariant" />
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/imageView_cover"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="16dp"
android:layout_marginTop="28dp"
android:foreground="?selectableItemBackground"
android:scaleType="centerCrop"
app:layout_constraintDimensionRatio="H,13:18"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintTop_toBottomOf="@id/dragHandle"
app:layout_constraintWidth_percent="0.3"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover"
tools:background="@tools:sample/backgrounds/scenic"
@@ -47,14 +40,13 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="28dp"
android:layout_marginEnd="6dp"
android:ellipsize="end"
android:maxLines="2"
android:textAppearance="?attr/textAppearanceHeadlineSmall"
app:layout_constraintEnd_toStartOf="@id/button_menu"
app:layout_constraintStart_toEndOf="@id/imageView_cover"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintTop_toBottomOf="@id/dragHandle"
tools:text="@tools:sample/lorem[15]" />
<ImageButton
@@ -62,12 +54,11 @@
style="?android:attr/actionOverflowButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginEnd="8dp"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/open_in_browser"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintTop_toBottomOf="@id/dragHandle"
app:tint="?android:colorControlNormal" />
<RatingBar

View File

@@ -7,45 +7,22 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:layout_width="36dp"
android:layout_height="3dp"
android:layout_gravity="center"
android:layout_marginTop="12dp"
android:layout_marginBottom="12dp"
android:alpha="0.2"
android:src="@drawable/tab_rounded_rectangle"
app:layout_constraintBottom_toTopOf="@+id/textView_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tint="?attr/colorOnSurfaceVariant" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
<org.koitharu.kotatsu.base.ui.widgets.BottomSheetHeaderBar
android:id="@+id/headerBar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@drawable/sheet_toolbar_background">
android:layout_height="wrap_content"
app:title="@string/tracking">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:navigationIcon="?actionModeCloseDrawable"
app:title="@string/tracking">
<Button
android:id="@+id/button_done"
style="@style/Widget.Material3.Button.UnelevatedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginHorizontal="@dimen/toolbar_button_margin"
android:text="@string/done" />
<Button
android:id="@+id/button_done"
style="@style/Widget.Material3.Button.UnelevatedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginEnd="4dp"
android:text="@string/done" />
</com.google.android.material.appbar.MaterialToolbar>
</com.google.android.material.appbar.AppBarLayout>
</org.koitharu.kotatsu.base.ui.widgets.BottomSheetHeaderBar>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"

View File

@@ -69,4 +69,9 @@
</attr>
</declare-styleable>
</resources>
<declare-styleable name="BottomSheetHeaderBar">
<attr name="title" />
<attr name="menu" />
</declare-styleable>
</resources>

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="config_defaultAnimTime">300</integer>
<integer name="config_tinyAnimTime">50</integer>
<integer name="manga_badge_max_character_count">3</integer>
<integer name="explore_buttons_columns">2</integer>
</resources>
</resources>