Fixes batch

master
Koitharu 2 years ago
parent 5301cc7f97
commit 1d28538893
Signed by: Koitharu
GPG Key ID: 676DEE768C17A9D7

@ -82,8 +82,7 @@ afterEvaluate {
} }
} }
dependencies { dependencies {
//noinspection GradleDependency implementation('com.github.KotatsuApp:kotatsu-parsers:1.2.1') {
implementation('com.github.KotatsuApp:kotatsu-parsers:645006fde8') {
exclude group: 'org.json', module: 'json' exclude group: 'org.json', module: 'json'
} }

@ -63,7 +63,6 @@ class KotatsuApp : BaseApp() {
detectRetainInstanceUsage() detectRetainInstanceUsage()
detectSetUserVisibleHint() detectSetUserVisibleHint()
detectWrongNestedHierarchy() detectWrongNestedHierarchy()
detectTargetFragmentUsage()
detectFragmentReuse() detectFragmentReuse()
penaltyLog() penaltyLog()
if (notifier != null) { if (notifier != null) {

@ -165,13 +165,14 @@ class AutoFixService : CoroutineIntentService() {
} else { } else {
error.getDisplayMessage(applicationContext.resources) error.getDisplayMessage(applicationContext.resources)
}, },
) ).setSmallIcon(android.R.drawable.stat_notify_error)
.setSmallIcon(android.R.drawable.stat_notify_error) ErrorReporterReceiver.getPendingIntent(applicationContext, error)?.let { reportIntent ->
.addAction( notification.addAction(
R.drawable.ic_alert_outline, R.drawable.ic_alert_outline,
applicationContext.getString(R.string.report), applicationContext.getString(R.string.report),
ErrorReporterReceiver.getPendingIntent(applicationContext, error), reportIntent,
) )
}
} }
return notification.build() return notification.build()
} }

@ -5,9 +5,11 @@ import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.BadParcelableException
import androidx.core.app.PendingIntentCompat import androidx.core.app.PendingIntentCompat
import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.core.util.ext.getSerializableExtraCompat import org.koitharu.kotatsu.core.util.ext.getSerializableExtraCompat
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.core.util.ext.report import org.koitharu.kotatsu.core.util.ext.report
class ErrorReporterReceiver : BroadcastReceiver() { class ErrorReporterReceiver : BroadcastReceiver() {
@ -22,12 +24,15 @@ class ErrorReporterReceiver : BroadcastReceiver() {
private const val EXTRA_ERROR = "err" private const val EXTRA_ERROR = "err"
private const val ACTION_REPORT = "${BuildConfig.APPLICATION_ID}.action.REPORT_ERROR" private const val ACTION_REPORT = "${BuildConfig.APPLICATION_ID}.action.REPORT_ERROR"
fun getPendingIntent(context: Context, e: Throwable): PendingIntent { fun getPendingIntent(context: Context, e: Throwable): PendingIntent? = try {
val intent = Intent(context, ErrorReporterReceiver::class.java) val intent = Intent(context, ErrorReporterReceiver::class.java)
intent.setAction(ACTION_REPORT) intent.setAction(ACTION_REPORT)
intent.setData(Uri.parse("err://${e.hashCode()}")) intent.setData(Uri.parse("err://${e.hashCode()}"))
intent.putExtra(EXTRA_ERROR, e) intent.putExtra(EXTRA_ERROR, e)
return checkNotNull(PendingIntentCompat.getBroadcast(context, 0, intent, 0, false)) PendingIntentCompat.getBroadcast(context, 0, intent, 0, false)
} catch (e: BadParcelableException) {
e.printStackTraceDebug()
null
} }
} }
} }

