Remove AsyncLayoutInflater; fixes
This commit is contained in:
@@ -14,8 +14,8 @@ android {
|
|||||||
applicationId 'org.koitharu.kotatsu'
|
applicationId 'org.koitharu.kotatsu'
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 32
|
targetSdkVersion 32
|
||||||
versionCode 399
|
versionCode 400
|
||||||
versionName '3.0-alpha1'
|
versionName '3.0'
|
||||||
generatedDensities = []
|
generatedDensities = []
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ android {
|
|||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
|
||||||
implementation 'com.github.nv95:kotatsu-parsers:1ba2bba12e'
|
implementation 'com.github.nv95:kotatsu-parsers:3ea7e92e64'
|
||||||
|
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0'
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0'
|
||||||
@@ -82,7 +82,6 @@ dependencies {
|
|||||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||||
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
|
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
|
||||||
implementation 'androidx.asynclayoutinflater:asynclayoutinflater:1.0.0'
|
|
||||||
implementation 'androidx.preference:preference-ktx:1.2.0'
|
implementation 'androidx.preference:preference-ktx:1.2.0'
|
||||||
implementation 'androidx.work:work-runtime-ktx:2.7.1'
|
implementation 'androidx.work:work-runtime-ktx:2.7.1'
|
||||||
implementation 'com.google.android.material:material:1.6.0-beta01'
|
implementation 'com.google.android.material:material:1.6.0-beta01'
|
||||||
|
|||||||
@@ -3,20 +3,12 @@ package org.koitharu.kotatsu.core.ui
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import java.io.PrintWriter
|
|
||||||
import java.io.StringWriter
|
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
class AppCrashHandler(private val applicationContext: Context) : Thread.UncaughtExceptionHandler {
|
class AppCrashHandler(private val applicationContext: Context) : Thread.UncaughtExceptionHandler {
|
||||||
|
|
||||||
override fun uncaughtException(t: Thread, e: Throwable) {
|
override fun uncaughtException(t: Thread, e: Throwable) {
|
||||||
val crashInfo = buildString {
|
val intent = CrashActivity.newIntent(applicationContext, e)
|
||||||
val writer = StringWriter()
|
|
||||||
e.printStackTrace(PrintWriter(writer))
|
|
||||||
append(writer.toString().trimIndent())
|
|
||||||
}
|
|
||||||
val intent = Intent(applicationContext, CrashActivity::class.java)
|
|
||||||
intent.putExtra(Intent.EXTRA_TEXT, crashInfo)
|
|
||||||
intent.flags = (Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
|
intent.flags = (Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
try {
|
try {
|
||||||
applicationContext.startActivity(intent)
|
applicationContext.startActivity(intent)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.core.ui
|
|||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@@ -11,6 +12,7 @@ import android.view.View
|
|||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.databinding.ActivityCrashBinding
|
import org.koitharu.kotatsu.databinding.ActivityCrashBinding
|
||||||
import org.koitharu.kotatsu.main.ui.MainActivity
|
import org.koitharu.kotatsu.main.ui.MainActivity
|
||||||
|
import org.koitharu.kotatsu.parsers.util.ellipsize
|
||||||
import org.koitharu.kotatsu.utils.ShareHelper
|
import org.koitharu.kotatsu.utils.ShareHelper
|
||||||
|
|
||||||
class CrashActivity : Activity(), View.OnClickListener {
|
class CrashActivity : Activity(), View.OnClickListener {
|
||||||
@@ -63,4 +65,19 @@ class CrashActivity : Activity(), View.OnClickListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private const val MAX_TRACE_SIZE = 131071
|
||||||
|
|
||||||
|
fun newIntent(context: Context, error: Throwable): Intent {
|
||||||
|
val crashInfo = error
|
||||||
|
.stackTraceToString()
|
||||||
|
.trimIndent()
|
||||||
|
.ellipsize(MAX_TRACE_SIZE)
|
||||||
|
val intent = Intent(context, CrashActivity::class.java)
|
||||||
|
intent.putExtra(Intent.EXTRA_TEXT, crashInfo)
|
||||||
|
return intent
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,7 @@ import coil.ImageLoader
|
|||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import coil.size.Scale
|
import coil.size.Scale
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
@@ -39,7 +40,7 @@ class DownloadManager(
|
|||||||
private val localMangaRepository: LocalMangaRepository,
|
private val localMangaRepository: LocalMangaRepository,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val connectivityManager = context.getSystemService(
|
private val connectivityManager = context.applicationContext.getSystemService(
|
||||||
Context.CONNECTIVITY_SERVICE
|
Context.CONNECTIVITY_SERVICE
|
||||||
) as ConnectivityManager
|
) as ConnectivityManager
|
||||||
private val coverWidth = context.resources.getDimensionPixelSize(
|
private val coverWidth = context.resources.getDimensionPixelSize(
|
||||||
@@ -49,7 +50,7 @@ class DownloadManager(
|
|||||||
androidx.core.R.dimen.compat_notification_large_icon_max_height
|
androidx.core.R.dimen.compat_notification_large_icon_max_height
|
||||||
)
|
)
|
||||||
|
|
||||||
fun downloadManga(manga: Manga, chaptersIds: Set<Long>?, startId: Int) = flow<State> {
|
fun downloadManga(manga: Manga, chaptersIds: Set<Long>?, startId: Int): Flow<State> = flow {
|
||||||
emit(State.Preparing(startId, manga, null))
|
emit(State.Preparing(startId, manga, null))
|
||||||
var cover: Drawable? = null
|
var cover: Drawable? = null
|
||||||
val destination = localMangaRepository.getOutputDir()
|
val destination = localMangaRepository.getOutputDir()
|
||||||
@@ -102,13 +103,15 @@ class DownloadManager(
|
|||||||
}
|
}
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
emit(State.Progress(
|
emit(
|
||||||
startId, manga, cover,
|
State.Progress(
|
||||||
totalChapters = chapters.size,
|
startId, manga, cover,
|
||||||
currentChapter = chapterIndex,
|
totalChapters = chapters.size,
|
||||||
totalPages = pages.size,
|
currentChapter = chapterIndex,
|
||||||
currentPage = pageIndex,
|
totalPages = pages.size,
|
||||||
))
|
currentPage = pageIndex,
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -191,7 +194,7 @@ class DownloadManager(
|
|||||||
val currentChapter: Int,
|
val currentChapter: Int,
|
||||||
val totalPages: Int,
|
val totalPages: Int,
|
||||||
val currentPage: Int,
|
val currentPage: Int,
|
||||||
): State {
|
) : State {
|
||||||
|
|
||||||
val max: Int = totalChapters * totalPages
|
val max: Int = totalChapters * totalPages
|
||||||
|
|
||||||
@@ -204,7 +207,7 @@ class DownloadManager(
|
|||||||
override val startId: Int,
|
override val startId: Int,
|
||||||
override val manga: Manga,
|
override val manga: Manga,
|
||||||
override val cover: Drawable?,
|
override val cover: Drawable?,
|
||||||
): State
|
) : State
|
||||||
|
|
||||||
data class Done(
|
data class Done(
|
||||||
override val startId: Int,
|
override val startId: Int,
|
||||||
@@ -224,7 +227,7 @@ class DownloadManager(
|
|||||||
override val startId: Int,
|
override val startId: Int,
|
||||||
override val manga: Manga,
|
override val manga: Manga,
|
||||||
override val cover: Drawable?,
|
override val cover: Drawable?,
|
||||||
): State
|
) : State
|
||||||
|
|
||||||
data class PostProcessing(
|
data class PostProcessing(
|
||||||
override val startId: Int,
|
override val startId: Int,
|
||||||
@@ -232,4 +235,4 @@ class DownloadManager(
|
|||||||
override val cover: Drawable?,
|
override val cover: Drawable?,
|
||||||
) : State
|
) : State
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,7 @@ import androidx.core.app.NotificationManagerCompat
|
|||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.graphics.drawable.toBitmap
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.core.ui.CrashActivity
|
||||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||||
import org.koitharu.kotatsu.download.domain.DownloadManager
|
import org.koitharu.kotatsu.download.domain.DownloadManager
|
||||||
import org.koitharu.kotatsu.download.ui.DownloadsActivity
|
import org.koitharu.kotatsu.download.ui.DownloadsActivity
|
||||||
@@ -81,6 +82,14 @@ class DownloadNotification(
|
|||||||
builder.setSubText(context.getString(R.string.error))
|
builder.setSubText(context.getString(R.string.error))
|
||||||
builder.setContentText(message)
|
builder.setContentText(message)
|
||||||
builder.setAutoCancel(true)
|
builder.setAutoCancel(true)
|
||||||
|
builder.setContentIntent(
|
||||||
|
PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
state.manga.hashCode(),
|
||||||
|
CrashActivity.newIntent(context, state.error),
|
||||||
|
PendingIntent.FLAG_CANCEL_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
|
||||||
|
)
|
||||||
|
)
|
||||||
builder.setCategory(NotificationCompat.CATEGORY_ERROR)
|
builder.setCategory(NotificationCompat.CATEGORY_ERROR)
|
||||||
builder.setStyle(NotificationCompat.BigTextStyle().bigText(message))
|
builder.setStyle(NotificationCompat.BigTextStyle().bigText(message))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.mapLatest
|
import kotlinx.coroutines.flow.mapLatest
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -105,6 +106,7 @@ class DownloadService : BaseService() {
|
|||||||
try {
|
try {
|
||||||
withContext(Dispatchers.Default) {
|
withContext(Dispatchers.Default) {
|
||||||
downloadManager.downloadManga(manga, chaptersIds, startId)
|
downloadManager.downloadManga(manga, chaptersIds, startId)
|
||||||
|
.distinctUntilChanged()
|
||||||
.collect { state ->
|
.collect { state ->
|
||||||
stateFlow.value = state
|
stateFlow.value = state
|
||||||
notificationManager.notify(startId, notification.create(state))
|
notificationManager.notify(startId, notification.create(state))
|
||||||
@@ -181,7 +183,7 @@ class DownloadService : BaseService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getCancelIntent(startId: Int) = Intent(ACTION_DOWNLOAD_CANCEL)
|
fun getCancelIntent(startId: Int) = Intent(ACTION_DOWNLOAD_CANCEL)
|
||||||
.putExtra(ACTION_DOWNLOAD_CANCEL, startId)
|
.putExtra(EXTRA_CANCEL_ID, startId)
|
||||||
|
|
||||||
private fun confirmDataTransfer(context: Context, callback: () -> Unit) {
|
private fun confirmDataTransfer(context: Context, callback: () -> Unit) {
|
||||||
val settings = GlobalContext.get().get<AppSettings>()
|
val settings = GlobalContext.get().get<AppSettings>()
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
|||||||
import org.koitharu.kotatsu.core.prefs.ListMode
|
import org.koitharu.kotatsu.core.prefs.ListMode
|
||||||
import org.koitharu.kotatsu.databinding.FragmentListBinding
|
import org.koitharu.kotatsu.databinding.FragmentListBinding
|
||||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.AsyncViewFactory
|
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.MangaListAdapter
|
import org.koitharu.kotatsu.list.ui.adapter.MangaListAdapter
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.MangaListAdapter.Companion.ITEM_TYPE_MANGA_GRID
|
import org.koitharu.kotatsu.list.ui.adapter.MangaListAdapter.Companion.ITEM_TYPE_MANGA_GRID
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.MangaListListener
|
import org.koitharu.kotatsu.list.ui.adapter.MangaListListener
|
||||||
@@ -35,12 +34,10 @@ import org.koitharu.kotatsu.parsers.model.Manga
|
|||||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||||
import org.koitharu.kotatsu.utils.ext.*
|
import org.koitharu.kotatsu.utils.ext.*
|
||||||
|
|
||||||
private const val PREFETCH_ITEM_LIST = 10
|
abstract class MangaListFragment :
|
||||||
private const val PREFETCH_ITEM_DETAILED = 8
|
BaseFragment<FragmentListBinding>(),
|
||||||
private const val PREFETCH_ITEM_GRID = 16
|
PaginationScrollListener.Callback,
|
||||||
|
MangaListListener,
|
||||||
abstract class MangaListFragment : BaseFragment<FragmentListBinding>(),
|
|
||||||
PaginationScrollListener.Callback, MangaListListener,
|
|
||||||
SwipeRefreshLayout.OnRefreshListener {
|
SwipeRefreshLayout.OnRefreshListener {
|
||||||
|
|
||||||
private var listAdapter: MangaListAdapter? = null
|
private var listAdapter: MangaListAdapter? = null
|
||||||
@@ -50,7 +47,6 @@ abstract class MangaListFragment : BaseFragment<FragmentListBinding>(),
|
|||||||
private val listCommitCallback = Runnable {
|
private val listCommitCallback = Runnable {
|
||||||
spanSizeLookup.invalidateCache()
|
spanSizeLookup.invalidateCache()
|
||||||
}
|
}
|
||||||
private var asyncViewFactory: AsyncViewFactory? = null
|
|
||||||
open val isSwipeRefreshEnabled = true
|
open val isSwipeRefreshEnabled = true
|
||||||
|
|
||||||
protected abstract val viewModel: MangaListViewModel
|
protected abstract val viewModel: MangaListViewModel
|
||||||
@@ -67,12 +63,10 @@ abstract class MangaListFragment : BaseFragment<FragmentListBinding>(),
|
|||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
asyncViewFactory = AsyncViewFactory(binding.recyclerView)
|
|
||||||
listAdapter = MangaListAdapter(
|
listAdapter = MangaListAdapter(
|
||||||
coil = get(),
|
coil = get(),
|
||||||
lifecycleOwner = viewLifecycleOwner,
|
lifecycleOwner = viewLifecycleOwner,
|
||||||
listener = this,
|
listener = this,
|
||||||
viewFactory = checkNotNull(asyncViewFactory),
|
|
||||||
)
|
)
|
||||||
paginationListener = PaginationScrollListener(4, this)
|
paginationListener = PaginationScrollListener(4, this)
|
||||||
with(binding.recyclerView) {
|
with(binding.recyclerView) {
|
||||||
@@ -97,8 +91,6 @@ abstract class MangaListFragment : BaseFragment<FragmentListBinding>(),
|
|||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
listAdapter = null
|
listAdapter = null
|
||||||
paginationListener = null
|
paginationListener = null
|
||||||
asyncViewFactory?.clear()
|
|
||||||
asyncViewFactory = null
|
|
||||||
spanSizeLookup.invalidateCache()
|
spanSizeLookup.invalidateCache()
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
@@ -172,7 +164,7 @@ abstract class MangaListFragment : BaseFragment<FragmentListBinding>(),
|
|||||||
@CallSuper
|
@CallSuper
|
||||||
protected open fun onLoadingStateChanged(isLoading: Boolean) {
|
protected open fun onLoadingStateChanged(isLoading: Boolean) {
|
||||||
binding.swipeRefreshLayout.isEnabled = binding.swipeRefreshLayout.isRefreshing ||
|
binding.swipeRefreshLayout.isEnabled = binding.swipeRefreshLayout.isRefreshing ||
|
||||||
isSwipeRefreshEnabled && !isLoading
|
isSwipeRefreshEnabled && !isLoading
|
||||||
if (!isLoading) {
|
if (!isLoading) {
|
||||||
binding.swipeRefreshLayout.isRefreshing = false
|
binding.swipeRefreshLayout.isRefreshing = false
|
||||||
}
|
}
|
||||||
@@ -219,26 +211,18 @@ abstract class MangaListFragment : BaseFragment<FragmentListBinding>(),
|
|||||||
with(binding.recyclerView) {
|
with(binding.recyclerView) {
|
||||||
clearItemDecorations()
|
clearItemDecorations()
|
||||||
removeOnLayoutChangeListener(spanResolver)
|
removeOnLayoutChangeListener(spanResolver)
|
||||||
asyncViewFactory?.clear()
|
|
||||||
val isListPending = viewModel.isListPending()
|
|
||||||
when (mode) {
|
when (mode) {
|
||||||
ListMode.LIST -> {
|
ListMode.LIST -> {
|
||||||
layoutManager = FitHeightLinearLayoutManager(context)
|
layoutManager = FitHeightLinearLayoutManager(context)
|
||||||
val spacing = resources.getDimensionPixelOffset(R.dimen.list_spacing)
|
val spacing = resources.getDimensionPixelOffset(R.dimen.list_spacing)
|
||||||
addItemDecoration(SpacingItemDecoration(spacing))
|
addItemDecoration(SpacingItemDecoration(spacing))
|
||||||
updatePadding(left = spacing, right = spacing)
|
updatePadding(left = spacing, right = spacing)
|
||||||
if (isListPending) {
|
|
||||||
asyncViewFactory?.prefetch(R.layout.item_manga_list, PREFETCH_ITEM_LIST)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ListMode.DETAILED_LIST -> {
|
ListMode.DETAILED_LIST -> {
|
||||||
layoutManager = FitHeightLinearLayoutManager(context)
|
layoutManager = FitHeightLinearLayoutManager(context)
|
||||||
val spacing = resources.getDimensionPixelOffset(R.dimen.list_spacing)
|
val spacing = resources.getDimensionPixelOffset(R.dimen.list_spacing)
|
||||||
updatePadding(left = spacing, right = spacing)
|
updatePadding(left = spacing, right = spacing)
|
||||||
addItemDecoration(SpacingItemDecoration(spacing))
|
addItemDecoration(SpacingItemDecoration(spacing))
|
||||||
if (isListPending) {
|
|
||||||
asyncViewFactory?.prefetch(R.layout.item_manga_list_details, PREFETCH_ITEM_DETAILED)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ListMode.GRID -> {
|
ListMode.GRID -> {
|
||||||
layoutManager = FitHeightGridLayoutManager(context, spanResolver.spanCount).also {
|
layoutManager = FitHeightGridLayoutManager(context, spanResolver.spanCount).also {
|
||||||
@@ -248,9 +232,6 @@ abstract class MangaListFragment : BaseFragment<FragmentListBinding>(),
|
|||||||
addItemDecoration(SpacingItemDecoration(spacing))
|
addItemDecoration(SpacingItemDecoration(spacing))
|
||||||
updatePadding(left = spacing, right = spacing)
|
updatePadding(left = spacing, right = spacing)
|
||||||
addOnLayoutChangeListener(spanResolver)
|
addOnLayoutChangeListener(spanResolver)
|
||||||
if (isListPending) {
|
|
||||||
asyncViewFactory?.prefetch(R.layout.item_manga_grid, PREFETCH_ITEM_GRID)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,10 +44,4 @@ abstract class MangaListViewModel(
|
|||||||
abstract fun onRefresh()
|
abstract fun onRefresh()
|
||||||
|
|
||||||
abstract fun onRetry()
|
abstract fun onRetry()
|
||||||
|
|
||||||
fun isListPending(): Boolean {
|
|
||||||
return content.value?.any {
|
|
||||||
it is MangaListModel || it is MangaGridModel || it is MangaListDetailedModel
|
|
||||||
} != true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
package org.koitharu.kotatsu.list.ui.adapter
|
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import android.util.SparseArray
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.annotation.LayoutRes
|
|
||||||
import androidx.asynclayoutinflater.view.AsyncLayoutInflater
|
|
||||||
import androidx.core.util.valueIterator
|
|
||||||
import org.koitharu.kotatsu.BuildConfig
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class AsyncViewFactory(private val parent: ViewGroup) : AsyncLayoutInflater.OnInflateFinishedListener {
|
|
||||||
|
|
||||||
private val asyncInflater = AsyncLayoutInflater(parent.context)
|
|
||||||
private val pool = SparseArray<LinkedList<View>>()
|
|
||||||
|
|
||||||
override fun onInflateFinished(view: View, resid: Int, parent: ViewGroup?) {
|
|
||||||
var list = pool.get(resid)
|
|
||||||
if (list != null) {
|
|
||||||
list.addLast(view)
|
|
||||||
} else {
|
|
||||||
list = LinkedList()
|
|
||||||
list.add(view)
|
|
||||||
pool.put(resid, list)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun clear() {
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
pool.valueIterator().forEach {
|
|
||||||
if (it.isNotEmpty()) {
|
|
||||||
Log.w("AsyncViewFactory", "You have ${it.size} unconsumed prefetched items")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pool.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun prefetch(@LayoutRes resId: Int, count: Int) {
|
|
||||||
if (count <= 0) return
|
|
||||||
repeat(count) {
|
|
||||||
asyncInflater.inflate(resId, parent, this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun get(@LayoutRes resId: Int): View? {
|
|
||||||
val result = pool.get(resId)?.removeFirstOrNull()
|
|
||||||
if (BuildConfig.DEBUG && result == null) {
|
|
||||||
Log.w("AsyncViewFactory", "Item requested but missing")
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getCount(@LayoutRes resId: Int): Int {
|
|
||||||
return pool[resId]?.size ?: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -20,15 +20,8 @@ fun mangaGridItemAD(
|
|||||||
coil: ImageLoader,
|
coil: ImageLoader,
|
||||||
lifecycleOwner: LifecycleOwner,
|
lifecycleOwner: LifecycleOwner,
|
||||||
clickListener: OnListItemClickListener<Manga>,
|
clickListener: OnListItemClickListener<Manga>,
|
||||||
viewFactory: AsyncViewFactory,
|
|
||||||
) = adapterDelegateViewBinding<MangaGridModel, ListModel, ItemMangaGridBinding>(
|
) = adapterDelegateViewBinding<MangaGridModel, ListModel, ItemMangaGridBinding>(
|
||||||
{ inflater, parent ->
|
{ inflater, parent -> ItemMangaGridBinding.inflate(inflater, parent, false) }
|
||||||
viewFactory[R.layout.item_manga_grid]?.let {
|
|
||||||
ItemMangaGridBinding.bind(it)
|
|
||||||
} ?: run {
|
|
||||||
ItemMangaGridBinding.inflate(inflater, parent, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
var imageRequest: Disposable? = null
|
var imageRequest: Disposable? = null
|
||||||
|
|||||||
@@ -12,20 +12,13 @@ class MangaListAdapter(
|
|||||||
coil: ImageLoader,
|
coil: ImageLoader,
|
||||||
lifecycleOwner: LifecycleOwner,
|
lifecycleOwner: LifecycleOwner,
|
||||||
listener: MangaListListener,
|
listener: MangaListListener,
|
||||||
viewFactory: AsyncViewFactory,
|
|
||||||
) : AsyncListDifferDelegationAdapter<ListModel>(DiffCallback()) {
|
) : AsyncListDifferDelegationAdapter<ListModel>(DiffCallback()) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
delegatesManager
|
delegatesManager
|
||||||
.addDelegate(
|
.addDelegate(ITEM_TYPE_MANGA_LIST, mangaListItemAD(coil, lifecycleOwner, listener))
|
||||||
ITEM_TYPE_MANGA_LIST,
|
.addDelegate(ITEM_TYPE_MANGA_LIST_DETAILED, mangaListDetailedItemAD(coil, lifecycleOwner, listener))
|
||||||
mangaListItemAD(coil, lifecycleOwner, listener, viewFactory)
|
.addDelegate(ITEM_TYPE_MANGA_GRID, mangaGridItemAD(coil, lifecycleOwner, listener))
|
||||||
)
|
|
||||||
.addDelegate(
|
|
||||||
ITEM_TYPE_MANGA_LIST_DETAILED,
|
|
||||||
mangaListDetailedItemAD(coil, lifecycleOwner, listener, viewFactory)
|
|
||||||
)
|
|
||||||
.addDelegate(ITEM_TYPE_MANGA_GRID, mangaGridItemAD(coil, lifecycleOwner, listener, viewFactory))
|
|
||||||
.addDelegate(ITEM_TYPE_LOADING_FOOTER, loadingFooterAD())
|
.addDelegate(ITEM_TYPE_LOADING_FOOTER, loadingFooterAD())
|
||||||
.addDelegate(ITEM_TYPE_LOADING_STATE, loadingStateAD())
|
.addDelegate(ITEM_TYPE_LOADING_STATE, loadingStateAD())
|
||||||
.addDelegate(ITEM_TYPE_DATE, relatedDateItemAD())
|
.addDelegate(ITEM_TYPE_DATE, relatedDateItemAD())
|
||||||
|
|||||||
@@ -21,15 +21,8 @@ fun mangaListDetailedItemAD(
|
|||||||
coil: ImageLoader,
|
coil: ImageLoader,
|
||||||
lifecycleOwner: LifecycleOwner,
|
lifecycleOwner: LifecycleOwner,
|
||||||
clickListener: OnListItemClickListener<Manga>,
|
clickListener: OnListItemClickListener<Manga>,
|
||||||
viewFactory: AsyncViewFactory,
|
|
||||||
) = adapterDelegateViewBinding<MangaListDetailedModel, ListModel, ItemMangaListDetailsBinding>(
|
) = adapterDelegateViewBinding<MangaListDetailedModel, ListModel, ItemMangaListDetailsBinding>(
|
||||||
{ inflater, parent ->
|
{ inflater, parent -> ItemMangaListDetailsBinding.inflate(inflater, parent, false) }
|
||||||
viewFactory[R.layout.item_manga_list_details]?.let {
|
|
||||||
ItemMangaListDetailsBinding.bind(it)
|
|
||||||
} ?: run {
|
|
||||||
ItemMangaListDetailsBinding.inflate(inflater, parent, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
var imageRequest: Disposable? = null
|
var imageRequest: Disposable? = null
|
||||||
|
|||||||
@@ -21,15 +21,8 @@ fun mangaListItemAD(
|
|||||||
coil: ImageLoader,
|
coil: ImageLoader,
|
||||||
lifecycleOwner: LifecycleOwner,
|
lifecycleOwner: LifecycleOwner,
|
||||||
clickListener: OnListItemClickListener<Manga>,
|
clickListener: OnListItemClickListener<Manga>,
|
||||||
viewFactory: AsyncViewFactory,
|
|
||||||
) = adapterDelegateViewBinding<MangaListModel, ListModel, ItemMangaListBinding>(
|
) = adapterDelegateViewBinding<MangaListModel, ListModel, ItemMangaListBinding>(
|
||||||
{ inflater, parent ->
|
{ inflater, parent -> ItemMangaListBinding.inflate(inflater, parent, false) }
|
||||||
viewFactory[R.layout.item_manga_list]?.let {
|
|
||||||
ItemMangaListBinding.bind(it)
|
|
||||||
} ?: run {
|
|
||||||
ItemMangaListBinding.inflate(inflater, parent, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
var imageRequest: Disposable? = null
|
var imageRequest: Disposable? = null
|
||||||
|
|||||||
Reference in New Issue
Block a user