Migrate to ViewBinding
This commit is contained in:
@@ -53,7 +53,6 @@ object MangaUtils : KoinComponent {
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
private fun getBitmapSize(input: InputStream?): Size {
|
||||
val options = BitmapFactory.Options().apply {
|
||||
inJustDecodeBounds = true
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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('-', "")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -22,7 +22,6 @@ interface SourceSettings {
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
operator fun invoke(context: Context, source: MangaSource): SourceSettings =
|
||||
PrefSourceSettings(context, source)
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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, *>
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user