Migrate to ViewBinding

This commit is contained in:
Koitharu
2020-12-01 07:45:11 +02:00
parent a215d9ebfc
commit 75b3ea0bc9
108 changed files with 1004 additions and 880 deletions

View File

@@ -53,7 +53,6 @@ object MangaUtils : KoinComponent {
}
}
@JvmStatic
private fun getBitmapSize(input: InputStream?): Size {
val options = BitmapFactory.Options().apply {
inJustDecodeBounds = true

View File

@@ -2,39 +2,43 @@ package org.koitharu.kotatsu.base.ui
import android.app.Dialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.CallSuper
import androidx.annotation.LayoutRes
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import androidx.viewbinding.ViewBinding
abstract class AlertDialogFragment(
@LayoutRes private val layoutResId: Int
) : DialogFragment() {
abstract class AlertDialogFragment<B : ViewBinding> : DialogFragment() {
private var rootView: View? = null
private var viewBinding: B? = null
protected val binding: B
get() = checkNotNull(viewBinding)
final override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val view = activity?.layoutInflater?.inflate(layoutResId, null)
rootView = view
if (view != null) {
onViewCreated(view, savedInstanceState)
}
val inflater = activity?.layoutInflater ?: LayoutInflater.from(requireContext())
val binding = onInflateView(inflater, null)
viewBinding = binding
onViewCreated(binding.root, savedInstanceState)
return AlertDialog.Builder(requireContext(), theme)
.setView(view)
.setView(binding.root)
.also(::onBuildDialog)
.create()
}
@CallSuper
override fun onDestroyView() {
rootView = null
viewBinding = null
super.onDestroyView()
}
final override fun getView(): View? {
return rootView
return viewBinding?.root
}
open fun onBuildDialog(builder: AlertDialog.Builder) = Unit
protected abstract fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): B
}

View File

@@ -7,12 +7,16 @@ import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.core.app.ActivityCompat
import androidx.viewbinding.ViewBinding
import org.koin.android.ext.android.get
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.prefs.AppSettings
abstract class BaseActivity : AppCompatActivity() {
abstract class BaseActivity<B : ViewBinding> : AppCompatActivity() {
protected lateinit var binding: B
private set
override fun onCreate(savedInstanceState: Bundle?) {
if (get<AppSettings>().isAmoledTheme) {
@@ -21,16 +25,24 @@ abstract class BaseActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
}
@Deprecated("Use ViewBinding", level = DeprecationLevel.ERROR)
override fun setContentView(layoutResID: Int) {
super.setContentView(layoutResID)
setupToolbar()
}
@Deprecated("Use ViewBinding", level = DeprecationLevel.ERROR)
override fun setContentView(view: View?) {
super.setContentView(view)
setupToolbar()
}
protected fun setContentView(binding: B) {
this.binding = binding
super.setContentView(binding.root)
(binding.root.findViewById<View>(R.id.toolbar) as? Toolbar)?.let(this::setSupportActionBar)
}
private fun setupToolbar() {
(findViewById<View>(R.id.toolbar) as? Toolbar)?.let(this::setSupportActionBar)
}

View File

@@ -5,23 +5,39 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import androidx.appcompat.app.AppCompatDialog
import androidx.viewbinding.ViewBinding
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import org.koitharu.kotatsu.utils.UiUtils
abstract class BaseBottomSheet(@LayoutRes private val layoutResId: Int) :
abstract class BaseBottomSheet<B : ViewBinding> :
BottomSheetDialogFragment() {
private var viewBinding: B? = null
protected val binding: B
get() = checkNotNull(viewBinding)
final override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(layoutResId, container, false)
): View {
val binding = onInflateView(inflater, container)
viewBinding = binding
return binding.root
}
override fun onDestroyView() {
viewBinding = null
super.onDestroyView()
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return if (UiUtils.isTablet(requireContext())) {
AppCompatDialog(context, theme)
} else super.onCreateDialog(savedInstanceState)
}
protected abstract fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): B
}

View File

@@ -1,19 +1,39 @@
package org.koitharu.kotatsu.base.ui
import android.content.Context
import androidx.annotation.LayoutRes
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import coil.ImageLoader
import org.koin.android.ext.android.inject
import androidx.viewbinding.ViewBinding
abstract class BaseFragment(
@LayoutRes contentLayoutId: Int
) : Fragment(contentLayoutId) {
abstract class BaseFragment<B : ViewBinding> : Fragment() {
protected val coil by inject<ImageLoader>()
private var viewBinding: B? = null
protected val binding: B
get() = checkNotNull(viewBinding)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = onInflateView(inflater, container)
viewBinding = binding
return binding.root
}
override fun onDestroyView() {
viewBinding = null
super.onDestroyView()
}
open fun getTitle(): CharSequence? = null
protected abstract fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): B
override fun onAttach(context: Context) {
super.onAttach(context)
getTitle()?.let {

View File

@@ -5,9 +5,10 @@ import android.os.Build
import android.os.Bundle
import android.view.View
import android.view.WindowManager
import androidx.viewbinding.ViewBinding
abstract class BaseFullscreenActivity : BaseActivity() {
abstract class BaseFullscreenActivity<B : ViewBinding> : BaseActivity<B>() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View File

@@ -1,14 +1,12 @@
package org.koitharu.kotatsu.base.ui.dialog
import android.annotation.SuppressLint
import android.content.Context
import android.content.DialogInterface
import android.view.LayoutInflater
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
import com.google.android.material.checkbox.MaterialCheckBox
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.databinding.DialogCheckboxBinding
class CheckBoxAlertDialog private constructor(private val delegate: AlertDialog) :
DialogInterface by delegate {
@@ -17,13 +15,10 @@ class CheckBoxAlertDialog private constructor(private val delegate: AlertDialog)
class Builder(context: Context) {
@SuppressLint("InflateParams")
private val view = LayoutInflater.from(context)
.inflate(R.layout.dialog_checkbox, null, false)
private val checkBox = view.findViewById<MaterialCheckBox>(android.R.id.checkbox)
private val binding = DialogCheckboxBinding.inflate(LayoutInflater.from(context))
private val delegate = AlertDialog.Builder(context)
.setView(view)
.setView(binding.root)
fun setTitle(@StringRes titleResId: Int): Builder {
delegate.setTitle(titleResId)
@@ -46,12 +41,12 @@ class CheckBoxAlertDialog private constructor(private val delegate: AlertDialog)
}
fun setCheckBoxText(@StringRes textId: Int): Builder {
checkBox.setText(textId)
binding.checkbox.setText(textId)
return this
}
fun setCheckBoxChecked(isChecked: Boolean): Builder {
checkBox.isChecked = isChecked
binding.checkbox.isChecked = isChecked
return this
}
@@ -65,7 +60,7 @@ class CheckBoxAlertDialog private constructor(private val delegate: AlertDialog)
listener: (DialogInterface, Boolean) -> Unit
): Builder {
delegate.setPositiveButton(textId) { dialog, _ ->
listener(dialog, checkBox.isChecked)
listener(dialog, binding.checkbox.isChecked)
}
return this
}

View File

@@ -7,8 +7,8 @@ import android.view.ViewGroup
import android.widget.BaseAdapter
import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
import kotlinx.android.synthetic.main.item_storage.view.*
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.databinding.ItemStorageBinding
import org.koitharu.kotatsu.local.domain.LocalMangaRepository
import org.koitharu.kotatsu.utils.ext.getStorageName
import org.koitharu.kotatsu.utils.ext.inflate
@@ -64,8 +64,9 @@ class StorageSelectDialog private constructor(private val delegate: AlertDialog)
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = convertView ?: parent.inflate(R.layout.item_storage)
val item = volumes[position]
view.textView_title.text = item.second
view.textView_subtitle.text = item.first.path
val binding = ItemStorageBinding.bind(view)
binding.textViewTitle.text = item.second
binding.textViewSubtitle.text = item.first.path
return view
}
@@ -84,7 +85,6 @@ class StorageSelectDialog private constructor(private val delegate: AlertDialog)
private companion object {
@JvmStatic
fun getAvailableVolumes(context: Context): List<Pair<File, String>> {
return LocalMangaRepository.getAvailableStorageDirs(context).map {
it to it.getStorageName(context)

View File

@@ -1,14 +1,12 @@
package org.koitharu.kotatsu.base.ui.dialog
import android.annotation.SuppressLint
import android.content.Context
import android.content.DialogInterface
import android.text.InputFilter
import android.view.LayoutInflater
import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
import kotlinx.android.synthetic.main.dialog_input.view.*
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.databinding.DialogInputBinding
class TextInputDialog private constructor(
private val delegate: AlertDialog
@@ -18,12 +16,10 @@ class TextInputDialog private constructor(
class Builder(context: Context) {
@SuppressLint("InflateParams")
private val view = LayoutInflater.from(context)
.inflate(R.layout.dialog_input, null, false)
private val binding = DialogInputBinding.inflate(LayoutInflater.from(context))
private val delegate = AlertDialog.Builder(context)
.setView(view)
.setView(binding.root)
fun setTitle(@StringRes titleResId: Int): Builder {
delegate.setTitle(titleResId)
@@ -36,29 +32,29 @@ class TextInputDialog private constructor(
}
fun setHint(@StringRes hintResId: Int): Builder {
view.inputLayout.hint = view.context.getString(hintResId)
binding.inputLayout.hint = binding.root.context.getString(hintResId)
return this
}
fun setMaxLength(maxLength: Int, strict: Boolean): Builder {
with(view.inputLayout) {
with(binding.inputLayout) {
counterMaxLength = maxLength
isCounterEnabled = maxLength > 0
}
if (strict && maxLength > 0) {
view.inputEdit.filters += InputFilter.LengthFilter(maxLength)
binding.inputEdit.filters += InputFilter.LengthFilter(maxLength)
}
return this
}
fun setInputType(inputType: Int): Builder {
view.inputEdit.inputType = inputType
binding.inputEdit.inputType = inputType
return this
}
fun setText(text: String): Builder {
view.inputEdit.setText(text)
view.inputEdit.setSelection(text.length)
binding.inputEdit.setText(text)
binding.inputEdit.setSelection(text.length)
return this
}
@@ -67,7 +63,7 @@ class TextInputDialog private constructor(
listener: (DialogInterface, String) -> Unit
): Builder {
delegate.setPositiveButton(textId) { dialog, _ ->
listener(dialog, view.inputEdit.text?.toString().orEmpty())
listener(dialog, binding.inputEdit.text.toString().orEmpty())
}
return this
}

View File

@@ -1,20 +1,11 @@
package org.koitharu.kotatsu.base.ui.list
import android.view.View
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.extensions.LayoutContainer
import androidx.viewbinding.ViewBinding
import org.koin.core.component.KoinComponent
import org.koitharu.kotatsu.utils.ext.inflate
abstract class BaseViewHolder<T, E> protected constructor(view: View) :
RecyclerView.ViewHolder(view), LayoutContainer, KoinComponent {
constructor(parent: ViewGroup, @LayoutRes resId: Int) : this(parent.inflate(resId))
override val containerView: View?
get() = itemView
abstract class BaseViewHolder<T, E, B : ViewBinding> protected constructor(val binding: B) :
RecyclerView.ViewHolder(binding.root), KoinComponent {
var boundData: T? = null
private set

View File

@@ -1,34 +0,0 @@
package org.koitharu.kotatsu.base.ui.list
import android.view.View
import android.view.ViewGroup
import kotlinx.android.synthetic.main.item_progress.*
import org.koitharu.kotatsu.R
class ProgressBarHolder(parent: ViewGroup) :
BaseViewHolder<Boolean, Unit>(parent, R.layout.item_progress) {
private var pendingVisibility: Int = View.GONE
private val action = Runnable {
progressBar?.visibility = pendingVisibility
pendingVisibility = View.GONE
}
override fun onBind(data: Boolean, extra: Unit) {
val visibility = if (data) {
View.VISIBLE
} else {
View.INVISIBLE
}
if (visibility != progressBar.visibility && visibility != pendingVisibility) {
progressBar.removeCallbacks(action)
pendingVisibility = visibility
progressBar.postDelayed(action, 400)
}
}
override fun onRecycled() {
progressBar.removeCallbacks(action)
super.onRecycled()
}
}

View File

@@ -9,29 +9,29 @@ import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.core.view.isVisible
import kotlinx.android.synthetic.main.activity_browser.*
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseActivity
import org.koitharu.kotatsu.databinding.ActivityBrowserBinding
@SuppressLint("SetJavaScriptEnabled")
class BrowserActivity : BaseActivity(), BrowserCallback {
class BrowserActivity : BaseActivity<ActivityBrowserBinding>(), BrowserCallback {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_browser)
setContentView(ActivityBrowserBinding.inflate(layoutInflater))
supportActionBar?.run {
setDisplayHomeAsUpEnabled(true)
setHomeAsUpIndicator(R.drawable.ic_cross)
}
with(webView.settings) {
with(binding.webView.settings) {
javaScriptEnabled = true
}
webView.webViewClient = BrowserClient(this)
binding.webView.webViewClient = BrowserClient(this)
val url = intent?.dataString
if (url.isNullOrEmpty()) {
finishAfterTransition()
} else {
webView.loadUrl(url)
binding.webView.loadUrl(url)
}
}
@@ -42,13 +42,13 @@ class BrowserActivity : BaseActivity(), BrowserCallback {
override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
android.R.id.home -> {
webView.stopLoading()
binding.webView.stopLoading()
finishAfterTransition()
true
}
R.id.action_browser -> {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(webView.url)
intent.data = Uri.parse(binding.webView.url)
try {
startActivity(Intent.createChooser(intent, item.title))
} catch (_: ActivityNotFoundException) {
@@ -59,25 +59,25 @@ class BrowserActivity : BaseActivity(), BrowserCallback {
}
override fun onBackPressed() {
if (webView.canGoBack()) {
webView.goBack()
if (binding.webView.canGoBack()) {
binding.webView.goBack()
} else {
super.onBackPressed()
}
}
override fun onPause() {
webView.onPause()
binding.webView.onPause()
super.onPause()
}
override fun onResume() {
super.onResume()
webView.onResume()
binding.webView.onResume()
}
override fun onLoadingStateChanged(isLoading: Boolean) {
progressBar.isVisible = isLoading
binding.progressBar.isVisible = isLoading
}
override fun onTitleChanged(title: CharSequence, subtitle: CharSequence?) {
@@ -87,7 +87,6 @@ class BrowserActivity : BaseActivity(), BrowserCallback {
companion object {
@JvmStatic
fun newIntent(context: Context, url: String) = Intent(context, BrowserActivity::class.java)
.setData(Uri.parse(url))
}

View File

@@ -2,44 +2,50 @@ package org.koitharu.kotatsu.browser.cloudflare
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.webkit.CookieManager
import android.webkit.WebSettings
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isInvisible
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import kotlinx.android.synthetic.main.fragment_cloudflare.*
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.AlertDialogFragment
import org.koitharu.kotatsu.core.network.UserAgentInterceptor
import org.koitharu.kotatsu.databinding.FragmentCloudflareBinding
import org.koitharu.kotatsu.utils.ext.stringArgument
import org.koitharu.kotatsu.utils.ext.withArgs
class CloudFlareDialog : AlertDialogFragment(R.layout.fragment_cloudflare), CloudFlareCallback {
class CloudFlareDialog : AlertDialogFragment<FragmentCloudflareBinding>(), CloudFlareCallback {
private val url by stringArgument(ARG_URL)
override fun onInflateView(
inflater: LayoutInflater,
container: ViewGroup?
) = FragmentCloudflareBinding.inflate(inflater, container, false)
@SuppressLint("SetJavaScriptEnabled")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
with(webView.settings) {
with(binding.webView.settings) {
javaScriptEnabled = true
cacheMode = WebSettings.LOAD_DEFAULT
domStorageEnabled = true
databaseEnabled = true
userAgentString = UserAgentInterceptor.userAgent
}
webView.webViewClient = CloudFlareClient(this, url.orEmpty())
CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true)
binding.webView.webViewClient = CloudFlareClient(this, url.orEmpty())
CookieManager.getInstance().setAcceptThirdPartyCookies(binding.webView, true)
if (url.isNullOrEmpty()) {
dismissAllowingStateLoss()
} else {
webView.loadUrl(url.orEmpty())
binding.webView.loadUrl(url.orEmpty())
}
}
override fun onDestroyView() {
webView.stopLoading()
binding.webView.stopLoading()
super.onDestroyView()
}
@@ -49,16 +55,16 @@ class CloudFlareDialog : AlertDialogFragment(R.layout.fragment_cloudflare), Clou
override fun onResume() {
super.onResume()
webView.onResume()
binding.webView.onResume()
}
override fun onPause() {
webView.onPause()
binding.webView.onPause()
super.onPause()
}
override fun onPageLoaded() {
progressBar?.isInvisible = true
binding.progressBar.isInvisible = true
}
override fun onCheckPassed() {

View File

@@ -1,7 +1,7 @@
package org.koitharu.kotatsu.core.github
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
import kotlinx.parcelize.Parcelize
@Parcelize
data class AppVersion(

View File

@@ -32,7 +32,6 @@ data class VersionId(
companion object {
@JvmStatic
private fun variantWeight(variantType: String) =
when (variantType.toLowerCase(Locale.ROOT)) {
"a", "alpha" -> 1
@@ -42,7 +41,6 @@ data class VersionId(
else -> 0
}
@JvmStatic
fun parse(versionName: String): VersionId {
val parts = versionName.substringBeforeLast('-').split('.')
val variant = versionName.substringAfterLast('-', "")

View File

@@ -1,7 +1,7 @@
package org.koitharu.kotatsu.core.model
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
import kotlinx.parcelize.Parcelize
import java.util.*
@Parcelize

View File

@@ -1,7 +1,7 @@
package org.koitharu.kotatsu.core.model
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
import kotlinx.parcelize.Parcelize
@Parcelize
data class Manga(

View File

@@ -1,7 +1,7 @@
package org.koitharu.kotatsu.core.model
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
import kotlinx.parcelize.Parcelize
@Parcelize
data class MangaChapter(

View File

@@ -1,7 +1,7 @@
package org.koitharu.kotatsu.core.model
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
import kotlinx.parcelize.Parcelize
@Parcelize
data class MangaFilter(

View File

@@ -1,7 +1,7 @@
package org.koitharu.kotatsu.core.model
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
import kotlinx.parcelize.Parcelize
import java.util.*
@Parcelize

View File

@@ -1,7 +1,7 @@
package org.koitharu.kotatsu.core.model
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
import kotlinx.parcelize.Parcelize
@Parcelize
data class MangaPage(

View File

@@ -1,7 +1,7 @@
package org.koitharu.kotatsu.core.model
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
import kotlinx.parcelize.Parcelize
import org.koin.core.context.GlobalContext
import org.koin.core.error.NoBeanDefFoundException
import org.koin.core.qualifier.named

View File

@@ -1,7 +1,7 @@
package org.koitharu.kotatsu.core.model
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
import kotlinx.parcelize.Parcelize
@Parcelize
data class MangaTag(

View File

@@ -1,7 +1,7 @@
package org.koitharu.kotatsu.core.model
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
import kotlinx.parcelize.Parcelize
import java.util.*
@Parcelize

View File

@@ -1,7 +1,7 @@
package org.koitharu.kotatsu.core.model
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
import kotlinx.parcelize.Parcelize
import java.util.*
@Parcelize

View File

@@ -73,7 +73,6 @@ class PersistentCookieJar(
private companion object {
@JvmStatic
fun filterPersistentCookies(cookies: List<Cookie>): List<Cookie> {
val persistentCookies: MutableList<Cookie> = ArrayList()
for (cookie in cookies) {
@@ -84,7 +83,6 @@ class PersistentCookieJar(
return persistentCookies
}
@JvmStatic
fun isCookieExpired(cookie: Cookie): Boolean {
return cookie.expiresAt < System.currentTimeMillis()
}

View File

@@ -47,7 +47,6 @@ internal class IdentifiableCookie(val cookie: Cookie) {
companion object {
@JvmStatic
fun decorateAll(cookies: Collection<Cookie>): List<IdentifiableCookie> {
val identifiableCookies: MutableList<IdentifiableCookie> = ArrayList(cookies.size)
for (cookie in cookies) {

View File

@@ -117,7 +117,6 @@ class SerializableCookie : Serializable {
* @param bytes byte array to be converted
* @return string containing hex values
*/
@JvmStatic
private fun byteArrayToHexString(bytes: ByteArray): String {
val sb = StringBuilder(bytes.size * 2)
for (element in bytes) {
@@ -136,7 +135,6 @@ class SerializableCookie : Serializable {
* @param hexString string of hex-encoded values
* @return decoded byte array
*/
@JvmStatic
private fun hexStringToByteArray(hexString: String): ByteArray {
val len = hexString.length
val data = ByteArray(len / 2)

View File

@@ -22,7 +22,6 @@ interface SourceSettings {
companion object {
@JvmStatic
operator fun invoke(context: Context, source: MangaSource): SourceSettings =
PrefSourceSettings(context, source)

View File

@@ -8,20 +8,23 @@ import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import kotlinx.android.synthetic.main.activity_crash.*
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.databinding.ActivityCrashBinding
import org.koitharu.kotatsu.main.ui.MainActivity
import org.koitharu.kotatsu.utils.ShareHelper
class CrashActivity : Activity(), View.OnClickListener {
private lateinit var binding: ActivityCrashBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_crash)
textView.text = intent.getStringExtra(Intent.EXTRA_TEXT)
button_close.setOnClickListener(this)
button_restart.setOnClickListener(this)
button_report.setOnClickListener(this)
binding = ActivityCrashBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.textView.text = intent.getStringExtra(Intent.EXTRA_TEXT)
binding.buttonClose.setOnClickListener(this)
binding.buttonRestart.setOnClickListener(this)
binding.buttonReport.setOnClickListener(this)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
@@ -32,7 +35,7 @@ class CrashActivity : Activity(), View.OnClickListener {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_share -> {
ShareHelper.shareText(this, textView.text?.toString() ?: return false)
ShareHelper.shareText(this, binding.textView.text.toString() ?: return false)
}
else -> return super.onOptionsItemSelected(item)
}

View File

@@ -1,6 +1,6 @@
package org.koitharu.kotatsu.details
import org.koin.android.viewmodel.dsl.viewModel
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
import org.koitharu.kotatsu.base.domain.MangaIntent
import org.koitharu.kotatsu.details.ui.DetailsViewModel

View File

@@ -2,28 +2,26 @@ package org.koitharu.kotatsu.details.ui
import android.app.ActivityOptions
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.*
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode
import androidx.core.view.isVisible
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.fragment_chapters.*
import org.koin.android.viewmodel.ext.android.sharedViewModel
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseFragment
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.model.MangaChapter
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.databinding.FragmentChaptersBinding
import org.koitharu.kotatsu.details.ui.adapter.ChaptersAdapter
import org.koitharu.kotatsu.details.ui.adapter.ChaptersSelectionDecoration
import org.koitharu.kotatsu.details.ui.model.ChapterListItem
import org.koitharu.kotatsu.download.DownloadService
import org.koitharu.kotatsu.reader.ui.ReaderActivity
class ChaptersFragment : BaseFragment(R.layout.fragment_chapters),
class ChaptersFragment : BaseFragment<FragmentChaptersBinding>(),
OnListItemClickListener<MangaChapter>, ActionMode.Callback {
private val viewModel by sharedViewModel<DetailsViewModel>()
@@ -32,11 +30,16 @@ class ChaptersFragment : BaseFragment(R.layout.fragment_chapters),
private var actionMode: ActionMode? = null
private var selectionDecoration: ChaptersSelectionDecoration? = null
override fun onInflateView(
inflater: LayoutInflater,
container: ViewGroup?
) = FragmentChaptersBinding.inflate(inflater, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
chaptersAdapter = ChaptersAdapter(this)
selectionDecoration = ChaptersSelectionDecoration(view.context)
with(recyclerView_chapters) {
with(binding.recyclerViewChapters) {
addItemDecoration(DividerItemDecoration(view.context, RecyclerView.VERTICAL))
addItemDecoration(selectionDecoration!!)
setHasFixedSize(true)
@@ -58,7 +61,7 @@ class ChaptersFragment : BaseFragment(R.layout.fragment_chapters),
}
private fun onLoadingStateChanged(isLoading: Boolean) {
progressBar.isVisible = isLoading
binding.progressBar.isVisible = isLoading
}
override fun onItemClick(item: MangaChapter, view: View) {
@@ -68,7 +71,7 @@ class ChaptersFragment : BaseFragment(R.layout.fragment_chapters),
actionMode?.finish()
} else {
actionMode?.invalidate()
recyclerView_chapters.invalidateItemDecorations()
binding.recyclerViewChapters.invalidateItemDecorations()
}
return
}
@@ -94,7 +97,7 @@ class ChaptersFragment : BaseFragment(R.layout.fragment_chapters),
}
return actionMode?.also {
selectionDecoration?.setItemIsChecked(item.id, true)
recyclerView_chapters.invalidateItemDecorations()
binding.recyclerViewChapters.invalidateItemDecorations()
it.invalidate()
} != null
}
@@ -113,7 +116,7 @@ class ChaptersFragment : BaseFragment(R.layout.fragment_chapters),
R.id.action_select_all -> {
val ids = chaptersAdapter?.items?.map { it.chapter.id } ?: return false
selectionDecoration?.checkAll(ids)
recyclerView_chapters.invalidateItemDecorations()
binding.recyclerViewChapters.invalidateItemDecorations()
mode.invalidate()
true
}
@@ -142,7 +145,7 @@ class ChaptersFragment : BaseFragment(R.layout.fragment_chapters),
override fun onDestroyActionMode(mode: ActionMode?) {
selectionDecoration?.clearSelection()
recyclerView_chapters.invalidateItemDecorations()
binding.recyclerViewChapters.invalidateItemDecorations()
actionMode = null
}
}

View File

@@ -16,9 +16,8 @@ import androidx.lifecycle.lifecycleScope
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import kotlinx.android.synthetic.main.activity_details.*
import kotlinx.coroutines.launch
import org.koin.android.viewmodel.ext.android.viewModel
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.R
@@ -27,6 +26,7 @@ import org.koitharu.kotatsu.base.ui.BaseActivity
import org.koitharu.kotatsu.browser.BrowserActivity
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.databinding.ActivityDetailsBinding
import org.koitharu.kotatsu.download.DownloadService
import org.koitharu.kotatsu.search.ui.global.GlobalSearchActivity
import org.koitharu.kotatsu.utils.MangaShortcut
@@ -34,7 +34,8 @@ import org.koitharu.kotatsu.utils.ShareHelper
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.getThemeColor
class DetailsActivity : BaseActivity(), TabLayoutMediator.TabConfigurationStrategy {
class DetailsActivity : BaseActivity<ActivityDetailsBinding>(),
TabLayoutMediator.TabConfigurationStrategy {
private val viewModel by viewModel<DetailsViewModel> {
parametersOf(MangaIntent.from(intent))
@@ -42,10 +43,10 @@ class DetailsActivity : BaseActivity(), TabLayoutMediator.TabConfigurationStrate
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_details)
setContentView(ActivityDetailsBinding.inflate(layoutInflater))
supportActionBar?.setDisplayHomeAsUpEnabled(true)
pager.adapter = MangaDetailsAdapter(this)
TabLayoutMediator(tabs, pager, this).attach()
binding.pager.adapter = MangaDetailsAdapter(this)
TabLayoutMediator(binding.tabs, binding.pager, this).attach()
viewModel.manga.observe(this, ::onMangaUpdated)
viewModel.newChaptersCount.observe(this, ::onNewChaptersChanged)
@@ -71,12 +72,13 @@ class DetailsActivity : BaseActivity(), TabLayoutMediator.TabConfigurationStrate
Toast.makeText(this, e.getDisplayMessage(resources), Toast.LENGTH_LONG).show()
finishAfterTransition()
} else {
Snackbar.make(pager, e.getDisplayMessage(resources), Snackbar.LENGTH_LONG).show()
Snackbar.make(binding.pager, e.getDisplayMessage(resources), Snackbar.LENGTH_LONG)
.show()
}
}
private fun onNewChaptersChanged(newChapters: Int) {
val tab = tabs.getTabAt(1) ?: return
val tab = binding.tabs.getTabAt(1) ?: return
if (newChapters == 0) {
tab.removeBadge()
} else {
@@ -171,7 +173,7 @@ class DetailsActivity : BaseActivity(), TabLayoutMediator.TabConfigurationStrate
lifecycleScope.launch {
if (!MangaShortcut(it).requestPinShortcut(this@DetailsActivity)) {
Snackbar.make(
pager,
binding.pager,
R.string.operation_not_supported,
Snackbar.LENGTH_SHORT
).show()
@@ -193,13 +195,13 @@ class DetailsActivity : BaseActivity(), TabLayoutMediator.TabConfigurationStrate
override fun onSupportActionModeStarted(mode: ActionMode) {
super.onSupportActionModeStarted(mode)
pager.isUserInputEnabled = false
binding.pager.isUserInputEnabled = false
window?.statusBarColor = ContextCompat.getColor(this, R.color.grey_dark)
}
override fun onSupportActionModeFinished(mode: ActionMode) {
super.onSupportActionModeFinished(mode)
pager.isUserInputEnabled = true
binding.pager.isUserInputEnabled = true
window?.statusBarColor = getThemeColor(androidx.appcompat.R.attr.colorPrimaryDark)
}

View File

@@ -2,20 +2,24 @@ package org.koitharu.kotatsu.details.ui
import android.os.Bundle
import android.text.Spanned
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.net.toUri
import androidx.core.text.parseAsHtml
import androidx.core.view.isVisible
import coil.ImageLoader
import com.google.android.material.chip.Chip
import kotlinx.android.synthetic.main.fragment_details.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.koin.android.viewmodel.ext.android.sharedViewModel
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseFragment
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaHistory
import org.koitharu.kotatsu.databinding.FragmentDetailsBinding
import org.koitharu.kotatsu.favourites.ui.categories.select.FavouriteCategoriesDialog
import org.koitharu.kotatsu.reader.ui.ReaderActivity
import org.koitharu.kotatsu.search.ui.MangaSearchSheet
@@ -23,10 +27,16 @@ import org.koitharu.kotatsu.utils.FileSizeUtils
import org.koitharu.kotatsu.utils.ext.*
import kotlin.math.roundToInt
class DetailsFragment : BaseFragment(R.layout.fragment_details), View.OnClickListener,
class DetailsFragment : BaseFragment<FragmentDetailsBinding>(), View.OnClickListener,
View.OnLongClickListener {
private val viewModel by sharedViewModel<DetailsViewModel>()
private val coil by inject<ImageLoader>()
override fun onInflateView(
inflater: LayoutInflater,
container: ViewGroup?
) = FragmentDetailsBinding.inflate(inflater, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -37,63 +47,66 @@ class DetailsFragment : BaseFragment(R.layout.fragment_details), View.OnClickLis
}
private fun onMangaUpdated(manga: Manga) {
imageView_cover.newImageRequest(manga.largeCoverUrl ?: manga.coverUrl)
.fallback(R.drawable.ic_placeholder)
.crossfade(true)
.lifecycle(viewLifecycleOwner)
.enqueueWith(coil)
textView_title.text = manga.title
textView_subtitle.textAndVisible = manga.altTitle
textView_description.text = manga.description?.parseAsHtml()?.takeUnless(Spanned::isBlank)
?: getString(R.string.no_description)
if (manga.rating == Manga.NO_RATING) {
ratingBar.isVisible = false
} else {
ratingBar.progress = (ratingBar.max * manga.rating).roundToInt()
ratingBar.isVisible = true
}
chips_tags.removeAllViews()
manga.author?.let { a ->
chips_tags.addChips(listOf(a)) {
create(
text = it,
iconRes = R.drawable.ic_chip_user,
tag = it,
onClickListener = this@DetailsFragment
)
with(binding) {
imageViewCover.newImageRequest(manga.largeCoverUrl ?: manga.coverUrl)
.fallback(R.drawable.ic_placeholder)
.crossfade(true)
.lifecycle(viewLifecycleOwner)
.enqueueWith(coil)
textViewTitle.text = manga.title
textViewSubtitle.textAndVisible = manga.altTitle
textViewDescription.text =
manga.description?.parseAsHtml()?.takeUnless(Spanned::isBlank)
?: getString(R.string.no_description)
if (manga.rating == Manga.NO_RATING) {
ratingBar.isVisible = false
} else {
ratingBar.progress = (ratingBar.max * manga.rating).roundToInt()
ratingBar.isVisible = true
}
}
chips_tags.addChips(manga.tags) {
create(
text = it.title,
iconRes = R.drawable.ic_chip_tag,
tag = it,
onClickListener = this@DetailsFragment
)
}
manga.url.toUri().toFileOrNull()?.let { f ->
viewLifecycleScope.launch {
val size = withContext(Dispatchers.IO) {
f.length()
}
chips_tags.addChips(listOf(f)) {
chipsTags.removeAllViews()
manga.author?.let { a ->
chipsTags.addChips(listOf(a)) {
create(
text = FileSizeUtils.formatBytes(context, size),
iconRes = R.drawable.ic_chip_storage,
text = it,
iconRes = R.drawable.ic_chip_user,
tag = it,
onClickListener = this@DetailsFragment
)
}
}
chipsTags.addChips(manga.tags) {
create(
text = it.title,
iconRes = R.drawable.ic_chip_tag,
tag = it,
onClickListener = this@DetailsFragment
)
}
manga.url.toUri().toFileOrNull()?.let { f ->
viewLifecycleScope.launch {
val size = withContext(Dispatchers.IO) {
f.length()
}
chipsTags.addChips(listOf(f)) {
create(
text = FileSizeUtils.formatBytes(context, size),
iconRes = R.drawable.ic_chip_storage,
tag = it,
onClickListener = this@DetailsFragment
)
}
}
}
imageViewFavourite.setOnClickListener(this@DetailsFragment)
buttonRead.setOnClickListener(this@DetailsFragment)
buttonRead.setOnLongClickListener(this@DetailsFragment)
buttonRead.isEnabled = !manga.chapters.isNullOrEmpty()
}
imageView_favourite.setOnClickListener(this)
button_read.setOnClickListener(this)
button_read.setOnLongClickListener(this)
button_read.isEnabled = !manga.chapters.isNullOrEmpty()
}
private fun onHistoryChanged(history: MangaHistory?) {
with(button_read) {
with(binding.buttonRead) {
if (history == null) {
setText(R.string.read)
setIconResource(R.drawable.ic_read)
@@ -105,7 +118,7 @@ class DetailsFragment : BaseFragment(R.layout.fragment_details), View.OnClickLis
}
private fun onFavouriteChanged(isFavourite: Boolean) {
imageView_favourite.setImageResource(
binding.imageViewFavourite.setImageResource(
if (isFavourite) {
R.drawable.ic_heart
} else {
@@ -115,7 +128,7 @@ class DetailsFragment : BaseFragment(R.layout.fragment_details), View.OnClickLis
}
private fun onLoadingStateChanged(isLoading: Boolean) {
progressBar.isVisible = isLoading
binding.progressBar.isVisible = isLoading
}
override fun onClick(v: View) {

View File

@@ -1,17 +1,19 @@
package org.koitharu.kotatsu.details.ui.adapter
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateLayoutContainer
import kotlinx.android.synthetic.main.item_chapter.*
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.model.MangaChapter
import org.koitharu.kotatsu.databinding.ItemChapterBinding
import org.koitharu.kotatsu.details.ui.model.ChapterListItem
import org.koitharu.kotatsu.history.domain.ChapterExtra
import org.koitharu.kotatsu.utils.ext.getThemeColor
fun chapterListItemAD(
clickListener: OnListItemClickListener<MangaChapter>
) = adapterDelegateLayoutContainer<ChapterListItem, ChapterListItem>(R.layout.item_chapter) {
) = adapterDelegateViewBinding<ChapterListItem, ChapterListItem, ItemChapterBinding>(
{ inflater, parent -> ItemChapterBinding.inflate(inflater, parent, false) }
) {
itemView.setOnClickListener {
clickListener.onItemClick(item.chapter, it)
@@ -21,24 +23,24 @@ fun chapterListItemAD(
}
bind { payload ->
textView_title.text = item.chapter.name
textView_number.text = item.chapter.number.toString()
binding.textViewTitle.text = item.chapter.name
binding.textViewNumber.text = item.chapter.number.toString()
when (item.extra) {
ChapterExtra.UNREAD -> {
textView_number.setBackgroundResource(R.drawable.bg_badge_default)
textView_number.setTextColor(context.getThemeColor(android.R.attr.textColorSecondaryInverse))
binding.textViewNumber.setBackgroundResource(R.drawable.bg_badge_default)
binding.textViewNumber.setTextColor(context.getThemeColor(android.R.attr.textColorSecondaryInverse))
}
ChapterExtra.READ -> {
textView_number.setBackgroundResource(R.drawable.bg_badge_outline)
textView_number.setTextColor(context.getThemeColor(android.R.attr.textColorTertiary))
binding.textViewNumber.setBackgroundResource(R.drawable.bg_badge_outline)
binding.textViewNumber.setTextColor(context.getThemeColor(android.R.attr.textColorTertiary))
}
ChapterExtra.CURRENT -> {
textView_number.setBackgroundResource(R.drawable.bg_badge_outline_accent)
textView_number.setTextColor(context.getThemeColor(androidx.appcompat.R.attr.colorAccent))
binding.textViewNumber.setBackgroundResource(R.drawable.bg_badge_outline_accent)
binding.textViewNumber.setTextColor(context.getThemeColor(androidx.appcompat.R.attr.colorAccent))
}
ChapterExtra.NEW -> {
textView_number.setBackgroundResource(R.drawable.bg_badge_accent)
textView_number.setTextColor(context.getThemeColor(android.R.attr.textColorPrimaryInverse))
binding.textViewNumber.setBackgroundResource(R.drawable.bg_badge_accent)
binding.textViewNumber.setTextColor(context.getThemeColor(android.R.attr.textColorPrimaryInverse))
}
}
}

View File

@@ -142,7 +142,6 @@ class DownloadNotification(private val context: Context) {
private const val PROGRESS_STEP = 20
@JvmStatic
private fun createIntent(context: Context, manga: Manga) = PendingIntent.getActivity(
context,
manga.hashCode(),

View File

@@ -55,9 +55,9 @@ class DownloadService : BaseService() {
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "kotatsu:downloading")
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
when (intent.action) {
when (intent?.action) {
ACTION_DOWNLOAD_START -> {
val manga = intent.getParcelableExtra<Manga>(EXTRA_MANGA)
val chapters = intent.getLongArrayExtra(EXTRA_CHAPTERS_IDS)?.toArraySet()

View File

@@ -1,6 +1,6 @@
package org.koitharu.kotatsu.favourites
import org.koin.android.viewmodel.dsl.viewModel
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository

View File

@@ -1,26 +1,24 @@
package org.koitharu.kotatsu.favourites.ui
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.*
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.tabs.TabLayoutMediator
import kotlinx.android.synthetic.main.fragment_favourites.*
import org.koin.android.viewmodel.ext.android.viewModel
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseFragment
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.databinding.FragmentFavouritesBinding
import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity
import org.koitharu.kotatsu.favourites.ui.categories.CategoriesEditDelegate
import org.koitharu.kotatsu.favourites.ui.categories.FavouritesCategoriesViewModel
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.showPopupMenu
import java.util.*
import kotlin.collections.ArrayList
class FavouritesContainerFragment : BaseFragment(R.layout.fragment_favourites),
FavouritesTabLongClickListener, CategoriesEditDelegate.CategoriesEditCallback {
class FavouritesContainerFragment : BaseFragment<FragmentFavouritesBinding>(),
FavouritesTabLongClickListener, CategoriesEditDelegate.CategoriesEditCallback {
private val viewModel by viewModel<FavouritesCategoriesViewModel>()
@@ -33,11 +31,16 @@ class FavouritesContainerFragment : BaseFragment(R.layout.fragment_favourites),
setHasOptionsMenu(true)
}
override fun onInflateView(
inflater: LayoutInflater,
container: ViewGroup?
) = FragmentFavouritesBinding.inflate(inflater, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val adapter = FavouritesPagerAdapter(this, this)
pager.adapter = adapter
TabLayoutMediator(tabs, pager, adapter).attach()
binding.pager.adapter = adapter
TabLayoutMediator(binding.tabs, binding.pager, adapter).attach()
viewModel.categories.observe(viewLifecycleOwner, ::onCategoriesChanged)
viewModel.onError.observe(viewLifecycleOwner, ::onError)
@@ -47,7 +50,7 @@ class FavouritesContainerFragment : BaseFragment(R.layout.fragment_favourites),
val data = ArrayList<FavouriteCategory>(categories.size + 1)
data += FavouriteCategory(0L, getString(R.string.all_favourites), -1, Date())
data += categories
(pager.adapter as? FavouritesPagerAdapter)?.replaceData(data)
(binding.pager.adapter as? FavouritesPagerAdapter)?.replaceData(data)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
@@ -70,7 +73,7 @@ class FavouritesContainerFragment : BaseFragment(R.layout.fragment_favourites),
}
private fun onError(e: Throwable) {
Snackbar.make(pager, e.message ?: return, Snackbar.LENGTH_LONG).show()
Snackbar.make(binding.pager, e.getDisplayMessage(resources), Snackbar.LENGTH_LONG).show()
}
override fun onTabLongClick(tabView: View, category: FavouriteCategory): Boolean {

View File

@@ -11,16 +11,17 @@ import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.activity_categories.*
import org.koin.android.viewmodel.ext.android.viewModel
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseActivity
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.databinding.ActivityCategoriesBinding
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.showPopupMenu
class CategoriesActivity : BaseActivity(), OnListItemClickListener<FavouriteCategory>,
class CategoriesActivity : BaseActivity<ActivityCategoriesBinding>(),
OnListItemClickListener<FavouriteCategory>,
View.OnClickListener, CategoriesEditDelegate.CategoriesEditCallback {
private val viewModel by viewModel<FavouritesCategoriesViewModel>()
@@ -31,16 +32,16 @@ class CategoriesActivity : BaseActivity(), OnListItemClickListener<FavouriteCate
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_categories)
setContentView(ActivityCategoriesBinding.inflate(layoutInflater))
supportActionBar?.setDisplayHomeAsUpEnabled(true)
fab_add.imageTintList = ColorStateList.valueOf(Color.WHITE)
binding.fabAdd.imageTintList = ColorStateList.valueOf(Color.WHITE)
adapter = CategoriesAdapter(this)
editDelegate = CategoriesEditDelegate(this, this)
recyclerView.addItemDecoration(DividerItemDecoration(this, RecyclerView.VERTICAL))
recyclerView.adapter = adapter
fab_add.setOnClickListener(this)
binding.recyclerView.addItemDecoration(DividerItemDecoration(this, RecyclerView.VERTICAL))
binding.recyclerView.adapter = adapter
binding.fabAdd.setOnClickListener(this)
reorderHelper = ItemTouchHelper(ReorderHelperCallback())
reorderHelper.attachToRecyclerView(recyclerView)
reorderHelper.attachToRecyclerView(binding.recyclerView)
viewModel.categories.observe(this, ::onCategoriesChanged)
viewModel.onError.observe(this, ::onError)
@@ -64,18 +65,18 @@ class CategoriesActivity : BaseActivity(), OnListItemClickListener<FavouriteCate
override fun onItemLongClick(item: FavouriteCategory, view: View): Boolean {
reorderHelper.startDrag(
recyclerView.findContainingViewHolder(view) ?: return false
binding.recyclerView.findContainingViewHolder(view) ?: return false
)
return true
}
private fun onCategoriesChanged(categories: List<FavouriteCategory>) {
adapter.items = categories
textView_holder.isVisible = categories.isEmpty()
binding.textViewHolder.isVisible = categories.isEmpty()
}
private fun onError(e: Throwable) {
Snackbar.make(recyclerView, e.getDisplayMessage(resources), Snackbar.LENGTH_LONG)
Snackbar.make(binding.recyclerView, e.getDisplayMessage(resources), Snackbar.LENGTH_LONG)
.show()
}

View File

@@ -1,21 +1,22 @@
package org.koitharu.kotatsu.favourites.ui.categories
import android.view.MotionEvent
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateLayoutContainer
import kotlinx.android.synthetic.main.item_category.*
import org.koitharu.kotatsu.R
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.databinding.ItemCategoryBinding
fun categoryAD(
clickListener: OnListItemClickListener<FavouriteCategory>
) = adapterDelegateLayoutContainer<FavouriteCategory, FavouriteCategory>(R.layout.item_category) {
) = adapterDelegateViewBinding<FavouriteCategory, FavouriteCategory, ItemCategoryBinding>(
{ inflater, parent -> ItemCategoryBinding.inflate(inflater, parent, false) }
) {
imageView_more.setOnClickListener {
binding.imageViewMore.setOnClickListener {
clickListener.onItemClick(item, it)
}
@Suppress("ClickableViewAccessibility")
imageView_handle.setOnTouchListener { v, event ->
binding.imageViewHandle.setOnTouchListener { v, event ->
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
clickListener.onItemLongClick(item, itemView)
} else {
@@ -24,6 +25,6 @@ fun categoryAD(
}
bind {
textView_title.text = item.title
binding.textViewTitle.text = item.title
}
}

View File

@@ -1,11 +1,12 @@
package org.koitharu.kotatsu.favourites.ui.categories.select
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.FragmentManager
import kotlinx.android.synthetic.main.dialog_favorite_categories.*
import org.koin.android.viewmodel.ext.android.viewModel
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.domain.MangaIntent
@@ -13,13 +14,14 @@ 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.core.model.Manga
import org.koitharu.kotatsu.databinding.DialogFavoriteCategoriesBinding
import org.koitharu.kotatsu.favourites.ui.categories.CategoriesEditDelegate
import org.koitharu.kotatsu.favourites.ui.categories.select.adapter.MangaCategoriesAdapter
import org.koitharu.kotatsu.favourites.ui.categories.select.model.MangaCategoryItem
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.withArgs
class FavouriteCategoriesDialog : BaseBottomSheet(R.layout.dialog_favorite_categories),
class FavouriteCategoriesDialog : BaseBottomSheet<DialogFavoriteCategoriesBinding>(),
OnListItemClickListener<MangaCategoryItem>, CategoriesEditDelegate.CategoriesEditCallback,
View.OnClickListener {
@@ -32,11 +34,16 @@ class FavouriteCategoriesDialog : BaseBottomSheet(R.layout.dialog_favorite_categ
CategoriesEditDelegate(requireContext(), this@FavouriteCategoriesDialog)
}
override fun onInflateView(
inflater: LayoutInflater,
container: ViewGroup?
) = DialogFavoriteCategoriesBinding.inflate(inflater, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
adapter = MangaCategoriesAdapter(this)
recyclerView_categories.adapter = adapter
textView_add.setOnClickListener(this)
binding.recyclerViewCategories.adapter = adapter
binding.textViewAdd.setOnClickListener(this)
viewModel.content.observe(viewLifecycleOwner, this::onContentChanged)
viewModel.onError.observe(viewLifecycleOwner, ::onError)

View File

@@ -1,15 +1,14 @@
package org.koitharu.kotatsu.favourites.ui.categories.select.adapter
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateLayoutContainer
import kotlinx.android.synthetic.main.item_category_checkable.*
import org.koitharu.kotatsu.R
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.databinding.ItemCategoryCheckableBinding
import org.koitharu.kotatsu.favourites.ui.categories.select.model.MangaCategoryItem
fun mangaCategoryAD(
clickListener: OnListItemClickListener<MangaCategoryItem>
) = adapterDelegateLayoutContainer<MangaCategoryItem, MangaCategoryItem>(
R.layout.item_category_checkable
) = adapterDelegateViewBinding<MangaCategoryItem, MangaCategoryItem, ItemCategoryCheckableBinding>(
{ inflater, parent -> ItemCategoryCheckableBinding.inflate(inflater, parent, false) }
) {
itemView.setOnClickListener {
@@ -17,7 +16,9 @@ fun mangaCategoryAD(
}
bind {
checkedTextView.text = item.name
checkedTextView.isChecked = item.isChecked
with(binding.checkedTextView) {
text = item.name
isChecked = item.isChecked
}
}
}

View File

@@ -3,8 +3,7 @@ package org.koitharu.kotatsu.favourites.ui.list
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import kotlinx.android.synthetic.main.fragment_list.*
import org.koin.android.viewmodel.ext.android.viewModel
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.Manga
@@ -25,14 +24,14 @@ class FavouritesListFragment : MangaListFragment() {
override fun onScrolledToEnd() = Unit
override fun setUpEmptyListHolder() {
textView_holder.setText(
binding.textViewHolder.setText(
if (categoryId == 0L) {
R.string.you_have_not_favourites_yet
} else {
R.string.favourites_category_empty
}
)
textView_holder.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0)
binding.textViewHolder.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0)
}
override fun onCreatePopupMenu(inflater: MenuInflater, menu: Menu, data: Manga) {

View File

@@ -1,7 +1,7 @@
package org.koitharu.kotatsu.history
import org.koin.android.ext.koin.androidContext
import org.koin.android.viewmodel.dsl.viewModel
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
import org.koitharu.kotatsu.history.domain.HistoryRepository
import org.koitharu.kotatsu.history.ui.HistoryListViewModel

View File

@@ -7,8 +7,7 @@ import android.view.MenuItem
import android.view.View
import androidx.appcompat.app.AlertDialog
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.fragment_list.*
import org.koin.android.viewmodel.ext.android.viewModel
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.list.ui.MangaListFragment
@@ -36,7 +35,8 @@ class HistoryListFragment : MangaListFragment() {
override fun onPrepareOptionsMenu(menu: Menu) {
super.onPrepareOptionsMenu(menu)
menu.findItem(R.id.action_history_grouping)?.isChecked = viewModel.isGroupingEnabled.value == true
menu.findItem(R.id.action_history_grouping)?.isChecked =
viewModel.isGroupingEnabled.value == true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
@@ -64,8 +64,8 @@ class HistoryListFragment : MangaListFragment() {
}
override fun setUpEmptyListHolder() {
textView_holder.setText(R.string.text_history_holder)
textView_holder.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0)
binding.textViewHolder.setText(R.string.text_history_holder)
binding.textViewHolder.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0)
}
override fun onCreatePopupMenu(inflater: MenuInflater, menu: Menu, data: Manga) {
@@ -85,7 +85,7 @@ class HistoryListFragment : MangaListFragment() {
private fun onItemRemoved(item: Manga) {
Snackbar.make(
recyclerView, getString(
binding.recyclerView, getString(
R.string._s_removed_from_history,
item.title.ellipsize(16)
), Snackbar.LENGTH_SHORT

View File

@@ -1,18 +1,20 @@
package org.koitharu.kotatsu.list.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.SeekBar
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.FragmentManager
import kotlinx.android.synthetic.main.dialog_list_mode.*
import org.koin.android.ext.android.inject
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.AlertDialogFragment
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.databinding.DialogListModeBinding
class ListModeSelectDialog : AlertDialogFragment(R.layout.dialog_list_mode), View.OnClickListener,
class ListModeSelectDialog : AlertDialogFragment<DialogListModeBinding>(), View.OnClickListener,
SeekBar.OnSeekBarChangeListener {
private val settings by inject<AppSettings>()
@@ -26,6 +28,11 @@ class ListModeSelectDialog : AlertDialogFragment(R.layout.dialog_list_mode), Vie
pendingGridSize = settings.gridSize
}
override fun onInflateView(
inflater: LayoutInflater,
container: ViewGroup?
) = DialogListModeBinding.inflate(inflater, container, false)
override fun onBuildDialog(builder: AlertDialog.Builder) {
builder.setTitle(R.string.list_mode)
.setPositiveButton(R.string.done, null)
@@ -34,18 +41,18 @@ class ListModeSelectDialog : AlertDialogFragment(R.layout.dialog_list_mode), Vie
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
button_list.isChecked = mode == ListMode.LIST
button_list_detailed.isChecked = mode == ListMode.DETAILED_LIST
button_grid.isChecked = mode == ListMode.GRID
binding.buttonList.isChecked = mode == ListMode.LIST
binding.buttonListDetailed.isChecked = mode == ListMode.DETAILED_LIST
binding.buttonGrid.isChecked = mode == ListMode.GRID
with(seekbar_grid) {
with(binding.seekbarGrid) {
progress = pendingGridSize - 50
setOnSeekBarChangeListener(this@ListModeSelectDialog)
}
button_list.setOnClickListener(this)
button_grid.setOnClickListener(this)
button_list_detailed.setOnClickListener(this)
binding.buttonList.setOnClickListener(this)
binding.buttonGrid.setOnClickListener(this)
binding.buttonListDetailed.setOnClickListener(this)
}
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {

View File

@@ -14,7 +14,6 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.fragment_list.*
import org.koin.android.ext.android.get
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseFragment
@@ -28,6 +27,7 @@ import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaFilter
import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.databinding.FragmentListBinding
import org.koitharu.kotatsu.details.ui.DetailsActivity
import org.koitharu.kotatsu.list.ui.adapter.MangaListAdapter
import org.koitharu.kotatsu.list.ui.filter.FilterAdapter
@@ -37,11 +37,11 @@ import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.hasItems
import org.koitharu.kotatsu.utils.ext.toggleDrawer
abstract class MangaListFragment : BaseFragment(R.layout.fragment_list),
abstract class MangaListFragment : BaseFragment<FragmentListBinding>(),
PaginationScrollListener.Callback, OnListItemClickListener<Manga>, OnFilterChangedListener,
SectionItemDecoration.Callback, SwipeRefreshLayout.OnRefreshListener {
private var adapter: MangaListAdapter? = null
private var listAdapter: MangaListAdapter? = null
private var paginationListener: PaginationScrollListener? = null
private val spanResolver = MangaListSpanResolver()
private val spanSizeLookup = SpanSizeLookup()
@@ -54,19 +54,30 @@ abstract class MangaListFragment : BaseFragment(R.layout.fragment_list),
setHasOptionsMenu(true)
}
override fun onInflateView(
inflater: LayoutInflater,
container: ViewGroup?
) = FragmentListBinding.inflate(inflater, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
drawer?.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
adapter = MangaListAdapter(get(), this)
binding.drawer?.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
listAdapter = MangaListAdapter(get(), this)
paginationListener = PaginationScrollListener(4, this)
recyclerView.setHasFixedSize(true)
recyclerView.adapter = adapter
recyclerView.addOnScrollListener(paginationListener!!)
swipeRefreshLayout.setOnRefreshListener(this)
swipeRefreshLayout.isEnabled = isSwipeRefreshEnabled
recyclerView_filter.setHasFixedSize(true)
recyclerView_filter.addItemDecoration(ItemTypeDividerDecoration(view.context))
recyclerView_filter.addItemDecoration(SectionItemDecoration(false, this))
with(binding.recyclerView) {
setHasFixedSize(true)
adapter = listAdapter
addOnScrollListener(paginationListener!!)
}
with(binding.swipeRefreshLayout) {
setOnRefreshListener(this@MangaListFragment)
isEnabled = isSwipeRefreshEnabled
}
with(binding.recyclerViewFilter) {
setHasFixedSize(true)
addItemDecoration(ItemTypeDividerDecoration(view.context))
addItemDecoration(SectionItemDecoration(false, this@MangaListFragment))
}
viewModel.content.observe(viewLifecycleOwner, ::onListChanged)
viewModel.filter.observe(viewLifecycleOwner, ::onInitFilter)
@@ -78,7 +89,7 @@ abstract class MangaListFragment : BaseFragment(R.layout.fragment_list),
}
override fun onDestroyView() {
adapter = null
listAdapter = null
paginationListener = null
spanSizeLookup.invalidateCache()
super.onDestroyView()
@@ -95,15 +106,15 @@ abstract class MangaListFragment : BaseFragment(R.layout.fragment_list),
true
}
R.id.action_filter -> {
drawer?.toggleDrawer(GravityCompat.END)
binding.drawer?.toggleDrawer(GravityCompat.END)
true
}
else -> super.onOptionsItemSelected(item)
}
override fun onPrepareOptionsMenu(menu: Menu) {
menu.findItem(R.id.action_filter).isVisible = drawer != null &&
drawer?.getDrawerLockMode(GravityCompat.END) != DrawerLayout.LOCK_MODE_LOCKED_CLOSED
menu.findItem(R.id.action_filter).isVisible = binding.drawer != null &&
binding.drawer?.getDrawerLockMode(GravityCompat.END) != DrawerLayout.LOCK_MODE_LOCKED_CLOSED
super.onPrepareOptionsMenu(menu)
}
@@ -128,12 +139,12 @@ abstract class MangaListFragment : BaseFragment(R.layout.fragment_list),
@CallSuper
override fun onRefresh() {
swipeRefreshLayout.isRefreshing = true
binding.swipeRefreshLayout.isRefreshing = true
}
private fun onListChanged(list: List<Any>) {
spanSizeLookup.invalidateCache()
adapter?.items = list
listAdapter?.items = list
}
private fun onError(e: Throwable) {
@@ -141,27 +152,33 @@ abstract class MangaListFragment : BaseFragment(R.layout.fragment_list),
CloudFlareDialog.newInstance(e.url).show(childFragmentManager, CloudFlareDialog.TAG)
}
if (viewModel.isEmptyState.value == true) {
textView_holder.text = e.getDisplayMessage(resources)
textView_holder.setCompoundDrawablesRelativeWithIntrinsicBounds(
binding.textViewHolder.text = e.getDisplayMessage(resources)
binding.textViewHolder.setCompoundDrawablesRelativeWithIntrinsicBounds(
0,
R.drawable.ic_error_large,
0,
0
)
layout_holder.isVisible = true
binding.textViewHolder.isVisible = true
} else {
Snackbar.make(recyclerView, e.getDisplayMessage(resources), Snackbar.LENGTH_SHORT)
Snackbar.make(
binding.recyclerView,
e.getDisplayMessage(resources),
Snackbar.LENGTH_SHORT
)
.show()
}
}
@CallSuper
protected open fun onLoadingStateChanged(isLoading: Boolean) {
val hasItems = recyclerView.hasItems
progressBar.isVisible = isLoading && !hasItems && viewModel.isEmptyState.value != true
swipeRefreshLayout.isEnabled = isSwipeRefreshEnabled && !progressBar.isVisible
val hasItems = binding.recyclerView.hasItems
binding.progressBar.isVisible =
isLoading && !hasItems && viewModel.isEmptyState.value != true
binding.swipeRefreshLayout.isEnabled =
isSwipeRefreshEnabled && !binding.progressBar.isVisible
if (!isLoading) {
swipeRefreshLayout.isRefreshing = false
binding.swipeRefreshLayout.isRefreshing = false
}
}
@@ -169,47 +186,49 @@ abstract class MangaListFragment : BaseFragment(R.layout.fragment_list),
if (isEmpty) {
setUpEmptyListHolder()
}
layout_holder.isVisible = isEmpty
binding.layoutHolder.isVisible = isEmpty
}
protected fun onInitFilter(config: MangaFilterConfig) {
recyclerView_filter.adapter = FilterAdapter(
binding.recyclerViewFilter.adapter = FilterAdapter(
sortOrders = config.sortOrders,
tags = config.tags,
state = config.currentFilter,
listener = this
)
drawer?.setDrawerLockMode(
binding.drawer?.setDrawerLockMode(
if (config.sortOrders.isEmpty() && config.tags.isEmpty()) {
DrawerLayout.LOCK_MODE_LOCKED_CLOSED
} else {
DrawerLayout.LOCK_MODE_UNLOCKED
}
) ?: divider_filter?.let {
) ?: binding.dividerFilter?.let {
it.isGone = config.sortOrders.isEmpty() && config.tags.isEmpty()
recyclerView_filter.isVisible = it.isVisible
binding.recyclerViewFilter.isVisible = it.isVisible
}
activity?.invalidateOptionsMenu()
}
@CallSuper
override fun onFilterChanged(filter: MangaFilter) {
drawer?.closeDrawers()
binding.drawer?.closeDrawers()
}
protected open fun setUpEmptyListHolder() {
textView_holder.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, null, null)
textView_holder.setText(R.string.nothing_found)
with(binding.textViewHolder) {
setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, null, null)
setText(R.string.nothing_found)
}
}
private fun onGridScaleChanged(scale: Float) {
spanSizeLookup.invalidateCache()
spanResolver.setGridSize(scale, recyclerView)
spanResolver.setGridSize(scale, binding.recyclerView)
}
private fun onListModeChanged(mode: ListMode) {
spanSizeLookup.invalidateCache()
with(recyclerView) {
with(binding.recyclerView) {
clearItemDecorations()
removeOnLayoutChangeListener(spanResolver)
when (mode) {
@@ -246,13 +265,13 @@ abstract class MangaListFragment : BaseFragment(R.layout.fragment_list),
}
final override fun isSection(position: Int): Boolean {
return position == 0 || recyclerView_filter.adapter?.run {
return position == 0 || binding.recyclerViewFilter.adapter?.run {
getItemViewType(position) != getItemViewType(position - 1)
} ?: false
}
final override fun getSectionTitle(position: Int): CharSequence? {
return when (recyclerView_filter.adapter?.getItemViewType(position)) {
return when (binding.recyclerViewFilter.adapter?.getItemViewType(position)) {
FilterAdapter.VIEW_TYPE_SORT -> getString(R.string.sort_order)
FilterAdapter.VIEW_TYPE_TAG -> getString(R.string.genre)
else -> null
@@ -271,8 +290,9 @@ abstract class MangaListFragment : BaseFragment(R.layout.fragment_list),
}
override fun getSpanSize(position: Int): Int {
val total = (recyclerView.layoutManager as? GridLayoutManager)?.spanCount ?: return 1
return when (adapter?.getItemViewType(position)) {
val total =
(binding.recyclerView.layoutManager as? GridLayoutManager)?.spanCount ?: return 1
return when (listAdapter?.getItemViewType(position)) {
MangaListAdapter.ITEM_TYPE_DATE,
MangaListAdapter.ITEM_TYPE_PROGRESS -> total
else -> 1

View File

@@ -2,8 +2,10 @@ package org.koitharu.kotatsu.list.ui
import android.content.SharedPreferences
import android.os.Bundle
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.Toolbar
import androidx.core.view.isVisible
import androidx.recyclerview.widget.DividerItemDecoration
@@ -13,7 +15,6 @@ import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.sheet_list.*
import org.koin.android.ext.android.get
import org.koin.android.ext.android.inject
import org.koitharu.kotatsu.R
@@ -24,12 +25,13 @@ import org.koitharu.kotatsu.base.ui.list.decor.SpacingItemDecoration
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.databinding.SheetListBinding
import org.koitharu.kotatsu.details.ui.DetailsActivity
import org.koitharu.kotatsu.list.ui.adapter.MangaListAdapter
import org.koitharu.kotatsu.utils.UiUtils
import org.koitharu.kotatsu.utils.ext.*
abstract class MangaListSheet : BaseBottomSheet(R.layout.sheet_list),
abstract class MangaListSheet : BaseBottomSheet<SheetListBinding>(),
PaginationScrollListener.Callback, OnListItemClickListener<Manga>,
SharedPreferences.OnSharedPreferenceChangeListener, Toolbar.OnMenuItemClickListener {
@@ -39,22 +41,26 @@ abstract class MangaListSheet : BaseBottomSheet(R.layout.sheet_list),
protected abstract val viewModel: MangaListViewModel
override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): SheetListBinding {
return SheetListBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
adapter = MangaListAdapter(get(), this)
initListMode(settings.listMode)
recyclerView.adapter = adapter
recyclerView.addOnScrollListener(PaginationScrollListener(4, this))
binding.recyclerView.adapter = adapter
binding.recyclerView.addOnScrollListener(PaginationScrollListener(4, this))
settings.subscribe(this)
toolbar.inflateMenu(R.menu.opt_list_sheet)
toolbar.setOnMenuItemClickListener(this)
toolbar.setNavigationOnClickListener {
binding.toolbar.inflateMenu(R.menu.opt_list_sheet)
binding.toolbar.setOnMenuItemClickListener(this)
binding.toolbar.setNavigationOnClickListener {
dismiss()
}
if (dialog !is BottomSheetDialog) {
toolbar.isVisible = true
textView_title.isVisible = false
appbar.elevation = resources.getDimension(R.dimen.elevation_large)
binding.toolbar.isVisible = true
binding.textViewTitle.isVisible = false
binding.appbar.elevation = resources.getDimension(R.dimen.elevation_large)
}
if (savedInstanceState == null) {
onScrolledToEnd()
@@ -72,12 +78,12 @@ abstract class MangaListSheet : BaseBottomSheet(R.layout.sheet_list),
}
protected fun setTitle(title: CharSequence) {
toolbar.title = title
textView_title.text = title
binding.toolbar.title = title
binding.textViewTitle.text = title
}
protected fun setSubtitle(subtitle: CharSequence) {
toolbar.subtitle = subtitle
binding.toolbar.subtitle = subtitle
}
override fun onCreateDialog(savedInstanceState: Bundle?) =
@@ -90,13 +96,13 @@ abstract class MangaListSheet : BaseBottomSheet(R.layout.sheet_list),
override fun onStateChanged(bottomSheet: View, newState: Int) {
if (newState == BottomSheetBehavior.STATE_EXPANDED) {
toolbar.isVisible = true
textView_title.isVisible = false
appbar.elevation = elevation
binding.toolbar.isVisible = true
binding.textViewTitle.isVisible = false
binding.appbar.elevation = elevation
} else {
toolbar.isVisible = false
textView_title.isVisible = true
appbar.elevation = 0f
binding.toolbar.isVisible = false
binding.textViewTitle.isVisible = true
binding.appbar.elevation = 0f
}
}
})
@@ -114,7 +120,7 @@ abstract class MangaListSheet : BaseBottomSheet(R.layout.sheet_list),
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
when (key) {
AppSettings.KEY_LIST_MODE -> initListMode(settings.listMode)
AppSettings.KEY_GRID_SIZE -> UiUtils.SpanCountResolver.update(recyclerView)
AppSettings.KEY_GRID_SIZE -> UiUtils.SpanCountResolver.update(binding.recyclerView)
}
}
@@ -124,28 +130,29 @@ abstract class MangaListSheet : BaseBottomSheet(R.layout.sheet_list),
private fun onListChanged(list: List<Any>) {
adapter?.items = list
textView_holder.isVisible = list.isEmpty()
recyclerView.callOnScrollListeners()
binding.textViewHolder.isVisible = list.isEmpty()
binding.recyclerView.callOnScrollListeners()
}
private fun onError(e: Throwable) {
Snackbar.make(recyclerView, e.getDisplayMessage(resources), Snackbar.LENGTH_SHORT).show()
Snackbar.make(binding.recyclerView, e.getDisplayMessage(resources), Snackbar.LENGTH_SHORT)
.show()
}
private fun onLoadingStateChanged(isLoading: Boolean) {
progressBar.isVisible = isLoading && !recyclerView.hasItems
binding.progressBar.isVisible = isLoading && !binding.recyclerView.hasItems
if (isLoading) {
textView_holder.isVisible = false
binding.textViewHolder.isVisible = false
}
}
private fun initListMode(mode: ListMode) {
val ctx = context ?: return
val position = recyclerView.firstItem
recyclerView.layoutManager = null
recyclerView.clearItemDecorations()
recyclerView.removeOnLayoutChangeListener(UiUtils.SpanCountResolver)
recyclerView.layoutManager = when (mode) {
val position = binding.recyclerView.firstItem
binding.recyclerView.layoutManager = null
binding.recyclerView.clearItemDecorations()
binding.recyclerView.removeOnLayoutChangeListener(UiUtils.SpanCountResolver)
binding.recyclerView.layoutManager = when (mode) {
ListMode.GRID -> {
GridLayoutManager(ctx, UiUtils.resolveGridSpanCount(ctx)).apply {
spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
@@ -156,7 +163,7 @@ abstract class MangaListSheet : BaseBottomSheet(R.layout.sheet_list),
}
else -> LinearLayoutManager(ctx)
}
recyclerView.addItemDecoration(
binding.recyclerView.addItemDecoration(
when (mode) {
ListMode.LIST -> DividerItemDecoration(ctx, RecyclerView.VERTICAL)
ListMode.DETAILED_LIST,
@@ -166,9 +173,9 @@ abstract class MangaListSheet : BaseBottomSheet(R.layout.sheet_list),
}
)
if (mode == ListMode.GRID) {
recyclerView.addOnLayoutChangeListener(UiUtils.SpanCountResolver)
binding.recyclerView.addOnLayoutChangeListener(UiUtils.SpanCountResolver)
}
adapter?.notifyDataSetChanged()
recyclerView.firstItem = position
binding.recyclerView.firstItem = position
}
}

View File

@@ -2,11 +2,11 @@ package org.koitharu.kotatsu.list.ui.adapter
import coil.ImageLoader
import coil.request.Disposable
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateLayoutContainer
import kotlinx.android.synthetic.main.item_manga_list.*
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.databinding.ItemMangaGridBinding
import org.koitharu.kotatsu.list.ui.model.MangaGridModel
import org.koitharu.kotatsu.utils.ext.enqueueWith
import org.koitharu.kotatsu.utils.ext.newImageRequest
@@ -14,7 +14,9 @@ import org.koitharu.kotatsu.utils.ext.newImageRequest
fun mangaGridItemAD(
coil: ImageLoader,
clickListener: OnListItemClickListener<Manga>
) = adapterDelegateLayoutContainer<MangaGridModel, Any>(R.layout.item_manga_grid) {
) = adapterDelegateViewBinding<MangaGridModel, Any, ItemMangaGridBinding>(
{ inflater, parent -> ItemMangaGridBinding.inflate(inflater, parent, false) }
) {
var imageRequest: Disposable? = null
@@ -26,9 +28,9 @@ fun mangaGridItemAD(
}
bind {
textView_title.text = item.title
binding.textViewTitle.text = item.title
imageRequest?.dispose()
imageRequest = imageView_cover.newImageRequest(item.coverUrl)
imageRequest = binding.imageViewCover.newImageRequest(item.coverUrl)
.placeholder(R.drawable.ic_placeholder)
.fallback(R.drawable.ic_placeholder)
.error(R.drawable.ic_placeholder)
@@ -37,6 +39,6 @@ fun mangaGridItemAD(
onViewRecycled {
imageRequest?.dispose()
imageView_cover.setImageDrawable(null)
binding.imageViewCover.setImageDrawable(null)
}
}

View File

@@ -2,11 +2,11 @@ package org.koitharu.kotatsu.list.ui.adapter
import coil.ImageLoader
import coil.request.Disposable
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateLayoutContainer
import kotlinx.android.synthetic.main.item_manga_list_details.*
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.databinding.ItemMangaListDetailsBinding
import org.koitharu.kotatsu.list.ui.model.MangaListDetailedModel
import org.koitharu.kotatsu.utils.ext.enqueueWith
import org.koitharu.kotatsu.utils.ext.newImageRequest
@@ -15,7 +15,9 @@ import org.koitharu.kotatsu.utils.ext.textAndVisible
fun mangaListDetailedItemAD(
coil: ImageLoader,
clickListener: OnListItemClickListener<Manga>
) = adapterDelegateLayoutContainer<MangaListDetailedModel, Any>(R.layout.item_manga_list_details) {
) = adapterDelegateViewBinding<MangaListDetailedModel, Any, ItemMangaListDetailsBinding>(
{ inflater, parent -> ItemMangaListDetailsBinding.inflate(inflater, parent, false) }
) {
var imageRequest: Disposable? = null
@@ -28,19 +30,19 @@ fun mangaListDetailedItemAD(
bind {
imageRequest?.dispose()
textView_title.text = item.title
textView_subtitle.textAndVisible = item.subtitle
imageRequest = imageView_cover.newImageRequest(item.coverUrl)
binding.textViewTitle.text = item.title
binding.textViewSubtitle.textAndVisible = item.subtitle
imageRequest = binding.imageViewCover.newImageRequest(item.coverUrl)
.placeholder(R.drawable.ic_placeholder)
.fallback(R.drawable.ic_placeholder)
.error(R.drawable.ic_placeholder)
.enqueueWith(coil)
textView_rating.textAndVisible = item.rating
textView_tags.text = item.tags
binding.textViewRating.textAndVisible = item.rating
binding.textViewTags.text = item.tags
}
onViewRecycled {
imageRequest?.dispose()
imageView_cover.setImageDrawable(null)
binding.imageViewCover.setImageDrawable(null)
}
}

View File

@@ -2,11 +2,11 @@ package org.koitharu.kotatsu.list.ui.adapter
import coil.ImageLoader
import coil.request.Disposable
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateLayoutContainer
import kotlinx.android.synthetic.main.item_manga_list.*
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.databinding.ItemMangaListBinding
import org.koitharu.kotatsu.list.ui.model.MangaListModel
import org.koitharu.kotatsu.utils.ext.enqueueWith
import org.koitharu.kotatsu.utils.ext.newImageRequest
@@ -15,7 +15,9 @@ import org.koitharu.kotatsu.utils.ext.textAndVisible
fun mangaListItemAD(
coil: ImageLoader,
clickListener: OnListItemClickListener<Manga>
) = adapterDelegateLayoutContainer<MangaListModel, Any>(R.layout.item_manga_list) {
) = adapterDelegateViewBinding<MangaListModel, Any, ItemMangaListBinding>(
{ inflater, parent -> ItemMangaListBinding.inflate(inflater, parent, false) }
) {
var imageRequest: Disposable? = null
@@ -28,9 +30,9 @@ fun mangaListItemAD(
bind {
imageRequest?.dispose()
textView_title.text = item.title
textView_subtitle.textAndVisible = item.subtitle
imageRequest = imageView_cover.newImageRequest(item.coverUrl)
binding.textViewTitle.text = item.title
binding.textViewSubtitle.textAndVisible = item.subtitle
imageRequest = binding.imageViewCover.newImageRequest(item.coverUrl)
.placeholder(R.drawable.ic_placeholder)
.fallback(R.drawable.ic_placeholder)
.error(R.drawable.ic_placeholder)
@@ -39,6 +41,6 @@ fun mangaListItemAD(
onViewRecycled {
imageRequest?.dispose()
imageView_cover.setImageDrawable(null)
binding.imageViewCover.setImageDrawable(null)
}
}

View File

@@ -14,7 +14,7 @@ class FilterAdapter(
tags: List<MangaTag> = emptyList(),
state: MangaFilter?,
private val listener: OnFilterChangedListener
) : RecyclerView.Adapter<BaseViewHolder<*, Boolean>>() {
) : RecyclerView.Adapter<BaseViewHolder<*, Boolean, *>>() {
private val sortOrders = ArrayList<SortOrder>(sortOrders)
private val tags = ArrayList(Collections.singletonList(null) + tags)
@@ -37,7 +37,7 @@ class FilterAdapter(
override fun getItemCount() = sortOrders.size + tags.size
override fun onBindViewHolder(holder: BaseViewHolder<*, Boolean>, position: Int) {
override fun onBindViewHolder(holder: BaseViewHolder<*, Boolean, *>, position: Int) {
when (holder) {
is FilterSortHolder -> {
val item = sortOrders[position]

View File

@@ -1,16 +1,18 @@
package org.koitharu.kotatsu.list.ui.filter
import android.view.LayoutInflater
import android.view.ViewGroup
import kotlinx.android.synthetic.main.item_checkable_single.*
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.list.BaseViewHolder
import org.koitharu.kotatsu.core.model.SortOrder
import org.koitharu.kotatsu.databinding.ItemCheckableSingleBinding
class FilterSortHolder(parent: ViewGroup) :
BaseViewHolder<SortOrder, Boolean>(parent, R.layout.item_checkable_single) {
BaseViewHolder<SortOrder, Boolean, ItemCheckableSingleBinding>(
ItemCheckableSingleBinding.inflate(LayoutInflater.from(parent.context), parent, false)
) {
override fun onBind(data: SortOrder, extra: Boolean) {
radio.setText(data.titleRes)
radio.isChecked = extra
binding.radio.setText(data.titleRes)
binding.radio.isChecked = extra
}
}

View File

@@ -1,16 +1,19 @@
package org.koitharu.kotatsu.list.ui.filter
import android.view.LayoutInflater
import android.view.ViewGroup
import kotlinx.android.synthetic.main.item_checkable_single.*
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.list.BaseViewHolder
import org.koitharu.kotatsu.core.model.MangaTag
import org.koitharu.kotatsu.databinding.ItemCheckableSingleBinding
class FilterTagHolder(parent: ViewGroup) :
BaseViewHolder<MangaTag?, Boolean>(parent, R.layout.item_checkable_single) {
BaseViewHolder<MangaTag?, Boolean, ItemCheckableSingleBinding>(
ItemCheckableSingleBinding.inflate(LayoutInflater.from(parent.context), parent, false)
) {
override fun onBind(data: MangaTag?, extra: Boolean) {
radio.text = data?.title ?: context.getString(R.string.all)
radio.isChecked = extra
binding.radio.text = data?.title ?: context.getString(R.string.all)
binding.radio.isChecked = extra
}
}

View File

@@ -1,7 +1,7 @@
package org.koitharu.kotatsu.local
import org.koin.android.ext.koin.androidContext
import org.koin.android.viewmodel.dsl.viewModel
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.core.qualifier.named
import org.koin.dsl.module
import org.koitharu.kotatsu.core.model.MangaSource

View File

@@ -11,8 +11,7 @@ import androidx.activity.result.ActivityResultCallback
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.fragment_list.*
import org.koin.android.viewmodel.ext.android.viewModel
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.Manga
@@ -54,7 +53,9 @@ class LocalListFragment : MangaListFragment(), ActivityResultCallback<Uri> {
e.printStackTrace()
}
Snackbar.make(
recyclerView, R.string.operation_not_supported, Snackbar.LENGTH_SHORT
binding.recyclerView,
R.string.operation_not_supported,
Snackbar.LENGTH_SHORT
).show()
}
true
@@ -64,12 +65,12 @@ class LocalListFragment : MangaListFragment(), ActivityResultCallback<Uri> {
}
override fun getTitle(): CharSequence? {
return getString(R.string.local_storage)
return context?.getString(R.string.local_storage)
}
override fun setUpEmptyListHolder() {
textView_holder.setText(R.string.text_local_holder)
textView_holder.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0)
binding.textViewHolder.setText(R.string.text_local_holder)
binding.textViewHolder.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0)
}
override fun onActivityResult(result: Uri?) {
@@ -102,7 +103,7 @@ class LocalListFragment : MangaListFragment(), ActivityResultCallback<Uri> {
private fun onItemRemoved(item: Manga) {
Snackbar.make(
recyclerView, getString(
binding.recyclerView, getString(
R.string._s_deleted_from_local_storage,
item.title.ellipsize(16)
), Snackbar.LENGTH_SHORT

View File

@@ -1,6 +1,6 @@
package org.koitharu.kotatsu.main
import org.koin.android.viewmodel.dsl.viewModel
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
import org.koitharu.kotatsu.main.ui.MainViewModel
import org.koitharu.kotatsu.main.ui.protect.ProtectViewModel

View File

@@ -15,12 +15,12 @@ import androidx.fragment.app.Fragment
import androidx.swiperefreshlayout.widget.CircularProgressDrawable
import com.google.android.material.navigation.NavigationView
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.activity_main.*
import org.koin.android.viewmodel.ext.android.viewModel
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseActivity
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.prefs.AppSection
import org.koitharu.kotatsu.databinding.ActivityMainBinding
import org.koitharu.kotatsu.favourites.ui.FavouritesContainerFragment
import org.koitharu.kotatsu.history.ui.HistoryListFragment
import org.koitharu.kotatsu.local.ui.LocalListFragment
@@ -37,7 +37,8 @@ import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.resolveDp
import java.io.Closeable
class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener,
class MainActivity : BaseActivity<ActivityMainBinding>(),
NavigationView.OnNavigationItemSelectedListener,
View.OnClickListener {
private val viewModel by viewModel<MainViewModel>()
@@ -47,21 +48,27 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setContentView(ActivityMainBinding.inflate(layoutInflater))
drawerToggle =
ActionBarDrawerToggle(this, drawer, toolbar, R.string.open_menu, R.string.close_menu)
drawer.addDrawerListener(drawerToggle)
ActionBarDrawerToggle(
this,
binding.drawer,
binding.toolbar,
R.string.open_menu,
R.string.close_menu
)
binding.drawer.addDrawerListener(drawerToggle)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
navigationView.setNavigationItemSelectedListener(this)
binding.navigationView.setNavigationItemSelectedListener(this)
with(fab) {
with(binding.fab) {
imageTintList = ColorStateList.valueOf(Color.WHITE)
setOnClickListener(this@MainActivity)
}
supportFragmentManager.findFragmentById(R.id.container)?.let {
fab.isVisible = it is HistoryListFragment
binding.fab.isVisible = it is HistoryListFragment
} ?: run {
openDefaultSection()
}
@@ -94,8 +101,8 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
override fun onBackPressed() {
if (drawer.isDrawerOpen(navigationView)) {
drawer.closeDrawer(navigationView)
if (binding.drawer.isDrawerOpen(binding.navigationView)) {
binding.drawer.closeDrawer(binding.navigationView)
} else {
super.onBackPressed()
}
@@ -148,42 +155,43 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
else -> return false
}
drawer.closeDrawers()
binding.drawer.closeDrawers()
return true
}
private fun onOpenReader(state: ReaderState) {
val options = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
ActivityOptions.makeClipRevealAnimation(
fab, 0, 0, fab.measuredWidth, fab.measuredHeight
binding.fab, 0, 0, binding.fab.measuredWidth, binding.fab.measuredHeight
)
} else {
ActivityOptions.makeScaleUpAnimation(
fab, 0, 0, fab.measuredWidth, fab.measuredHeight
binding.fab, 0, 0, binding.fab.measuredWidth, binding.fab.measuredHeight
)
}
startActivity(ReaderActivity.newIntent(this, state), options?.toBundle())
}
private fun onError(e: Throwable) {
Snackbar.make(container, e.getDisplayMessage(resources), Snackbar.LENGTH_SHORT).show()
Snackbar.make(binding.container, e.getDisplayMessage(resources), Snackbar.LENGTH_SHORT)
.show()
}
private fun onLoadingStateChanged(isLoading: Boolean) {
fab.isEnabled = !isLoading
binding.fab.isEnabled = !isLoading
if (isLoading) {
fab.setImageDrawable(CircularProgressDrawable(this).also {
binding.fab.setImageDrawable(CircularProgressDrawable(this).also {
it.setColorSchemeColors(Color.WHITE)
it.strokeWidth = resources.resolveDp(2f)
it.start()
})
} else {
fab.setImageResource(R.drawable.ic_read_fill)
binding.fab.setImageResource(R.drawable.ic_read_fill)
}
}
private fun updateSideMenu(remoteSources: List<MangaSource>) {
val submenu = navigationView.menu.findItem(R.id.nav_remote_sources).subMenu
val submenu = binding.navigationView.menu.findItem(R.id.nav_remote_sources).subMenu
submenu.removeGroup(R.id.group_remote_sources)
remoteSources.forEachIndexed { index, source ->
submenu.add(R.id.group_remote_sources, source.ordinal, index, source.title)
@@ -194,19 +202,19 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
private fun openDefaultSection() {
when (viewModel.defaultSection) {
AppSection.LOCAL -> {
navigationView.setCheckedItem(R.id.nav_local_storage)
binding.navigationView.setCheckedItem(R.id.nav_local_storage)
setPrimaryFragment(LocalListFragment.newInstance())
}
AppSection.FAVOURITES -> {
navigationView.setCheckedItem(R.id.nav_favourites)
binding.navigationView.setCheckedItem(R.id.nav_favourites)
setPrimaryFragment(FavouritesContainerFragment.newInstance())
}
AppSection.HISTORY -> {
navigationView.setCheckedItem(R.id.nav_history)
binding.navigationView.setCheckedItem(R.id.nav_history)
setPrimaryFragment(HistoryListFragment.newInstance())
}
AppSection.FEED -> {
navigationView.setCheckedItem(R.id.nav_feed)
binding.navigationView.setCheckedItem(R.id.nav_feed)
setPrimaryFragment(FeedFragment.newInstance())
}
}
@@ -216,6 +224,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
supportFragmentManager.beginTransaction()
.replace(R.id.container, fragment)
.commit()
fab.isVisible = fragment is HistoryListFragment
binding.fab.isVisible = fragment is HistoryListFragment
}
}

View File

@@ -10,21 +10,22 @@ import android.view.Menu
import android.view.MenuItem
import android.view.inputmethod.EditorInfo
import android.widget.TextView
import kotlinx.android.synthetic.main.activity_protect.*
import org.koin.android.viewmodel.ext.android.viewModel
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseActivity
import org.koitharu.kotatsu.databinding.ActivityProtectBinding
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
class ProtectActivity : BaseActivity(), TextView.OnEditorActionListener, TextWatcher {
class ProtectActivity : BaseActivity<ActivityProtectBinding>(), TextView.OnEditorActionListener,
TextWatcher {
private val viewModel by viewModel<ProtectViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_protect)
edit_password.setOnEditorActionListener(this)
edit_password.addTextChangedListener(this)
setContentView(ActivityProtectBinding.inflate(layoutInflater))
binding.editPassword.setOnEditorActionListener(this)
binding.editPassword.addTextChangedListener(this)
supportActionBar?.run {
setDisplayHomeAsUpEnabled(true)
setHomeAsUpIndicator(R.drawable.ic_cross)
@@ -42,7 +43,7 @@ class ProtectActivity : BaseActivity(), TextView.OnEditorActionListener, TextWat
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.action_done -> {
viewModel.tryUnlock(edit_password.text?.toString().orEmpty())
viewModel.tryUnlock(binding.editPassword.text.toString().orEmpty())
true
}
else -> super.onOptionsItemSelected(item)
@@ -50,7 +51,7 @@ class ProtectActivity : BaseActivity(), TextView.OnEditorActionListener, TextWat
override fun onEditorAction(v: TextView?, actionId: Int, event: KeyEvent?): Boolean {
return if (actionId == EditorInfo.IME_ACTION_DONE) {
viewModel.tryUnlock(edit_password.text?.toString().orEmpty())
viewModel.tryUnlock(binding.editPassword.text.toString().orEmpty())
true
} else {
false
@@ -62,7 +63,7 @@ class ProtectActivity : BaseActivity(), TextView.OnEditorActionListener, TextWat
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit
override fun afterTextChanged(s: Editable?) {
layout_password.error = null
binding.layoutPassword.error = null
}
private fun onUnlockSuccess(unit: Unit) {
@@ -70,11 +71,11 @@ class ProtectActivity : BaseActivity(), TextView.OnEditorActionListener, TextWat
}
private fun onError(e: Throwable) {
layout_password.error = e.getDisplayMessage(resources)
binding.layoutPassword.error = e.getDisplayMessage(resources)
}
private fun onLoadingStateChanged(isLoading: Boolean) {
layout_password.isEnabled = !isLoading
binding.layoutPassword.isEnabled = !isLoading
}
companion object {

View File

@@ -1,6 +1,6 @@
package org.koitharu.kotatsu.reader
import org.koin.android.viewmodel.dsl.viewModel
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
import org.koitharu.kotatsu.base.domain.MangaDataRepository
import org.koitharu.kotatsu.local.data.PagesCache

View File

@@ -1,22 +1,29 @@
package org.koitharu.kotatsu.reader.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.dialog_chapters.*
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.AlertDialogFragment
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.model.MangaChapter
import org.koitharu.kotatsu.databinding.DialogChaptersBinding
import org.koitharu.kotatsu.details.ui.adapter.ChaptersAdapter
import org.koitharu.kotatsu.utils.ext.withArgs
class ChaptersDialog : AlertDialogFragment(R.layout.dialog_chapters),
class ChaptersDialog : AlertDialogFragment<DialogChaptersBinding>(),
OnListItemClickListener<MangaChapter> {
override fun onInflateView(
inflater: LayoutInflater,
container: ViewGroup?
) = DialogChaptersBinding.inflate(inflater, container, false)
override fun onBuildDialog(builder: AlertDialog.Builder) {
builder.setTitle(R.string.chapters)
.setNegativeButton(R.string.close, null)
@@ -24,13 +31,13 @@ class ChaptersDialog : AlertDialogFragment(R.layout.dialog_chapters),
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
recyclerView_chapters.addItemDecoration(
binding.recyclerViewChapters.addItemDecoration(
DividerItemDecoration(
requireContext(),
RecyclerView.VERTICAL
)
)
recyclerView_chapters.adapter = ChaptersAdapter(this).apply {
binding.recyclerViewChapters.adapter = ChaptersAdapter(this).apply {
// arguments?.getParcelableArrayList<MangaChapter>(ARG_CHAPTERS)?.let(this::setItems)
// currentChapterId = arguments?.getLong(ARG_CURRENT_ID, 0L)?.takeUnless { it == 0L }
}

View File

@@ -19,14 +19,13 @@ import androidx.core.view.*
import androidx.fragment.app.commit
import androidx.lifecycle.lifecycleScope
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.activity_reader.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import org.koin.android.viewmodel.ext.android.viewModel
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseFullscreenActivity
import org.koitharu.kotatsu.core.model.Manga
@@ -35,6 +34,7 @@ import org.koitharu.kotatsu.core.model.MangaHistory
import org.koitharu.kotatsu.core.model.MangaPage
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.ReaderMode
import org.koitharu.kotatsu.databinding.ActivityReaderBinding
import org.koitharu.kotatsu.reader.ui.base.AbstractReader
import org.koitharu.kotatsu.reader.ui.reversed.ReversedReaderFragment
import org.koitharu.kotatsu.reader.ui.standard.PagerReaderFragment
@@ -48,7 +48,8 @@ import org.koitharu.kotatsu.utils.ShareHelper
import org.koitharu.kotatsu.utils.anim.Motion
import org.koitharu.kotatsu.utils.ext.*
class ReaderActivity : BaseFullscreenActivity(), ChaptersDialog.OnChapterChangeListener,
class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
ChaptersDialog.OnChapterChangeListener,
GridTouchHelper.OnGridTouchListener, OnPageSelectListener, ReaderConfigDialog.Callback,
ReaderListener, SharedPreferences.OnSharedPreferenceChangeListener,
ActivityResultCallback<Boolean>, OnApplyWindowInsetsListener {
@@ -65,16 +66,16 @@ class ReaderActivity : BaseFullscreenActivity(), ChaptersDialog.OnChapterChangeL
private var isVolumeKeysSwitchEnabled = false
private val reader
get() = supportFragmentManager.findFragmentById(R.id.container) as? AbstractReader
get() = supportFragmentManager.findFragmentById(R.id.container) as? AbstractReader<*>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_reader)
setContentView(ActivityReaderBinding.inflate(layoutInflater))
supportActionBar?.setDisplayHomeAsUpEnabled(true)
touchHelper = GridTouchHelper(this, this)
orientationHelper = ScreenOrientationHelper(this)
toolbar_bottom.inflateMenu(R.menu.opt_reader_bottom)
toolbar_bottom.setOnMenuItemClickListener(::onOptionsItemSelected)
binding.toolbarBottom.inflateMenu(R.menu.opt_reader_bottom)
binding.toolbarBottom.setOnMenuItemClickListener(::onOptionsItemSelected)
@Suppress("RemoveExplicitTypeArguments")
state = savedInstanceState?.getParcelable<ReaderState>(EXTRA_STATE)
@@ -91,13 +92,13 @@ class ReaderActivity : BaseFullscreenActivity(), ChaptersDialog.OnChapterChangeL
getString(R.string.chapter_d_of_d, state.chapter?.number ?: 0, size)
}
ViewCompat.setOnApplyWindowInsetsListener(rootLayout, this)
ViewCompat.setOnApplyWindowInsetsListener(binding.rootLayout, this)
settings.subscribe(this)
loadSwitchSettings()
orientationHelper.observeAutoOrientation()
.onEach {
toolbar_bottom.menu.findItem(R.id.action_screen_rotate).isVisible = !it
binding.toolbarBottom.menu.findItem(R.id.action_screen_rotate).isVisible = !it
}.launchIn(lifecycleScope)
if (savedInstanceState == null) {
@@ -133,14 +134,14 @@ class ReaderActivity : BaseFullscreenActivity(), ChaptersDialog.OnChapterChangeL
}
}
}
toolbar_bottom.menu.findItem(R.id.action_reader_mode).setIcon(
binding.toolbarBottom.menu.findItem(R.id.action_reader_mode).setIcon(
when (mode) {
ReaderMode.WEBTOON -> R.drawable.ic_script
ReaderMode.REVERSED -> R.drawable.ic_read_reversed
ReaderMode.STANDARD -> R.drawable.ic_book_page
}
)
appbar_top.postDelayed(1000) {
binding.appbarTop.postDelayed(1000) {
setUiIsVisible(false)
}
}
@@ -242,8 +243,8 @@ class ReaderActivity : BaseFullscreenActivity(), ChaptersDialog.OnChapterChangeL
override fun onLoadingStateChanged(isLoading: Boolean) {
val hasPages = reader?.hasItems == true
layout_loading.isVisible = isLoading && !hasPages
progressBar_bottom.isVisible = isLoading && hasPages
binding.layoutLoading.isVisible = isLoading && !hasPages
binding.progressBarBottom.isVisible = isLoading && hasPages
}
override fun onError(e: Throwable) {
@@ -262,7 +263,7 @@ class ReaderActivity : BaseFullscreenActivity(), ChaptersDialog.OnChapterChangeL
override fun onGridTouch(area: Int) {
when (area) {
GridTouchHelper.AREA_CENTER -> {
setUiIsVisible(!appbar_top.isVisible)
setUiIsVisible(!binding.appbarTop.isVisible)
}
GridTouchHelper.AREA_TOP,
GridTouchHelper.AREA_LEFT -> if (isTapSwitchEnabled) {
@@ -276,12 +277,12 @@ class ReaderActivity : BaseFullscreenActivity(), ChaptersDialog.OnChapterChangeL
}
override fun onProcessTouch(rawX: Int, rawY: Int): Boolean {
return if (appbar_top.hasGlobalPoint(rawX, rawY)
|| appbar_bottom.hasGlobalPoint(rawX, rawY)
return if (binding.appbarTop.hasGlobalPoint(rawX, rawY)
|| binding.appbarBottom.hasGlobalPoint(rawX, rawY)
) {
false
} else {
val targets = rootLayout.hitTest(rawX, rawY)
val targets = binding.rootLayout.hitTest(rawX, rawY)
targets.none { it.hasOnClickListeners() }
}
}
@@ -318,7 +319,7 @@ class ReaderActivity : BaseFullscreenActivity(), ChaptersDialog.OnChapterChangeL
true
}
KeyEvent.KEYCODE_DPAD_CENTER -> {
setUiIsVisible(!appbar_top.isVisible)
setUiIsVisible(!binding.appbarTop.isVisible)
true
}
else -> super.onKeyDown(keyCode, event)
@@ -350,14 +351,14 @@ class ReaderActivity : BaseFullscreenActivity(), ChaptersDialog.OnChapterChangeL
private fun onPageSaved(uri: Uri?) {
if (uri != null) {
Snackbar.make(container, R.string.page_saved, Snackbar.LENGTH_LONG)
.setAnchorView(appbar_bottom)
Snackbar.make(binding.container, R.string.page_saved, Snackbar.LENGTH_LONG)
.setAnchorView(binding.appbarBottom)
.setAction(R.string.share) {
ShareHelper.shareImage(this, uri)
}.show()
} else {
Snackbar.make(container, R.string.error_occurred, Snackbar.LENGTH_SHORT)
.setAnchorView(appbar_bottom)
Snackbar.make(binding.container, R.string.error_occurred, Snackbar.LENGTH_SHORT)
.setAnchorView(binding.appbarBottom)
.show()
}
}
@@ -385,14 +386,14 @@ class ReaderActivity : BaseFullscreenActivity(), ChaptersDialog.OnChapterChangeL
}
private fun setUiIsVisible(isUiVisible: Boolean) {
if (appbar_top.isVisible != isUiVisible) {
if (binding.appbarTop.isVisible != isUiVisible) {
if (isUiVisible) {
appbar_top.showAnimated(Motion.SlideTop)
appbar_bottom.showAnimated(Motion.SlideBottom)
binding.appbarTop.showAnimated(Motion.SlideTop)
binding.appbarBottom.showAnimated(Motion.SlideBottom)
showSystemUI()
} else {
appbar_top.hideAnimated(Motion.SlideTop)
appbar_bottom.hideAnimated(Motion.SlideBottom)
binding.appbarTop.hideAnimated(Motion.SlideTop)
binding.appbarBottom.hideAnimated(Motion.SlideBottom)
hideSystemUI()
}
}
@@ -400,12 +401,12 @@ class ReaderActivity : BaseFullscreenActivity(), ChaptersDialog.OnChapterChangeL
override fun onApplyWindowInsets(v: View, insets: WindowInsetsCompat): WindowInsetsCompat {
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
appbar_top.updatePadding(
binding.appbarTop.updatePadding(
top = systemBars.top,
right = systemBars.right,
left = systemBars.left
)
appbar_bottom.updatePadding(
binding.appbarBottom.updatePadding(
bottom = systemBars.bottom,
right = systemBars.right,
left = systemBars.left

View File

@@ -1,20 +1,27 @@
package org.koitharu.kotatsu.reader.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.FragmentManager
import kotlinx.android.synthetic.main.dialog_reader_config.*
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.AlertDialogFragment
import org.koitharu.kotatsu.core.prefs.ReaderMode
import org.koitharu.kotatsu.databinding.DialogReaderConfigBinding
import org.koitharu.kotatsu.utils.ext.withArgs
class ReaderConfigDialog : AlertDialogFragment(R.layout.dialog_reader_config),
class ReaderConfigDialog : AlertDialogFragment<DialogReaderConfigBinding>(),
View.OnClickListener {
private lateinit var mode: ReaderMode
override fun onInflateView(
inflater: LayoutInflater,
container: ViewGroup?
) = DialogReaderConfigBinding.inflate(inflater, container, false)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mode = arguments?.getInt(ARG_MODE)
@@ -29,14 +36,14 @@ class ReaderConfigDialog : AlertDialogFragment(R.layout.dialog_reader_config),
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
button_standard.isChecked = mode == ReaderMode.STANDARD
button_reversed.isChecked = mode == ReaderMode.REVERSED
button_webtoon.isChecked = mode == ReaderMode.WEBTOON
binding.buttonStandard.isChecked = mode == ReaderMode.STANDARD
binding.buttonReversed.isChecked = mode == ReaderMode.REVERSED
binding.buttonWebtoon.isChecked = mode == ReaderMode.WEBTOON
button_ok.setOnClickListener(this)
button_standard.setOnClickListener(this)
button_reversed.setOnClickListener(this)
button_webtoon.setOnClickListener(this)
binding.buttonOk.setOnClickListener(this)
binding.buttonStandard.setOnClickListener(this)
binding.buttonReversed.setOnClickListener(this)
binding.buttonWebtoon.setOnClickListener(this)
}
override fun onClick(v: View) {

View File

@@ -2,7 +2,7 @@ package org.koitharu.kotatsu.reader.ui
import android.os.Parcelable
import kotlinx.android.parcel.IgnoredOnParcel
import kotlinx.android.parcel.Parcelize
import kotlinx.parcelize.Parcelize
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaChapter

View File

@@ -7,15 +7,16 @@ import androidx.fragment.app.commit
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseActivity
import org.koitharu.kotatsu.databinding.ActivitySettingsSimpleBinding
import org.koitharu.kotatsu.settings.MainSettingsFragment
import org.koitharu.kotatsu.settings.NetworkSettingsFragment
import org.koitharu.kotatsu.settings.ReaderSettingsFragment
class SimpleSettingsActivity : BaseActivity() {
class SimpleSettingsActivity : BaseActivity<ActivitySettingsSimpleBinding>() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_settings_simple)
setContentView(ActivitySettingsSimpleBinding.inflate(layoutInflater))
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportFragmentManager.commit {
replace(

View File

@@ -6,6 +6,7 @@ import android.view.View
import androidx.annotation.CallSuper
import androidx.collection.LongSparseArray
import androidx.core.view.postDelayed
import androidx.viewbinding.ViewBinding
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -20,8 +21,7 @@ import org.koitharu.kotatsu.reader.ui.ReaderState
import org.koitharu.kotatsu.utils.ext.associateByLong
import org.koitharu.kotatsu.utils.ext.viewLifecycleScope
abstract class AbstractReader(contentLayoutId: Int) : BaseFragment(contentLayoutId),
OnBoundsScrollListener {
abstract class AbstractReader<B : ViewBinding> : BaseFragment<B>(), OnBoundsScrollListener {
protected lateinit var manga: Manga
private set
@@ -30,11 +30,11 @@ abstract class AbstractReader(contentLayoutId: Int) : BaseFragment(contentLayout
PageLoader()
}
protected val pages = ArrayDeque<ReaderPage>()
protected var adapter: BaseReaderAdapter? = null
protected var readerAdapter: BaseReaderAdapter? = null
private set
val itemsCount: Int
get() = adapter?.itemCount ?: 0
get() = readerAdapter?.itemCount ?: 0
val hasItems: Boolean
get() = itemsCount != 0
@@ -52,7 +52,7 @@ abstract class AbstractReader(contentLayoutId: Int) : BaseFragment(contentLayout
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
adapter = onCreateAdapter(pages)
readerAdapter = onCreateAdapter(pages)
@Suppress("RemoveExplicitTypeArguments")
val state = savedInstanceState?.getParcelable<ReaderState>(ARG_STATE)
?: requireArguments().getParcelable<ReaderState>(ARG_STATE)!!
@@ -61,7 +61,7 @@ abstract class AbstractReader(contentLayoutId: Int) : BaseFragment(contentLayout
it.mapIndexedTo(pages) { i, p ->
ReaderPage.from(p, i, state.chapterId)
}
adapter?.notifyDataSetChanged()
readerAdapter?.notifyDataSetChanged()
setCurrentItem(state.page, false)
if (state.scroll != 0) {
restorePageScroll(state.page, state.scroll)
@@ -100,7 +100,7 @@ abstract class AbstractReader(contentLayoutId: Int) : BaseFragment(contentLayout
pages.addAll(0, it.mapIndexed { i, p ->
ReaderPage.from(p, i, prevChapterId)
})
adapter?.notifyItemsPrepended(it.size)
readerAdapter?.notifyItemsPrepended(it.size)
view?.postDelayed(500) {
trimEnd()
}
@@ -115,7 +115,7 @@ abstract class AbstractReader(contentLayoutId: Int) : BaseFragment(contentLayout
pages.addAll(it.mapIndexed { i, p ->
ReaderPage.from(p, i, nextChapterId)
})
adapter?.notifyItemsAppended(it.size)
readerAdapter?.notifyItemsAppended(it.size)
view?.postDelayed(500) {
trimStart()
}
@@ -123,7 +123,7 @@ abstract class AbstractReader(contentLayoutId: Int) : BaseFragment(contentLayout
}
override fun onDestroyView() {
adapter = null
readerAdapter = null
super.onDestroyView()
}
@@ -134,7 +134,7 @@ abstract class AbstractReader(contentLayoutId: Int) : BaseFragment(contentLayout
@CallSuper
open fun recreateAdapter() {
adapter = onCreateAdapter(pages)
readerAdapter = onCreateAdapter(pages)
}
fun getPages(): List<MangaPage>? {
@@ -212,13 +212,13 @@ abstract class AbstractReader(contentLayoutId: Int) : BaseFragment(contentLayout
val currentChapterId = pages.getOrNull(getCurrentItem())?.chapterId ?: 0L
if (chapterId != 0L && chapterId != currentChapterId) {
pages.clear()
adapter?.notifyDataSetChanged()
readerAdapter?.notifyDataSetChanged()
loadChapter(chapterId) {
pages.clear()
it.mapIndexedTo(pages) { i, p ->
ReaderPage.from(p, i, chapterId)
}
adapter?.notifyDataSetChanged()
readerAdapter?.notifyDataSetChanged()
setCurrentItem(
if (pageId == 0L) {
0

View File

@@ -5,14 +5,14 @@ import androidx.recyclerview.widget.RecyclerView
import org.koitharu.kotatsu.base.ui.list.BaseViewHolder
abstract class BaseReaderAdapter(protected val pages: List<ReaderPage>) :
RecyclerView.Adapter<BaseViewHolder<ReaderPage, Unit>>() {
RecyclerView.Adapter<BaseViewHolder<ReaderPage, Unit, *>>() {
init {
@Suppress("LeakingThis")
setHasStableIds(true)
}
override fun onBindViewHolder(holder: BaseViewHolder<ReaderPage, Unit>, position: Int) {
override fun onBindViewHolder(holder: BaseViewHolder<ReaderPage, Unit, *>, position: Int) {
val item = pages[position]
holder.bind(item, Unit)
}
@@ -42,11 +42,11 @@ abstract class BaseReaderAdapter(protected val pages: List<ReaderPage>) :
final override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): BaseViewHolder<ReaderPage, Unit> {
): BaseViewHolder<ReaderPage, Unit, *> {
return onCreateViewHolder(parent).also(this::onViewHolderCreated)
}
protected open fun onViewHolderCreated(holder: BaseViewHolder<ReaderPage, Unit>) = Unit
protected open fun onViewHolderCreated(holder: BaseViewHolder<ReaderPage, Unit, *>) = Unit
protected abstract fun onCreateViewHolder(parent: ViewGroup): BaseViewHolder<ReaderPage, Unit>
protected abstract fun onCreateViewHolder(parent: ViewGroup): BaseViewHolder<ReaderPage, Unit, *>
}

View File

@@ -1,7 +1,7 @@
package org.koitharu.kotatsu.reader.ui.base
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
import kotlinx.parcelize.Parcelize
import org.koitharu.kotatsu.core.model.MangaPage
import org.koitharu.kotatsu.core.model.MangaSource

View File

@@ -3,7 +3,6 @@ package org.koitharu.kotatsu.reader.ui.reversed
import android.graphics.PointF
import android.view.ViewGroup
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import kotlinx.android.synthetic.main.item_page.*
import org.koitharu.kotatsu.core.model.ZoomMode
import org.koitharu.kotatsu.reader.ui.PageLoader
import org.koitharu.kotatsu.reader.ui.standard.PageHolder
@@ -11,37 +10,39 @@ import org.koitharu.kotatsu.reader.ui.standard.PageHolder
class ReversedPageHolder(parent: ViewGroup, loader: PageLoader) : PageHolder(parent, loader) {
override fun onImageShowing(zoom: ZoomMode) {
ssiv.maxScale = 2f * maxOf(
ssiv.width / ssiv.sWidth.toFloat(),
ssiv.height / ssiv.sHeight.toFloat()
)
when (zoom) {
ZoomMode.FIT_CENTER -> {
ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE)
ssiv.resetScaleAndCenter()
}
ZoomMode.FIT_HEIGHT -> {
ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM)
ssiv.minScale = ssiv.height / ssiv.sHeight.toFloat()
ssiv.setScaleAndCenter(
ssiv.minScale,
PointF(ssiv.sWidth.toFloat(), ssiv.sHeight / 2f)
)
}
ZoomMode.FIT_WIDTH -> {
ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM)
ssiv.minScale = ssiv.width / ssiv.sWidth.toFloat()
ssiv.setScaleAndCenter(
ssiv.minScale,
PointF(ssiv.sWidth / 2f, 0f)
)
}
ZoomMode.KEEP_START -> {
ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE)
ssiv.setScaleAndCenter(
ssiv.maxScale,
PointF(ssiv.sWidth.toFloat(), 0f)
)
with(binding.ssiv) {
maxScale = 2f * maxOf(
width / sWidth.toFloat(),
height / sHeight.toFloat()
)
when (zoom) {
ZoomMode.FIT_CENTER -> {
setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE)
resetScaleAndCenter()
}
ZoomMode.FIT_HEIGHT -> {
setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM)
minScale = height / sHeight.toFloat()
setScaleAndCenter(
minScale,
PointF(sWidth.toFloat(), sHeight / 2f)
)
}
ZoomMode.FIT_WIDTH -> {
setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM)
minScale = width / sWidth.toFloat()
setScaleAndCenter(
minScale,
PointF(sWidth / 2f, 0f)
)
}
ZoomMode.KEEP_START -> {
setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE)
setScaleAndCenter(
maxScale,
PointF(sWidth.toFloat(), 0f)
)
}
}
}
}

View File

@@ -13,7 +13,7 @@ class ReversedPagesAdapter(
override fun onCreateViewHolder(parent: ViewGroup) = ReversedPageHolder(parent, loader)
override fun onBindViewHolder(holder: BaseViewHolder<ReaderPage, Unit>, position: Int) {
override fun onBindViewHolder(holder: BaseViewHolder<ReaderPage, Unit, *>, position: Int) {
super.onBindViewHolder(holder, reversed(position))
}

View File

@@ -3,11 +3,12 @@ package org.koitharu.kotatsu.reader.ui.reversed
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import kotlinx.android.synthetic.main.fragment_reader_standard.*
import android.view.ViewGroup
import org.koin.android.ext.android.inject
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.databinding.FragmentReaderStandardBinding
import org.koitharu.kotatsu.reader.ui.ReaderState
import org.koitharu.kotatsu.reader.ui.base.AbstractReader
import org.koitharu.kotatsu.reader.ui.base.BaseReaderAdapter
@@ -18,23 +19,30 @@ import org.koitharu.kotatsu.utils.ext.doOnPageChanged
import org.koitharu.kotatsu.utils.ext.swapAdapter
import org.koitharu.kotatsu.utils.ext.withArgs
class ReversedReaderFragment : AbstractReader(R.layout.fragment_reader_standard),
class ReversedReaderFragment : AbstractReader<FragmentReaderStandardBinding>(),
SharedPreferences.OnSharedPreferenceChangeListener {
private var paginationListener: PagerPaginationListener? = null
private val settings by inject<AppSettings>()
override fun onInflateView(
inflater: LayoutInflater,
container: ViewGroup?
) = FragmentReaderStandardBinding.inflate(inflater, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
paginationListener = PagerPaginationListener(adapter!!, 2, this)
pager.adapter = adapter
if (settings.readerAnimation) {
pager.setPageTransformer(ReversedPageAnimTransformer())
}
pager.offscreenPageLimit = 2
pager.registerOnPageChangeCallback(paginationListener!!)
pager.doOnPageChanged {
notifyPageChanged(reversed(it))
paginationListener = PagerPaginationListener(readerAdapter!!, 2, this)
with(binding.pager) {
adapter = readerAdapter
if (settings.readerAnimation) {
setPageTransformer(ReversedPageAnimTransformer())
}
offscreenPageLimit = 2
registerOnPageChangeCallback(paginationListener!!)
doOnPageChanged {
notifyPageChanged(reversed(it))
}
}
}
@@ -59,13 +67,13 @@ class ReversedReaderFragment : AbstractReader(R.layout.fragment_reader_standard)
override fun recreateAdapter() {
super.recreateAdapter()
pager.swapAdapter(adapter)
binding.pager.swapAdapter(readerAdapter)
}
override fun getCurrentItem() = reversed(pager.currentItem)
override fun getCurrentItem() = reversed(binding.pager.currentItem)
override fun setCurrentItem(position: Int, isSmooth: Boolean) {
pager.setCurrentItem(reversed(position), isSmooth)
binding.pager.setCurrentItem(reversed(position), isSmooth)
}
override fun getCurrentPageScroll() = 0
@@ -76,9 +84,9 @@ class ReversedReaderFragment : AbstractReader(R.layout.fragment_reader_standard)
when (key) {
AppSettings.KEY_READER_ANIMATION -> {
if (settings.readerAnimation) {
pager.setPageTransformer(PageAnimTransformer())
binding.pager.setPageTransformer(PageAnimTransformer())
} else {
pager.setPageTransformer(null)
binding.pager.setPageTransformer(null)
}
}
}

View File

@@ -2,29 +2,31 @@ package org.koitharu.kotatsu.reader.ui.standard
import android.graphics.PointF
import android.net.Uri
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import com.davemorrissey.labs.subscaleview.ImageSource
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import kotlinx.android.synthetic.main.item_page.*
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.list.BaseViewHolder
import org.koitharu.kotatsu.core.model.ZoomMode
import org.koitharu.kotatsu.databinding.ItemPageBinding
import org.koitharu.kotatsu.reader.ui.PageLoader
import org.koitharu.kotatsu.reader.ui.base.PageHolderDelegate
import org.koitharu.kotatsu.reader.ui.base.ReaderPage
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
open class PageHolder(parent: ViewGroup, loader: PageLoader) :
BaseViewHolder<ReaderPage, Unit>(parent, R.layout.item_page),
PageHolderDelegate.Callback, View.OnClickListener {
BaseViewHolder<ReaderPage, Unit, ItemPageBinding>(
ItemPageBinding.inflate(LayoutInflater.from(parent.context), parent, false)
), PageHolderDelegate.Callback, View.OnClickListener {
private val delegate = PageHolderDelegate(loader, this)
init {
ssiv.setOnImageEventListener(delegate)
button_retry.setOnClickListener(this)
binding.ssiv.setOnImageEventListener(delegate)
binding.buttonRetry.setOnClickListener(this)
}
override fun onBind(data: ReaderPage, extra: Unit) {
@@ -33,49 +35,49 @@ open class PageHolder(parent: ViewGroup, loader: PageLoader) :
override fun onRecycled() {
delegate.onRecycle()
ssiv.recycle()
binding.ssiv.recycle()
}
override fun onLoadingStarted() {
layout_error.isVisible = false
progressBar.isVisible = true
ssiv.recycle()
binding.layoutError.isVisible = false
binding.progressBar.isVisible = true
binding.ssiv.recycle()
}
override fun onImageReady(uri: Uri) {
ssiv.setImage(ImageSource.uri(uri))
binding.ssiv.setImage(ImageSource.uri(uri))
}
override fun onImageShowing(zoom: ZoomMode) {
ssiv.maxScale = 2f * maxOf(
ssiv.width / ssiv.sWidth.toFloat(),
ssiv.height / ssiv.sHeight.toFloat()
binding.ssiv.maxScale = 2f * maxOf(
binding.ssiv.width / binding.ssiv.sWidth.toFloat(),
binding.ssiv.height / binding.ssiv.sHeight.toFloat()
)
when (zoom) {
ZoomMode.FIT_CENTER -> {
ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE)
ssiv.resetScaleAndCenter()
binding.ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE)
binding.ssiv.resetScaleAndCenter()
}
ZoomMode.FIT_HEIGHT -> {
ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM)
ssiv.minScale = ssiv.height / ssiv.sHeight.toFloat()
ssiv.setScaleAndCenter(
ssiv.minScale,
PointF(0f, ssiv.sHeight / 2f)
binding.ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM)
binding.ssiv.minScale = binding.ssiv.height / binding.ssiv.sHeight.toFloat()
binding.ssiv.setScaleAndCenter(
binding.ssiv.minScale,
PointF(0f, binding.ssiv.sHeight / 2f)
)
}
ZoomMode.FIT_WIDTH -> {
ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM)
ssiv.minScale = ssiv.width / ssiv.sWidth.toFloat()
ssiv.setScaleAndCenter(
ssiv.minScale,
PointF(ssiv.sWidth / 2f, 0f)
binding.ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM)
binding.ssiv.minScale = binding.ssiv.width / binding.ssiv.sWidth.toFloat()
binding.ssiv.setScaleAndCenter(
binding.ssiv.minScale,
PointF(binding.ssiv.sWidth / 2f, 0f)
)
}
ZoomMode.KEEP_START -> {
ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE)
ssiv.setScaleAndCenter(
ssiv.maxScale,
binding.ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE)
binding.ssiv.setScaleAndCenter(
binding.ssiv.maxScale,
PointF(0f, 0f)
)
}
@@ -83,7 +85,7 @@ open class PageHolder(parent: ViewGroup, loader: PageLoader) :
}
override fun onImageShown() {
progressBar.isVisible = false
binding.progressBar.isVisible = false
}
override fun onClick(v: View) {
@@ -93,8 +95,8 @@ open class PageHolder(parent: ViewGroup, loader: PageLoader) :
}
override fun onError(e: Throwable) {
textView_error.text = e.getDisplayMessage(context.resources)
layout_error.isVisible = true
progressBar.isVisible = false
binding.textViewError.text = e.getDisplayMessage(context.resources)
binding.layoutError.isVisible = true
binding.progressBar.isVisible = false
}
}

View File

@@ -3,11 +3,12 @@ package org.koitharu.kotatsu.reader.ui.standard
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import kotlinx.android.synthetic.main.fragment_reader_standard.*
import android.view.ViewGroup
import org.koin.android.ext.android.inject
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.databinding.FragmentReaderStandardBinding
import org.koitharu.kotatsu.reader.ui.ReaderState
import org.koitharu.kotatsu.reader.ui.base.AbstractReader
import org.koitharu.kotatsu.reader.ui.base.BaseReaderAdapter
@@ -16,22 +17,29 @@ import org.koitharu.kotatsu.utils.ext.doOnPageChanged
import org.koitharu.kotatsu.utils.ext.swapAdapter
import org.koitharu.kotatsu.utils.ext.withArgs
class PagerReaderFragment : AbstractReader(R.layout.fragment_reader_standard),
class PagerReaderFragment : AbstractReader<FragmentReaderStandardBinding>(),
SharedPreferences.OnSharedPreferenceChangeListener {
private var paginationListener: PagerPaginationListener? = null
private val settings by inject<AppSettings>()
override fun onInflateView(
inflater: LayoutInflater,
container: ViewGroup?
) = FragmentReaderStandardBinding.inflate(inflater, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
paginationListener = PagerPaginationListener(adapter!!, 2, this)
pager.adapter = adapter
if (settings.readerAnimation) {
pager.setPageTransformer(PageAnimTransformer())
paginationListener = PagerPaginationListener(readerAdapter!!, 2, this)
with(binding.pager) {
adapter = readerAdapter
if (settings.readerAnimation) {
setPageTransformer(PageAnimTransformer())
}
offscreenPageLimit = 2
registerOnPageChangeCallback(paginationListener!!)
doOnPageChanged(::notifyPageChanged)
}
pager.offscreenPageLimit = 2
pager.registerOnPageChangeCallback(paginationListener!!)
pager.doOnPageChanged(::notifyPageChanged)
}
override fun onAttach(context: Context) {
@@ -55,13 +63,13 @@ class PagerReaderFragment : AbstractReader(R.layout.fragment_reader_standard),
override fun recreateAdapter() {
super.recreateAdapter()
pager.swapAdapter(adapter)
binding.pager.swapAdapter(readerAdapter)
}
override fun getCurrentItem() = pager.currentItem
override fun getCurrentItem() = binding.pager.currentItem
override fun setCurrentItem(position: Int, isSmooth: Boolean) {
pager.setCurrentItem(position, isSmooth)
binding.pager.setCurrentItem(position, isSmooth)
}
override fun getCurrentPageScroll() = 0
@@ -72,9 +80,9 @@ class PagerReaderFragment : AbstractReader(R.layout.fragment_reader_standard),
when (key) {
AppSettings.KEY_READER_ANIMATION -> {
if (settings.readerAnimation) {
pager.setPageTransformer(PageAnimTransformer())
binding.pager.setPageTransformer(PageAnimTransformer())
} else {
pager.setPageTransformer(null)
binding.pager.setPageTransformer(null)
}
}
}

View File

@@ -1,58 +0,0 @@
package org.koitharu.kotatsu.reader.ui.thumbnails
import android.view.ViewGroup
import androidx.core.net.toUri
import coil.ImageLoader
import coil.request.ImageRequest
import coil.size.PixelSize
import coil.size.Size
import kotlinx.android.synthetic.main.item_page_thumb.*
import kotlinx.coroutines.*
import org.koin.core.component.inject
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.list.BaseViewHolder
import org.koitharu.kotatsu.core.model.MangaPage
import org.koitharu.kotatsu.local.data.PagesCache
import org.koitharu.kotatsu.utils.ext.IgnoreErrors
class PageThumbnailHolder(parent: ViewGroup, private val scope: CoroutineScope) :
BaseViewHolder<MangaPage, PagesCache>(parent, R.layout.item_page_thumb) {
private var job: Job? = null
private val thumbSize: Size
private val coil by inject<ImageLoader>()
init {
val width = itemView.context.resources.getDimensionPixelSize(R.dimen.preferred_grid_width)
thumbSize = PixelSize(
width = width,
height = (width * 13f / 18f).toInt()
)
}
override fun onBind(data: MangaPage, extra: PagesCache) {
imageView_thumb.setImageDrawable(null)
textView_number.text = (bindingAdapterPosition + 1).toString()
job?.cancel()
job = scope.launch(Dispatchers.IO + IgnoreErrors) {
val url = data.preview ?: data.url.let {
val pageUrl = data.source.repository.getPageFullUrl(data)
extra[pageUrl]?.toUri()?.toString() ?: pageUrl
}
val drawable = coil.execute(
ImageRequest.Builder(context)
.data(url)
.size(thumbSize)
.build()
).drawable
withContext(Dispatchers.Main.immediate) {
imageView_thumb.setImageDrawable(drawable)
}
}
}
override fun onRecycled() {
job?.cancel()
imageView_thumb.setImageDrawable(null)
}
}

View File

@@ -1,50 +1,57 @@
package org.koitharu.kotatsu.reader.ui.thumbnails
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
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.koin.android.ext.android.get
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.core.model.MangaPage
import org.koitharu.kotatsu.databinding.SheetPagesBinding
import org.koitharu.kotatsu.reader.ui.thumbnails.adapter.PageThumbnailAdapter
import org.koitharu.kotatsu.utils.UiUtils
import org.koitharu.kotatsu.utils.ext.resolveDp
import org.koitharu.kotatsu.utils.ext.viewLifecycleScope
import org.koitharu.kotatsu.utils.ext.withArgs
class PagesThumbnailsSheet : BaseBottomSheet(R.layout.sheet_pages),
class PagesThumbnailsSheet : BaseBottomSheet<SheetPagesBinding>(),
OnListItemClickListener<MangaPage> {
override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): SheetPagesBinding {
return SheetPagesBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recyclerView.addItemDecoration(SpacingItemDecoration(view.resources.resolveDp(8)))
binding.recyclerView.addItemDecoration(SpacingItemDecoration(view.resources.resolveDp(8)))
val pages = arguments?.getParcelableArrayList<MangaPage>(ARG_PAGES)
if (pages == null) {
dismissAllowingStateLoss()
return
}
recyclerView.adapter = PageThumbnailAdapter(get(), viewLifecycleScope, get(), this).apply {
items = pages
}
binding.recyclerView.adapter =
PageThumbnailAdapter(get(), viewLifecycleScope, get(), this).apply {
items = pages
}
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
binding.toolbar.title = title
binding.toolbar.setNavigationOnClickListener { dismiss() }
binding.toolbar.subtitle =
resources.getQuantityString(R.plurals.pages, pages.size, pages.size)
binding.textViewTitle.text = title
if (dialog !is BottomSheetDialog) {
toolbar.isVisible = true
textView_title.isVisible = false
appbar.elevation = resources.getDimension(R.dimen.elevation_large)
binding.toolbar.isVisible = true
binding.textViewTitle.isVisible = false
binding.appbar.elevation = resources.getDimension(R.dimen.elevation_large)
}
recyclerView.addOnLayoutChangeListener(UiUtils.SpanCountResolver)
binding.recyclerView.addOnLayoutChangeListener(UiUtils.SpanCountResolver)
}
override fun onCreateDialog(savedInstanceState: Bundle?) =
@@ -57,13 +64,13 @@ class PagesThumbnailsSheet : BaseBottomSheet(R.layout.sheet_pages),
override fun onStateChanged(bottomSheet: View, newState: Int) {
if (newState == BottomSheetBehavior.STATE_EXPANDED) {
toolbar.isVisible = true
textView_title.isVisible = false
appbar.elevation = elevation
binding.toolbar.isVisible = true
binding.textViewTitle.isVisible = false
binding.appbar.elevation = elevation
} else {
toolbar.isVisible = false
textView_title.isVisible = true
appbar.elevation = 0f
binding.toolbar.isVisible = false
binding.textViewTitle.isVisible = true
binding.appbar.elevation = 0f
}
}
})
@@ -71,8 +78,7 @@ class PagesThumbnailsSheet : BaseBottomSheet(R.layout.sheet_pages),
}
override fun onDestroyView() {
(recyclerView.adapter as? DisposableHandle)?.dispose()
recyclerView.adapter = null
binding.recyclerView.adapter = null
super.onDestroyView()
}

View File

@@ -4,12 +4,12 @@ import androidx.core.net.toUri
import coil.ImageLoader
import coil.request.ImageRequest
import coil.size.PixelSize
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateLayoutContainer
import kotlinx.android.synthetic.main.item_page_thumb.*
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import kotlinx.coroutines.*
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.model.MangaPage
import org.koitharu.kotatsu.databinding.ItemPageThumbBinding
import org.koitharu.kotatsu.local.data.PagesCache
import org.koitharu.kotatsu.utils.ext.IgnoreErrors
@@ -18,7 +18,9 @@ fun pageThumbnailAD(
scope: CoroutineScope,
cache: PagesCache,
clickListener: OnListItemClickListener<MangaPage>
) = adapterDelegateLayoutContainer<MangaPage, MangaPage>(R.layout.item_page_thumb) {
) = adapterDelegateViewBinding<MangaPage, MangaPage, ItemPageThumbBinding>(
{ inflater, parent -> ItemPageThumbBinding.inflate(inflater, parent, false) }
) {
var job: Job? = null
val gridWidth = itemView.context.resources.getDimensionPixelSize(R.dimen.preferred_grid_width)
@@ -27,14 +29,14 @@ fun pageThumbnailAD(
height = (gridWidth * 13f / 18f).toInt()
)
handle.setOnClickListener {
binding.handle.setOnClickListener {
clickListener.onItemClick(item, itemView)
}
bind {
job?.cancel()
imageView_thumb.setImageDrawable(null)
textView_number.text = (bindingAdapterPosition + 1).toString()
binding.imageViewThumb.setImageDrawable(null)
binding.textViewNumber.text = (bindingAdapterPosition + 1).toString()
job = scope.launch(Dispatchers.Default + IgnoreErrors) {
val url = item.preview ?: item.url.let {
val pageUrl = item.source.repository.getPageFullUrl(item)
@@ -47,13 +49,13 @@ fun pageThumbnailAD(
.build()
).drawable
withContext(Dispatchers.Main) {
imageView_thumb.setImageDrawable(drawable)
binding.imageViewThumb.setImageDrawable(drawable)
}
}
}
onViewRecycled {
job?.cancel()
imageView_thumb.setImageDrawable(null)
binding.imageViewThumb.setImageDrawable(null)
}
}

View File

@@ -1,15 +1,16 @@
package org.koitharu.kotatsu.reader.ui.wetoon
import android.net.Uri
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import com.davemorrissey.labs.subscaleview.ImageSource
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import kotlinx.android.synthetic.main.item_page_webtoon.*
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.list.BaseViewHolder
import org.koitharu.kotatsu.core.model.ZoomMode
import org.koitharu.kotatsu.databinding.ItemPageWebtoonBinding
import org.koitharu.kotatsu.reader.ui.PageLoader
import org.koitharu.kotatsu.reader.ui.base.PageHolderDelegate
import org.koitharu.kotatsu.reader.ui.base.ReaderPage
@@ -17,15 +18,16 @@ import org.koitharu.kotatsu.utils.ext.getDisplayMessage
class WebtoonHolder(parent: ViewGroup, private val loader: PageLoader) :
BaseViewHolder<ReaderPage, Unit>(parent, R.layout.item_page_webtoon),
PageHolderDelegate.Callback, View.OnClickListener {
BaseViewHolder<ReaderPage, Unit, ItemPageWebtoonBinding>(
ItemPageWebtoonBinding.inflate(LayoutInflater.from(parent.context), parent, false)
), PageHolderDelegate.Callback, View.OnClickListener {
private val delegate = PageHolderDelegate(loader, this)
private var scrollToRestore = 0
init {
ssiv.setOnImageEventListener(delegate)
button_retry.setOnClickListener(this)
binding.ssiv.setOnImageEventListener(delegate)
binding.buttonRetry.setOnClickListener(this)
}
override fun onBind(data: ReaderPage, extra: Unit) {
@@ -34,34 +36,36 @@ class WebtoonHolder(parent: ViewGroup, private val loader: PageLoader) :
override fun onRecycled() {
delegate.onRecycle()
ssiv.recycle()
binding.ssiv.recycle()
}
override fun onLoadingStarted() {
layout_error.isVisible = false
progressBar.isVisible = true
ssiv.recycle()
binding.layoutError.isVisible = false
binding.progressBar.isVisible = true
binding.ssiv.recycle()
}
override fun onImageReady(uri: Uri) {
ssiv.setImage(ImageSource.uri(uri))
binding.ssiv.setImage(ImageSource.uri(uri))
}
override fun onImageShowing(zoom: ZoomMode) {
ssiv.maxScale = 2f * ssiv.width / ssiv.sWidth.toFloat()
ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM)
ssiv.minScale = ssiv.width / ssiv.sWidth.toFloat()
ssiv.scrollTo(
when {
scrollToRestore != 0 -> scrollToRestore
itemView.top < 0 -> ssiv.getScrollRange()
else -> 0
}
)
with(binding.ssiv) {
maxScale = 2f * width / sWidth.toFloat()
setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM)
minScale = width / sWidth.toFloat()
scrollTo(
when {
scrollToRestore != 0 -> scrollToRestore
itemView.top < 0 -> getScrollRange()
else -> 0
}
)
}
}
override fun onImageShown() {
progressBar.isVisible = false
binding.progressBar.isVisible = false
}
override fun onClick(v: View) {
@@ -71,16 +75,16 @@ class WebtoonHolder(parent: ViewGroup, private val loader: PageLoader) :
}
override fun onError(e: Throwable) {
textView_error.text = e.getDisplayMessage(context.resources)
layout_error.isVisible = true
progressBar.isVisible = false
binding.textViewError.text = e.getDisplayMessage(context.resources)
binding.layoutError.isVisible = true
binding.progressBar.isVisible = false
}
fun getScrollY() = ssiv.getScroll()
fun getScrollY() = binding.ssiv.getScroll()
fun restoreScroll(scroll: Int) {
if (ssiv.isReady) {
ssiv.scrollTo(scroll)
if (binding.ssiv.isReady) {
binding.ssiv.scrollTo(scroll)
} else {
scrollToRestore = scroll
}

View File

@@ -1,10 +1,11 @@
package org.koitharu.kotatsu.reader.ui.wetoon
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.AccelerateDecelerateInterpolator
import kotlinx.android.synthetic.main.fragment_reader_webtoon.*
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.databinding.FragmentReaderWebtoonBinding
import org.koitharu.kotatsu.reader.ui.ReaderState
import org.koitharu.kotatsu.reader.ui.base.AbstractReader
import org.koitharu.kotatsu.reader.ui.base.BaseReaderAdapter
@@ -14,18 +15,25 @@ import org.koitharu.kotatsu.utils.ext.findCenterViewPosition
import org.koitharu.kotatsu.utils.ext.firstItem
import org.koitharu.kotatsu.utils.ext.withArgs
class WebtoonReaderFragment : AbstractReader(R.layout.fragment_reader_webtoon) {
class WebtoonReaderFragment : AbstractReader<FragmentReaderWebtoonBinding>() {
private val scrollInterpolator = AccelerateDecelerateInterpolator()
private var paginationListener: ListPaginationListener? = null
override fun onInflateView(
inflater: LayoutInflater,
container: ViewGroup?
) = FragmentReaderWebtoonBinding.inflate(inflater, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
paginationListener = ListPaginationListener(2, this)
recyclerView.setHasFixedSize(true)
recyclerView.adapter = adapter
recyclerView.addOnScrollListener(paginationListener!!)
recyclerView.doOnCurrentItemChanged(::notifyPageChanged)
with(binding.recyclerView) {
setHasFixedSize(true)
adapter = readerAdapter
addOnScrollListener(paginationListener!!)
doOnCurrentItemChanged(::notifyPageChanged)
}
}
override fun onCreateAdapter(dataSet: List<ReaderPage>): BaseReaderAdapter {
@@ -34,7 +42,7 @@ class WebtoonReaderFragment : AbstractReader(R.layout.fragment_reader_webtoon) {
override fun recreateAdapter() {
super.recreateAdapter()
recyclerView.swapAdapter(adapter, true)
binding.recyclerView.swapAdapter(readerAdapter, true)
}
override fun onDestroyView() {
@@ -43,33 +51,33 @@ class WebtoonReaderFragment : AbstractReader(R.layout.fragment_reader_webtoon) {
}
override fun getCurrentItem(): Int {
return recyclerView.findCenterViewPosition()
return binding.recyclerView.findCenterViewPosition()
}
override fun setCurrentItem(position: Int, isSmooth: Boolean) {
if (isSmooth) {
recyclerView.smoothScrollToPosition(position)
binding.recyclerView.smoothScrollToPosition(position)
} else {
recyclerView.firstItem = position
binding.recyclerView.firstItem = position
}
}
override fun switchPageBy(delta: Int) {
recyclerView.smoothScrollBy(
binding.recyclerView.smoothScrollBy(
0,
(recyclerView.height * 0.9).toInt() * delta,
(binding.recyclerView.height * 0.9).toInt() * delta,
scrollInterpolator
)
}
override fun getCurrentPageScroll(): Int {
return (recyclerView.findViewHolderForAdapterPosition(getCurrentItem()) as? WebtoonHolder)
return (binding.recyclerView.findViewHolderForAdapterPosition(getCurrentItem()) as? WebtoonHolder)
?.getScrollY() ?: 0
}
override fun restorePageScroll(position: Int, scroll: Int) {
recyclerView.post {
val holder = recyclerView.findViewHolderForAdapterPosition(position) ?: return@post
binding.recyclerView.post {
val holder = binding.recyclerView.findViewHolderForAdapterPosition(position) ?: return@post
(holder as WebtoonHolder).restoreScroll(scroll)
}
}

View File

@@ -1,6 +1,6 @@
package org.koitharu.kotatsu.remotelist
import org.koin.android.viewmodel.dsl.viewModel
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.core.qualifier.named
import org.koin.dsl.module
import org.koitharu.kotatsu.core.model.MangaSource

View File

@@ -3,7 +3,7 @@ package org.koitharu.kotatsu.remotelist.ui
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import org.koin.android.viewmodel.ext.android.viewModel
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.MangaFilter

View File

@@ -1,6 +1,6 @@
package org.koitharu.kotatsu.search
import org.koin.android.viewmodel.dsl.viewModel
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.core.qualifier.named
import org.koin.dsl.module
import org.koitharu.kotatsu.core.model.MangaSource

View File

@@ -3,7 +3,7 @@ package org.koitharu.kotatsu.search.ui
import android.os.Bundle
import android.view.View
import androidx.fragment.app.FragmentManager
import org.koin.android.viewmodel.ext.android.viewModel
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.MangaSource

View File

@@ -5,40 +5,42 @@ import android.content.Intent
import android.os.Bundle
import android.os.Parcelable
import androidx.appcompat.widget.SearchView
import kotlinx.android.synthetic.main.activity_search.*
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseActivity
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.databinding.ActivitySearchBinding
import org.koitharu.kotatsu.utils.ext.showKeyboard
class SearchActivity : BaseActivity(), SearchView.OnQueryTextListener {
class SearchActivity : BaseActivity<ActivitySearchBinding>(), SearchView.OnQueryTextListener {
private lateinit var source: MangaSource
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_search)
setContentView(ActivitySearchBinding.inflate(layoutInflater))
source = intent.getParcelableExtra(EXTRA_SOURCE) ?: run {
finishAfterTransition()
return
}
val query = intent.getStringExtra(EXTRA_QUERY)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
searchView.queryHint = getString(R.string.search_on_s, source.title)
searchView.suggestionsAdapter = MangaSuggestionsProvider.getSuggestionAdapter(this)
searchView.setOnSuggestionListener(SearchHelper.SuggestionListener(searchView))
searchView.setOnQueryTextListener(this)
with(binding.searchView) {
queryHint = getString(R.string.search_on_s, source.title)
suggestionsAdapter = MangaSuggestionsProvider.getSuggestionAdapter(this@SearchActivity)
setOnSuggestionListener(SearchHelper.SuggestionListener(this))
setOnQueryTextListener(this@SearchActivity)
if (query.isNullOrBlank()) {
searchView.requestFocus()
searchView.showKeyboard()
} else {
searchView.setQuery(query, true)
if (query.isNullOrBlank()) {
requestFocus()
showKeyboard()
} else {
setQuery(query, true)
}
}
}
override fun onDestroy() {
searchView.suggestionsAdapter?.changeCursor(null) //close cursor
binding.searchView.suggestionsAdapter.changeCursor(null) //close cursor
super.onDestroy()
}
@@ -49,7 +51,7 @@ class SearchActivity : BaseActivity(), SearchView.OnQueryTextListener {
.beginTransaction()
.replace(R.id.container, SearchFragment.newInstance(source, query))
.commit()
searchView.clearFocus()
binding.searchView.clearFocus()
MangaSuggestionsProvider.saveQueryAsync(applicationContext, query)
true
} else false

View File

@@ -1,6 +1,6 @@
package org.koitharu.kotatsu.search.ui
import org.koin.android.viewmodel.ext.android.viewModel
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.list.ui.MangaListFragment

View File

@@ -12,7 +12,6 @@ import java.io.Closeable
object SearchHelper {
@JvmStatic
fun setupSearchView(menuItem: MenuItem): Closeable? {
val view = menuItem.actionView as? SearchView ?: return null
val context = view.context

View File

@@ -5,12 +5,13 @@ import android.content.Intent
import android.os.Bundle
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseActivity
import org.koitharu.kotatsu.databinding.ActivitySearchGlobalBinding
class GlobalSearchActivity : BaseActivity() {
class GlobalSearchActivity : BaseActivity<ActivitySearchGlobalBinding>() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_search_global)
setContentView(ActivitySearchGlobalBinding.inflate(layoutInflater))
val query = intent.getStringExtra(EXTRA_QUERY)
if (query == null) {

View File

@@ -1,6 +1,6 @@
package org.koitharu.kotatsu.search.ui.global
import org.koin.android.viewmodel.ext.android.viewModel
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
import org.koitharu.kotatsu.list.ui.MangaListFragment
import org.koitharu.kotatsu.utils.ext.stringArgument

View File

@@ -10,13 +10,14 @@ import androidx.preference.PreferenceFragmentCompat
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseActivity
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.databinding.ActivitySettingsBinding
class SettingsActivity : BaseActivity(),
class SettingsActivity : BaseActivity<ActivitySettingsBinding>(),
PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_settings)
setContentView(ActivitySettingsBinding.inflate(layoutInflater))
supportActionBar?.setDisplayHomeAsUpEnabled(true)
if (supportFragmentManager.findFragmentById(R.id.container) == null) {

View File

@@ -2,7 +2,7 @@ package org.koitharu.kotatsu.settings
import android.net.Uri
import org.koin.android.ext.koin.androidContext
import org.koin.android.viewmodel.dsl.viewModel
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
import org.koitharu.kotatsu.core.backup.BackupRepository
import org.koitharu.kotatsu.core.backup.RestoreRepository

View File

@@ -1,26 +1,33 @@
package org.koitharu.kotatsu.settings.backup
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import kotlinx.android.synthetic.main.dialog_progress.*
import org.koin.android.viewmodel.ext.android.viewModel
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.AlertDialogFragment
import org.koitharu.kotatsu.databinding.DialogProgressBinding
import org.koitharu.kotatsu.utils.ShareHelper
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.progress.Progress
import java.io.File
class BackupDialogFragment : AlertDialogFragment(R.layout.dialog_progress) {
class BackupDialogFragment : AlertDialogFragment<DialogProgressBinding>() {
private val viewModel by viewModel<BackupViewModel>()
override fun onInflateView(
inflater: LayoutInflater,
container: ViewGroup?
) = DialogProgressBinding.inflate(inflater, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
textView_title.setText(R.string.create_backup)
textView_subtitle.setText(R.string.processing_)
binding.textViewTitle.setText(R.string.create_backup)
binding.textViewSubtitle.setText(R.string.processing_)
viewModel.progress.observe(viewLifecycleOwner, this::onProgressChanged)
viewModel.onBackupDone.observe(viewLifecycleOwner, this::onBackupDone)
@@ -42,7 +49,7 @@ class BackupDialogFragment : AlertDialogFragment(R.layout.dialog_progress) {
}
private fun onProgressChanged(progress: Progress?) {
with(progressBar) {
with(binding.progressBar) {
isVisible = true
isIndeterminate = progress == null
if (progress != null) {

View File

@@ -7,7 +7,6 @@ import androidx.activity.result.ActivityResultCallback
import androidx.activity.result.contract.ActivityResultContracts
import androidx.preference.Preference
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.fragment_list.*
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BasePreferenceFragment
@@ -39,7 +38,7 @@ class BackupSettingsFragment : BasePreferenceFragment(R.string.backup_restore),
e.printStackTrace()
}
Snackbar.make(
recyclerView, R.string.operation_not_supported, Snackbar.LENGTH_SHORT
listView, R.string.operation_not_supported, Snackbar.LENGTH_SHORT
).show()
}
true

View File

@@ -2,21 +2,28 @@ package org.koitharu.kotatsu.settings.backup
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import kotlinx.android.synthetic.main.dialog_progress.*
import org.koin.android.viewmodel.ext.android.viewModel
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.AlertDialogFragment
import org.koitharu.kotatsu.core.backup.CompositeResult
import org.koitharu.kotatsu.databinding.DialogProgressBinding
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.toUriOrNull
import org.koitharu.kotatsu.utils.ext.withArgs
import org.koitharu.kotatsu.utils.progress.Progress
class RestoreDialogFragment : AlertDialogFragment(R.layout.dialog_progress) {
class RestoreDialogFragment : AlertDialogFragment<DialogProgressBinding>() {
override fun onInflateView(
inflater: LayoutInflater,
container: ViewGroup?
) = DialogProgressBinding.inflate(inflater, container, false)
private val viewModel by viewModel<RestoreViewModel> {
parametersOf(arguments?.getString(ARG_FILE)?.toUriOrNull())
@@ -24,8 +31,8 @@ class RestoreDialogFragment : AlertDialogFragment(R.layout.dialog_progress) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
textView_title.setText(R.string.restore_backup)
textView_subtitle.setText(R.string.preparing_)
binding.textViewTitle.setText(R.string.restore_backup)
binding.textViewSubtitle.setText(R.string.preparing_)
viewModel.progress.observe(viewLifecycleOwner, this::onProgressChanged)
viewModel.onRestoreDone.observe(viewLifecycleOwner, this::onRestoreDone)
@@ -46,7 +53,7 @@ class RestoreDialogFragment : AlertDialogFragment(R.layout.dialog_progress) {
}
private fun onProgressChanged(progress: Progress?) {
with(progressBar) {
with(binding.progressBar) {
isVisible = true
isIndeterminate = progress == null
if (progress != null) {

View File

@@ -1,16 +1,18 @@
package org.koitharu.kotatsu.settings.sources
import android.view.LayoutInflater
import android.view.ViewGroup
import kotlinx.android.synthetic.main.item_source_config.*
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.list.BaseViewHolder
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.databinding.ItemSourceConfigBinding
class SourceViewHolder(parent: ViewGroup) :
BaseViewHolder<MangaSource, Boolean>(parent, R.layout.item_source_config) {
BaseViewHolder<MangaSource, Boolean, ItemSourceConfigBinding>(
ItemSourceConfigBinding.inflate(LayoutInflater.from(parent.context), parent, false)
) {
override fun onBind(data: MangaSource, extra: Boolean) {
textView_title.text = data.title
imageView_hidden.isChecked = extra
binding.textViewTitle.text = data.title
binding.imageViewHidden.isChecked = extra
}
}

View File

@@ -4,7 +4,6 @@ import android.annotation.SuppressLint
import android.view.MotionEvent
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.item_source_config.*
import org.koitharu.kotatsu.base.domain.MangaProviderFactory
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.model.MangaSource
@@ -38,7 +37,7 @@ class SourcesAdapter(
@SuppressLint("ClickableViewAccessibility")
private fun onViewHolderCreated(holder: SourceViewHolder) {
holder.imageView_hidden.setOnCheckedChangeListener {
holder.binding.imageViewHidden.setOnCheckedChangeListener {
if (it) {
hiddenItems.remove(holder.requireData())
} else {
@@ -46,10 +45,10 @@ class SourcesAdapter(
}
settings.hiddenSources = hiddenItems.mapToSet { x -> x.name }
}
holder.imageView_config.setOnClickListener { v ->
holder.binding.imageViewConfig.setOnClickListener { v ->
onItemClickListener.onItemClick(holder.requireData(), v)
}
holder.imageView_handle.setOnTouchListener { v, event ->
holder.binding.imageViewHandle.setOnTouchListener { v, event ->
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
onItemClickListener.onItemLongClick(
holder.requireData(),

View File

@@ -1,19 +1,21 @@
package org.koitharu.kotatsu.settings.sources
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.fragment_settings_sources.*
import org.koin.android.ext.android.get
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseFragment
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.databinding.FragmentSettingsSourcesBinding
import org.koitharu.kotatsu.settings.SettingsActivity
class SourcesSettingsFragment : BaseFragment(R.layout.fragment_settings_sources),
class SourcesSettingsFragment : BaseFragment<FragmentSettingsSourcesBinding>(),
OnListItemClickListener<MangaSource> {
private lateinit var reorderHelper: ItemTouchHelper
@@ -23,6 +25,11 @@ class SourcesSettingsFragment : BaseFragment(R.layout.fragment_settings_sources)
reorderHelper = ItemTouchHelper(SourcesReorderCallback())
}
override fun onInflateView(
inflater: LayoutInflater,
container: ViewGroup?
) = FragmentSettingsSourcesBinding.inflate(inflater, container, false)
override fun onResume() {
super.onResume()
activity?.setTitle(R.string.remote_sources)
@@ -30,9 +37,11 @@ class SourcesSettingsFragment : BaseFragment(R.layout.fragment_settings_sources)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recyclerView.addItemDecoration(DividerItemDecoration(view.context, RecyclerView.VERTICAL))
recyclerView.adapter = SourcesAdapter(get(), this)
reorderHelper.attachToRecyclerView(recyclerView)
with(binding.recyclerView) {
addItemDecoration(DividerItemDecoration(view.context, RecyclerView.VERTICAL))
adapter = SourcesAdapter(get(), this@SourcesSettingsFragment)
reorderHelper.attachToRecyclerView(this)
}
}
override fun onDestroyView() {
@@ -46,7 +55,7 @@ class SourcesSettingsFragment : BaseFragment(R.layout.fragment_settings_sources)
override fun onItemLongClick(item: MangaSource, view: View): Boolean {
reorderHelper.startDrag(
recyclerView.findContainingViewHolder(view) ?: return false
binding.recyclerView.findContainingViewHolder(view) ?: return false
)
return true
}

View File

@@ -1,7 +1,7 @@
package org.koitharu.kotatsu.tracker
import org.koin.android.ext.koin.androidContext
import org.koin.android.viewmodel.dsl.viewModel
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
import org.koitharu.kotatsu.tracker.ui.FeedViewModel

View File

@@ -1,32 +1,29 @@
package org.koitharu.kotatsu.tracker.ui
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.*
import androidx.core.view.isVisible
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.fragment_tracklogs.*
import org.koin.android.ext.android.get
import org.koin.android.viewmodel.ext.android.viewModel
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseFragment
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.base.ui.list.PaginationScrollListener
import org.koitharu.kotatsu.base.ui.list.decor.SpacingItemDecoration
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.databinding.FragmentFeedBinding
import org.koitharu.kotatsu.details.ui.DetailsActivity
import org.koitharu.kotatsu.tracker.work.TrackWorker
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.hasItems
class FeedFragment : BaseFragment(R.layout.fragment_tracklogs), PaginationScrollListener.Callback,
class FeedFragment : BaseFragment<FragmentFeedBinding>(), PaginationScrollListener.Callback,
OnListItemClickListener<Manga> {
private val viewModel by viewModel<FeedViewModel>()
private var adapter: FeedAdapter? = null
private var feedAdapter: FeedAdapter? = null
override fun getTitle() = context?.getString(R.string.updates)
@@ -35,15 +32,22 @@ class FeedFragment : BaseFragment(R.layout.fragment_tracklogs), PaginationScroll
setHasOptionsMenu(true)
}
override fun onInflateView(
inflater: LayoutInflater,
container: ViewGroup?
) = FragmentFeedBinding.inflate(inflater, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
adapter = FeedAdapter(get(), this)
recyclerView.adapter = adapter
recyclerView.addItemDecoration(
SpacingItemDecoration(resources.getDimensionPixelOffset(R.dimen.grid_spacing))
)
recyclerView.setHasFixedSize(true)
recyclerView.addOnScrollListener(PaginationScrollListener(4, this))
feedAdapter = FeedAdapter(get(), this)
with(binding.recyclerView) {
adapter = feedAdapter
addItemDecoration(
SpacingItemDecoration(resources.getDimensionPixelOffset(R.dimen.grid_spacing))
)
setHasFixedSize(true)
addOnScrollListener(PaginationScrollListener(4, this@FeedFragment))
}
viewModel.content.observe(viewLifecycleOwner, this::onListChanged)
viewModel.isLoading.observe(viewLifecycleOwner, this::onLoadingStateChanged)
@@ -59,47 +63,56 @@ class FeedFragment : BaseFragment(R.layout.fragment_tracklogs), PaginationScroll
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.action_update -> {
TrackWorker.startNow(requireContext())
Snackbar.make(recyclerView, R.string.feed_will_update_soon, Snackbar.LENGTH_LONG).show()
Snackbar.make(
binding.recyclerView,
R.string.feed_will_update_soon,
Snackbar.LENGTH_LONG
).show()
true
}
else -> super.onOptionsItemSelected(item)
}
override fun onDestroyView() {
adapter = null
feedAdapter = null
super.onDestroyView()
}
private fun onListChanged(list: List<Any>) {
adapter?.items = list
feedAdapter?.items = list
}
private fun onError(e: Throwable) {
if (recyclerView.hasItems) {
Snackbar.make(recyclerView, e.getDisplayMessage(resources), Snackbar.LENGTH_SHORT)
.show()
if (binding.recyclerView.hasItems) {
Snackbar.make(
binding.recyclerView,
e.getDisplayMessage(resources),
Snackbar.LENGTH_SHORT
).show()
} else {
textView_holder.text = e.getDisplayMessage(resources)
textView_holder.setCompoundDrawablesRelativeWithIntrinsicBounds(
0,
R.drawable.ic_error_large,
0,
0
)
layout_holder.isVisible = true
with(binding.textViewHolder) {
text = e.getDisplayMessage(resources)
setCompoundDrawablesRelativeWithIntrinsicBounds(
0,
R.drawable.ic_error_large,
0,
0
)
isVisible = true
}
}
}
private fun onLoadingStateChanged(isLoading: Boolean) {
val hasItems = recyclerView.hasItems
progressBar.isVisible = isLoading && !hasItems
val hasItems = binding.recyclerView.hasItems
binding.progressBar.isVisible = isLoading && !hasItems
}
private fun onEmptyStateChanged(isEmpty: Boolean) {
if (isEmpty) {
setUpEmptyListHolder()
}
layout_holder.isVisible = isEmpty
binding.layoutHolder.isVisible = isEmpty
}
override fun onScrolledToEnd() {
@@ -111,8 +124,10 @@ class FeedFragment : BaseFragment(R.layout.fragment_tracklogs), PaginationScroll
}
private fun setUpEmptyListHolder() {
textView_holder.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, null, null)
textView_holder.setText(R.string.text_feed_holder)
with(binding.textViewHolder) {
setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, null, null)
setText(R.string.text_feed_holder)
}
}
companion object {

View File

@@ -2,11 +2,11 @@ package org.koitharu.kotatsu.tracker.ui.adapter
import coil.ImageLoader
import coil.request.Disposable
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateLayoutContainer
import kotlinx.android.synthetic.main.item_tracklog.*
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.databinding.ItemTracklogBinding
import org.koitharu.kotatsu.tracker.ui.model.FeedItem
import org.koitharu.kotatsu.utils.ext.enqueueWith
import org.koitharu.kotatsu.utils.ext.newImageRequest
@@ -14,7 +14,9 @@ import org.koitharu.kotatsu.utils.ext.newImageRequest
fun feedItemAD(
coil: ImageLoader,
clickListener: OnListItemClickListener<Manga>
) = adapterDelegateLayoutContainer<FeedItem, Any>(R.layout.item_tracklog) {
) = adapterDelegateViewBinding<FeedItem, Any, ItemTracklogBinding>(
{ inflater, parent -> ItemTracklogBinding.inflate(inflater, parent, false) }
) {
var imageRequest: Disposable? = null
@@ -24,18 +26,18 @@ fun feedItemAD(
bind {
imageRequest?.dispose()
imageRequest = imageView_cover.newImageRequest(item.imageUrl)
imageRequest = binding.imageViewCover.newImageRequest(item.imageUrl)
.placeholder(R.drawable.ic_placeholder)
.fallback(R.drawable.ic_placeholder)
.error(R.drawable.ic_placeholder)
.enqueueWith(coil)
textView_title.text = item.title
textView_subtitle.text = item.subtitle
textView_chapters.text = item.chapters
binding.textViewTitle.text = item.title
binding.textViewSubtitle.text = item.subtitle
binding.textViewChapters.text = item.chapters
}
onViewRecycled {
imageRequest?.dispose()
imageView_cover.setImageDrawable(null)
binding.imageViewCover.setImageDrawable(null)
}
}

View File

@@ -9,13 +9,10 @@ import kotlin.math.pow
object FileSizeUtils {
@JvmStatic
fun mbToBytes(mb: Int) = 1024L * 1024L * mb
@JvmStatic
fun kbToBytes(kb: Int) = 1024L * kb
@JvmStatic
fun formatBytes(context: Context, bytes: Long): String {
val units = context.getString(R.string.text_file_sizes).split('|')
if (bytes <= 0) {

Some files were not shown because too many files have changed in this diff Show More