Pages thumbnails sheet
parent
18889dcb39
commit
95ec1251c0
@ -0,0 +1,8 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.reader.thumbnails
|
||||||
|
|
||||||
|
import org.koitharu.kotatsu.core.model.MangaPage
|
||||||
|
|
||||||
|
interface OnPageSelectListener {
|
||||||
|
|
||||||
|
fun onPageSelected(page: MangaPage)
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.reader.thumbnails
|
||||||
|
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import coil.Coil
|
||||||
|
import coil.api.get
|
||||||
|
import kotlinx.android.synthetic.main.item_page_thumb.*
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.core.model.MangaPage
|
||||||
|
import org.koitharu.kotatsu.domain.MangaProviderFactory
|
||||||
|
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
|
||||||
|
import org.koitharu.kotatsu.utils.DrawUtils
|
||||||
|
import org.koitharu.kotatsu.utils.ext.resolveDp
|
||||||
|
|
||||||
|
class PageThumbnailHolder(parent: ViewGroup, private val scope: CoroutineScope) :
|
||||||
|
BaseViewHolder<MangaPage, Unit>(parent, R.layout.item_page_thumb) {
|
||||||
|
|
||||||
|
private var job: Job? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
val color = DrawUtils.invertColor(textView_number.currentTextColor)
|
||||||
|
textView_number.setShadowLayer(parent.resources.resolveDp(26f), 0f, 0f, color)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBind(data: MangaPage, extra: Unit) {
|
||||||
|
imageView_thumb.setImageDrawable(null)
|
||||||
|
textView_number.text = (adapterPosition + 1).toString()
|
||||||
|
job?.cancel()
|
||||||
|
job = scope.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val url = data.preview ?: data.url.let {
|
||||||
|
MangaProviderFactory.create(data.source).getPageFullUrl(data)
|
||||||
|
}
|
||||||
|
val drawable = Coil.get(url) {
|
||||||
|
|
||||||
|
}
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
imageView_thumb.setImageDrawable(drawable)
|
||||||
|
}
|
||||||
|
} catch (e: CancellationException) {
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.reader.thumbnails
|
||||||
|
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.DisposableHandle
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import org.koitharu.kotatsu.core.model.MangaPage
|
||||||
|
import org.koitharu.kotatsu.ui.common.list.BaseRecyclerAdapter
|
||||||
|
import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
class PagesThumbnailsAdapter(onItemClickListener: OnRecyclerItemClickListener<MangaPage>?) :
|
||||||
|
BaseRecyclerAdapter<MangaPage, Unit>(onItemClickListener), CoroutineScope, DisposableHandle {
|
||||||
|
|
||||||
|
private val job = SupervisorJob()
|
||||||
|
|
||||||
|
override val coroutineContext: CoroutineContext
|
||||||
|
get() = Dispatchers.Main + job
|
||||||
|
|
||||||
|
override fun dispose() {
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getExtra(item: MangaPage, position: Int) = Unit
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup) = PageThumbnailHolder(parent, this)
|
||||||
|
|
||||||
|
override fun onGetItemId(item: MangaPage) = item.id
|
||||||
|
}
|
||||||
@ -0,0 +1,91 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.reader.thumbnails
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.fragment.app.FragmentManager
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
|
import kotlinx.android.synthetic.main.sheet_pages.*
|
||||||
|
import kotlinx.coroutines.DisposableHandle
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.core.model.MangaPage
|
||||||
|
import org.koitharu.kotatsu.ui.common.BaseBottomSheet
|
||||||
|
import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener
|
||||||
|
import org.koitharu.kotatsu.ui.common.list.decor.SpacingItemDecoration
|
||||||
|
import org.koitharu.kotatsu.utils.ext.resolveDp
|
||||||
|
import org.koitharu.kotatsu.utils.ext.withArgs
|
||||||
|
|
||||||
|
class PagesThumbnailsSheet : BaseBottomSheet(R.layout.sheet_pages),
|
||||||
|
OnRecyclerItemClickListener<MangaPage> {
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
recyclerView.addItemDecoration(SpacingItemDecoration(view.resources.resolveDp(8)))
|
||||||
|
val pages = arguments?.getParcelableArrayList<MangaPage>(ARG_PAGES)
|
||||||
|
if (pages != null) {
|
||||||
|
recyclerView.adapter = PagesThumbnailsAdapter(this).apply {
|
||||||
|
replaceData(pages)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dismissAllowingStateLoss()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val title = arguments?.getString(ARG_TITLE)
|
||||||
|
toolbar.title = title
|
||||||
|
toolbar.setNavigationOnClickListener { dismiss() }
|
||||||
|
toolbar.subtitle = resources.getQuantityString(R.plurals.pages, pages.size, pages.size)
|
||||||
|
textView_title.text = title
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?) =
|
||||||
|
super.onCreateDialog(savedInstanceState).also {
|
||||||
|
val behavior = (it as BottomSheetDialog).behavior
|
||||||
|
behavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
|
||||||
|
private val elevation = resources.getDimension(R.dimen.elevation_large)
|
||||||
|
|
||||||
|
override fun onSlide(bottomSheet: View, slideOffset: Float) = Unit
|
||||||
|
|
||||||
|
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||||
|
if (newState == BottomSheetBehavior.STATE_EXPANDED) {
|
||||||
|
toolbar.isVisible = true
|
||||||
|
appbar.elevation = elevation
|
||||||
|
} else {
|
||||||
|
toolbar.isVisible = false
|
||||||
|
appbar.elevation = 0f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
behavior.peekHeight = BottomSheetBehavior.PEEK_HEIGHT_AUTO
|
||||||
|
behavior.isFitToContents = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
(recyclerView.adapter as? DisposableHandle)?.dispose()
|
||||||
|
recyclerView.adapter = null
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onItemClick(item: MangaPage, position: Int, view: View) {
|
||||||
|
((parentFragment as? OnPageSelectListener)
|
||||||
|
?: (activity as? OnPageSelectListener))?.run {
|
||||||
|
onPageSelected(item)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private const val ARG_PAGES = "pages"
|
||||||
|
private const val ARG_TITLE = "title"
|
||||||
|
|
||||||
|
private const val TAG = "PagesThumbnailsSheet"
|
||||||
|
|
||||||
|
fun show(fm: FragmentManager, pages: List<MangaPage>, title: String) =
|
||||||
|
PagesThumbnailsSheet().withArgs(2) {
|
||||||
|
putParcelableArrayList(ARG_PAGES, ArrayList<MangaPage>(pages))
|
||||||
|
putString(ARG_TITLE, title)
|
||||||
|
}.show(fm, TAG)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
package org.koitharu.kotatsu.utils
|
||||||
|
|
||||||
|
import android.graphics.Color
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
|
|
||||||
|
object DrawUtils {
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
@ColorInt
|
||||||
|
fun invertColor(@ColorInt color: Int): Int {
|
||||||
|
val red = Color.red(color)
|
||||||
|
val green = Color.green(color)
|
||||||
|
val blue = Color.blue(color)
|
||||||
|
val alpha = Color.alpha(color)
|
||||||
|
return Color.argb(alpha, 255 - red, 255 - green, 255 - blue)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
<vector
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z" />
|
||||||
|
</vector>
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:background="?attr/scrimBackground"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<org.koitharu.kotatsu.ui.common.widgets.CoverImageView
|
||||||
|
android:id="@+id/imageView_thumb"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:text="2"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:gravity="end|bottom"
|
||||||
|
android:background="?android:selectableItemBackground"
|
||||||
|
android:id="@+id/textView_number" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
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:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/appbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?android:windowBackground"
|
||||||
|
android:elevation="0dp"
|
||||||
|
app:elevation="0dp">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
style="@style/Widget.MaterialComponents.Toolbar.Surface"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:elevation="0dp"
|
||||||
|
android:outlineProvider="@null"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:elevation="0dp"
|
||||||
|
app:navigationIcon="@drawable/ic_cross"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_title"
|
||||||
|
style="@style/MaterialAlertDialog.MaterialComponents.Title.Text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:textColor="?android:textColorSecondary"
|
||||||
|
tools:visibility="gone" />
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recyclerView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||||
|
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||||
|
app:spanCount="3"
|
||||||
|
tools:listitem="@layout/item_page_thumb" />
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<plurals name="pages">
|
||||||
|
<item quantity="one">Total %1$d page</item>
|
||||||
|
<item quantity="other">Total %1$d pages</item>
|
||||||
|
</plurals>
|
||||||
|
</resources>
|
||||||
Loading…
Reference in New Issue