@ -6,7 +6,6 @@ import androidx.activity.result.ActivityResultCaller
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.collection.MutableScatterMap import androidx.collection.MutableScatterMap
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
@ -19,7 +18,8 @@ import org.koitharu.kotatsu.core.exceptions.ProxyConfigException
import org.koitharu.kotatsu.core.exceptions.UnsupportedSourceException import org.koitharu.kotatsu.core.exceptions.UnsupportedSourceException
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.ui.dialog.ErrorDetailsDialog import org.koitharu.kotatsu.core.ui.dialog.ErrorDetailsDialog
import org.koitharu.kotatsu.core.util.ext.findActivity import org.koitharu.kotatsu.core.ui.dialog.buildAlertDialog
import org.koitharu.kotatsu.core.util.ext.restartApplication
import org.koitharu.kotatsu.parsers.exception.AuthRequiredException import org.koitharu.kotatsu.parsers.exception.AuthRequiredException
import org.koitharu.kotatsu.parsers.exception.NotFoundException import org.koitharu.kotatsu.parsers.exception.NotFoundException
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
@ -124,15 +124,16 @@ class ExceptionResolver @AssistedInject constructor(
Toast.makeText(ctx, R.string.operation_not_supported, Toast.LENGTH_SHORT).show() Toast.makeText(ctx, R.string.operation_not_supported, Toast.LENGTH_SHORT).show()
return return
} }
MaterialAlertDialogBuilder(ctx) buildAlertDialog(ctx) {
.setTitle(R.string.ignore_ssl_errors) setTitle(R.string.ignore_ssl_errors)
.setMessage(R.string.ignore_ssl_errors_summary) setMessage(R.string.ignore_ssl_errors_summary)
.setPositiveButton(R.string.apply) { _, _ -> setPositiveButton(R.string.apply) { _, _ ->
settings.isSSLBypassEnabled = true settings.isSSLBypassEnabled = true
Toast.makeText(ctx, R.string.settings_apply_restart_required, Toast.LENGTH_SHORT).show() Toast.makeText(ctx, R.string.settings_apply_restart_required, Toast.LENGTH_LONG).show()
ctx.findActivity()?.finishAffinity() ctx.restartApplication()
}.setNegativeButton(android.R.string.cancel, null) }
.show() setNegativeButton(android.R.string.cancel, null)
}.show()
} }
private inline fun Host.withContext(block: Context.() -> Unit) { private inline fun Host.withContext(block: Context.() -> Unit) {

@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.koitharu.kotatsu.list.ui.adapter.ListItemType import org.koitharu.kotatsu.list.ui.adapter.ListItemType
import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.model.ListModel
import java.util.Collections import org.koitharu.kotatsu.parsers.util.move
import java.util.LinkedList import java.util.LinkedList
open class ReorderableListAdapter<T : ListModel> : ListDelegationAdapter<List<T>>(), FlowCollector<List<T>?> { open class ReorderableListAdapter<T : ListModel> : ListDelegationAdapter<List<T>>(), FlowCollector<List<T>?> {
@ -36,7 +36,9 @@ open class ReorderableListAdapter<T : ListModel> : ListDelegationAdapter<List<T>
override fun setItems(items: List<T>?) = super.setItems(items) override fun setItems(items: List<T>?) = super.setItems(items)
fun reorderItems(oldPos: Int, newPos: Int) { fun reorderItems(oldPos: Int, newPos: Int) {
Collections.swap(items ?: return, oldPos, newPos) val reordered = items?.toMutableList() ?: return
reordered.move(oldPos, newPos)
super.setItems(reordered)
notifyItemMoved(oldPos, newPos) notifyItemMoved(oldPos, newPos)
} }

@ -7,10 +7,12 @@ import android.app.ActivityManager
import android.app.ActivityManager.MemoryInfo import android.app.ActivityManager.MemoryInfo
import android.app.ActivityOptions import android.app.ActivityOptions
import android.app.LocaleConfig import android.app.LocaleConfig
import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Context.ACTIVITY_SERVICE import android.content.Context.ACTIVITY_SERVICE
import android.content.Context.POWER_SERVICE import android.content.Context.POWER_SERVICE
import android.content.ContextWrapper import android.content.ContextWrapper
import android.content.Intent
import android.content.OperationApplicationException import android.content.OperationApplicationException
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.SyncResult import android.content.SyncResult
@ -33,6 +35,7 @@ import androidx.annotation.IntegerRes
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDialog import androidx.appcompat.app.AppCompatDialog
import androidx.core.app.ActivityCompat
import androidx.core.app.ActivityOptionsCompat import androidx.core.app.ActivityOptionsCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
@ -61,6 +64,7 @@ import okio.use
import org.json.JSONException import org.json.JSONException
import org.jsoup.internal.StringUtil.StringJoiner import org.jsoup.internal.StringUtil.StringJoiner
import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.main.ui.MainActivity
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserException import org.xmlpull.v1.XmlPullParserException
@ -274,3 +278,10 @@ fun WebView.configureForParser(userAgentOverride: String?) = with(settings) {
userAgentString = userAgentOverride userAgentString = userAgentOverride
} }
} }
fun Context.restartApplication() {
val activity = findActivity()
val intent = Intent.makeRestartActivityTask(ComponentName(this, MainActivity::class.java))
startActivity(intent)
activity?.finishAndRemoveTask()
}

@ -213,13 +213,15 @@ class DownloadNotificationFactory @AssistedInject constructor(
builder.setWhen(System.currentTimeMillis()) builder.setWhen(System.currentTimeMillis())
builder.setStyle(NotificationCompat.BigTextStyle().bigText(state.errorMessage)) builder.setStyle(NotificationCompat.BigTextStyle().bigText(state.errorMessage))
if (state.error.isReportable()) { if (state.error.isReportable()) {
builder.addAction( ErrorReporterReceiver.getPendingIntent(context, state.error)?.let { reportIntent ->
NotificationCompat.Action( builder.addAction(
0, NotificationCompat.Action(
context.getString(R.string.report), 0,
ErrorReporterReceiver.getPendingIntent(context, state.error), context.getString(R.string.report),
), reportIntent,
) ),
)
}
} }
} }

@ -139,11 +139,13 @@ class ImportService : CoroutineIntentService() {
notification.setContentTitle(applicationContext.getString(R.string.error_occurred)) notification.setContentTitle(applicationContext.getString(R.string.error_occurred))
.setContentText(error.getDisplayMessage(applicationContext.resources)) .setContentText(error.getDisplayMessage(applicationContext.resources))
.setSmallIcon(android.R.drawable.stat_notify_error) .setSmallIcon(android.R.drawable.stat_notify_error)
.addAction( ErrorReporterReceiver.getPendingIntent(applicationContext, error)?.let { reportIntent ->
notification.addAction(
R.drawable.ic_alert_outline, R.drawable.ic_alert_outline,
applicationContext.getString(R.string.report), applicationContext.getString(R.string.report),
ErrorReporterReceiver.getPendingIntent(applicationContext, error), reportIntent,
) )
}
} }
return notification.build() return notification.build()
} }

@ -10,9 +10,9 @@ import org.jsoup.HttpStatusException
import org.koitharu.kotatsu.bookmarks.domain.Bookmark import org.koitharu.kotatsu.bookmarks.domain.Bookmark
import org.koitharu.kotatsu.bookmarks.domain.BookmarksRepository import org.koitharu.kotatsu.bookmarks.domain.BookmarksRepository
import org.koitharu.kotatsu.core.model.findById import org.koitharu.kotatsu.core.model.findById
import org.koitharu.kotatsu.core.model.isLocal
import org.koitharu.kotatsu.core.parser.MangaDataRepository import org.koitharu.kotatsu.core.parser.MangaDataRepository
import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.parser.ParserMangaRepository
import org.koitharu.kotatsu.core.util.ext.ifNullOrEmpty import org.koitharu.kotatsu.core.util.ext.ifNullOrEmpty
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.parsers.exception.ParseException import org.koitharu.kotatsu.parsers.exception.ParseException
@ -70,10 +70,10 @@ class CoverRestoreInterceptor @Inject constructor(
} }
private suspend fun restoreMangaImpl(manga: Manga): Boolean { private suspend fun restoreMangaImpl(manga: Manga): Boolean {
if (dataRepository.findMangaById(manga.id) == null) { if (dataRepository.findMangaById(manga.id) == null || manga.isLocal) {
return false return false
} }
val repo = repositoryFactory.create(manga.source) as? ParserMangaRepository ?: return false val repo = repositoryFactory.create(manga.source)
val fixed = repo.find(manga) ?: return false val fixed = repo.find(manga) ?: return false
return if (fixed != manga) { return if (fixed != manga) {
dataRepository.storeManga(fixed) dataRepository.storeManga(fixed)
@ -100,7 +100,10 @@ class CoverRestoreInterceptor @Inject constructor(
} }
private suspend fun restoreBookmarkImpl(bookmark: Bookmark): Boolean { private suspend fun restoreBookmarkImpl(bookmark: Bookmark): Boolean {
val repo = repositoryFactory.create(bookmark.manga.source) as? ParserMangaRepository ?: return false if (bookmark.manga.isLocal) {
return false
}
val repo = repositoryFactory.create(bookmark.manga.source)
val chapter = repo.getDetails(bookmark.manga).chapters?.findById(bookmark.chapterId) ?: return false val chapter = repo.getDetails(bookmark.manga).chapters?.findById(bookmark.chapterId) ?: return false
val page = repo.getPages(chapter)[bookmark.page] val page = repo.getPages(chapter)[bookmark.page]
val imageUrl = page.preview.ifNullOrEmpty { page.url } val imageUrl = page.preview.ifNullOrEmpty { page.url }

@ -55,7 +55,7 @@ import org.koitharu.kotatsu.details.service.MangaPrefetchService
import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.details.ui.DetailsActivity
import org.koitharu.kotatsu.favourites.ui.container.FavouritesContainerFragment import org.koitharu.kotatsu.favourites.ui.container.FavouritesContainerFragment
import org.koitharu.kotatsu.history.ui.HistoryListFragment import org.koitharu.kotatsu.history.ui.HistoryListFragment
import org.koitharu.kotatsu.local.data.index.LocalMangaIndex import org.koitharu.kotatsu.local.ui.LocalIndexUpdateService
import org.koitharu.kotatsu.local.ui.LocalStorageCleanupWorker import org.koitharu.kotatsu.local.ui.LocalStorageCleanupWorker
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
import org.koitharu.kotatsu.main.ui.owners.BottomNavOwner import org.koitharu.kotatsu.main.ui.owners.BottomNavOwner
@ -352,7 +352,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), AppBarOwner, BottomNav
MangaPrefetchService.prefetchLast(this@MainActivity) MangaPrefetchService.prefetchLast(this@MainActivity)
requestNotificationsPermission() requestNotificationsPermission()
} }
startService(Intent(this@MainActivity, LocalMangaIndex::class.java)) startService(Intent(this@MainActivity, LocalIndexUpdateService::class.java))
} }
} }

@ -10,6 +10,7 @@ import androidx.core.graphics.Insets
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentFactory
import androidx.fragment.app.FragmentTransaction import androidx.fragment.app.FragmentTransaction
import androidx.fragment.app.commit import androidx.fragment.app.commit
import androidx.preference.Preference import androidx.preference.Preference
@ -70,10 +71,12 @@ class SettingsActivity :
caller: PreferenceFragmentCompat, caller: PreferenceFragmentCompat,
pref: Preference, pref: Preference,
): Boolean { ): Boolean {
val fm = supportFragmentManager val fragmentName = pref.fragment ?: return false
val fragment = fm.fragmentFactory.instantiate(classLoader, pref.fragment ?: return false) openFragment(
fragment.arguments = pref.extras fragmentClass = FragmentFactory.loadFragmentClass(classLoader, fragmentName),
openFragment(fragment, isFromRoot = caller is RootSettingsFragment) args = pref.peekExtras(),
isFromRoot = caller is RootSettingsFragment,
)
return true return true
} }
@ -93,11 +96,11 @@ class SettingsActivity :
} ?: setTitle(title ?: getString(R.string.settings)) } ?: setTitle(title ?: getString(R.string.settings))
} }
fun openFragment(fragment: Fragment, isFromRoot: Boolean) { fun openFragment(fragmentClass: Class<out Fragment>, args: Bundle?, isFromRoot: Boolean) {
val hasFragment = supportFragmentManager.findFragmentById(R.id.container) != null val hasFragment = supportFragmentManager.findFragmentById(R.id.container) != null
supportFragmentManager.commit { supportFragmentManager.commit {
setReorderingAllowed(true) setReorderingAllowed(true)
replace(R.id.container, fragment) replace(R.id.container, fragmentClass, args)
setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
if (!isMasterDetails || (hasFragment && !isFromRoot)) { if (!isMasterDetails || (hasFragment && !isFromRoot)) {
addToBackStack(null) addToBackStack(null)

@ -18,10 +18,13 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.db.TABLE_SOURCES import org.koitharu.kotatsu.core.db.TABLE_SOURCES
import org.koitharu.kotatsu.core.model.getTitle import org.koitharu.kotatsu.core.model.getTitle
import org.koitharu.kotatsu.core.model.isNsfw import org.koitharu.kotatsu.core.model.isNsfw
import org.koitharu.kotatsu.core.model.unwrap
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.util.ext.lifecycleScope import org.koitharu.kotatsu.core.util.ext.lifecycleScope
import org.koitharu.kotatsu.explore.data.MangaSourcesRepository import org.koitharu.kotatsu.explore.data.MangaSourcesRepository
import org.koitharu.kotatsu.explore.data.SourcesSortOrder import org.koitharu.kotatsu.explore.data.SourcesSortOrder
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.util.mapToSet
import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem
import javax.inject.Inject import javax.inject.Inject
@ -63,8 +66,8 @@ class SourcesListProducer @Inject constructor(
} }
private suspend fun buildList(): List<SourceConfigItem> { private suspend fun buildList(): List<SourceConfigItem> {
val enabledSources = repository.getEnabledSources() val enabledSources = repository.getEnabledSources().filter { it.unwrap() is MangaParserSource }
val pinned = repository.getPinnedSources() val pinned = repository.getPinnedSources().mapToSet { it.name }
val isNsfwDisabled = settings.isNsfwContentDisabled val isNsfwDisabled = settings.isNsfwContentDisabled
val isReorderAvailable = settings.sourcesSortOrder == SourcesSortOrder.MANUAL val isReorderAvailable = settings.sourcesSortOrder == SourcesSortOrder.MANUAL
val withTip = isReorderAvailable && settings.isTipEnabled(TIP_REORDER) val withTip = isReorderAvailable && settings.isTipEnabled(TIP_REORDER)
@ -79,7 +82,7 @@ class SourcesListProducer @Inject constructor(
isEnabled = it in enabledSet, isEnabled = it in enabledSet,
isDraggable = false, isDraggable = false,
isAvailable = !isNsfwDisabled || !it.isNsfw(), isAvailable = !isNsfwDisabled || !it.isNsfw(),
isPinned = it in pinned, isPinned = it.name in pinned,
) )
}.ifEmpty { }.ifEmpty {
listOf(SourceConfigItem.EmptySearchResult) listOf(SourceConfigItem.EmptySearchResult)
@ -100,7 +103,7 @@ class SourcesListProducer @Inject constructor(
isEnabled = true, isEnabled = true,
isDraggable = isReorderAvailable, isDraggable = isReorderAvailable,
isAvailable = false, isAvailable = false,
isPinned = it in pinned, isPinned = it.name in pinned,
) )
} }
} }

@ -106,8 +106,11 @@ class SourcesManageFragment :
} }
override fun onItemSettingsClick(item: SourceConfigItem.SourceItem) { override fun onItemSettingsClick(item: SourceConfigItem.SourceItem) {
val fragment = SourceSettingsFragment.newInstance(item.source) (activity as? SettingsActivity)?.openFragment(
(activity as? SettingsActivity)?.openFragment(fragment, false) fragmentClass = SourceSettingsFragment::class.java,
args = Bundle(1).apply { putString(SourceSettingsFragment.EXTRA_SOURCE, item.source.name) },
isFromRoot = false,
)
} }
override fun onItemLiftClick(item: SourceConfigItem.SourceItem) { override fun onItemLiftClick(item: SourceConfigItem.SourceItem) {

Loading…
Cancel
Save