diff --git a/app/build.gradle b/app/build.gradle index 1d65bb8a5..96238c85b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -82,8 +82,7 @@ afterEvaluate { } } dependencies { - //noinspection GradleDependency - implementation('com.github.KotatsuApp:kotatsu-parsers:645006fde8') { + implementation('com.github.KotatsuApp:kotatsu-parsers:1.2.1') { exclude group: 'org.json', module: 'json' } diff --git a/app/src/debug/kotlin/org/koitharu/kotatsu/KotatsuApp.kt b/app/src/debug/kotlin/org/koitharu/kotatsu/KotatsuApp.kt index 7426b5575..1e9dd8a44 100644 --- a/app/src/debug/kotlin/org/koitharu/kotatsu/KotatsuApp.kt +++ b/app/src/debug/kotlin/org/koitharu/kotatsu/KotatsuApp.kt @@ -63,7 +63,6 @@ class KotatsuApp : BaseApp() { detectRetainInstanceUsage() detectSetUserVisibleHint() detectWrongNestedHierarchy() - detectTargetFragmentUsage() detectFragmentReuse() penaltyLog() if (notifier != null) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AutoFixService.kt b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AutoFixService.kt index 9fa0fdf42..aa6f7d436 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AutoFixService.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AutoFixService.kt @@ -165,13 +165,14 @@ class AutoFixService : CoroutineIntentService() { } else { error.getDisplayMessage(applicationContext.resources) }, - ) - .setSmallIcon(android.R.drawable.stat_notify_error) - .addAction( + ).setSmallIcon(android.R.drawable.stat_notify_error) + ErrorReporterReceiver.getPendingIntent(applicationContext, error)?.let { reportIntent -> + notification.addAction( R.drawable.ic_alert_outline, applicationContext.getString(R.string.report), - ErrorReporterReceiver.getPendingIntent(applicationContext, error), + reportIntent, ) + } } return notification.build() } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ErrorReporterReceiver.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ErrorReporterReceiver.kt index 7d190422e..628093428 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/ErrorReporterReceiver.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ErrorReporterReceiver.kt @@ -5,9 +5,11 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.net.Uri +import android.os.BadParcelableException import androidx.core.app.PendingIntentCompat import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.core.util.ext.getSerializableExtraCompat +import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.core.util.ext.report class ErrorReporterReceiver : BroadcastReceiver() { @@ -22,12 +24,15 @@ class ErrorReporterReceiver : BroadcastReceiver() { private const val EXTRA_ERROR = "err" 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) intent.setAction(ACTION_REPORT) intent.setData(Uri.parse("err://${e.hashCode()}")) 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 } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/resolve/ExceptionResolver.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/resolve/ExceptionResolver.kt index 44bd5d976..d843dd0d6 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/resolve/ExceptionResolver.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/resolve/ExceptionResolver.kt @@ -6,7 +6,6 @@ import androidx.activity.result.ActivityResultCaller import androidx.annotation.StringRes import androidx.collection.MutableScatterMap import androidx.fragment.app.FragmentManager -import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.assisted.Assisted import dagger.assisted.AssistedFactory 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.prefs.AppSettings 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.NotFoundException 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() return } - MaterialAlertDialogBuilder(ctx) - .setTitle(R.string.ignore_ssl_errors) - .setMessage(R.string.ignore_ssl_errors_summary) - .setPositiveButton(R.string.apply) { _, _ -> + buildAlertDialog(ctx) { + setTitle(R.string.ignore_ssl_errors) + setMessage(R.string.ignore_ssl_errors_summary) + setPositiveButton(R.string.apply) { _, _ -> settings.isSSLBypassEnabled = true - Toast.makeText(ctx, R.string.settings_apply_restart_required, Toast.LENGTH_SHORT).show() - ctx.findActivity()?.finishAffinity() - }.setNegativeButton(android.R.string.cancel, null) - .show() + Toast.makeText(ctx, R.string.settings_apply_restart_required, Toast.LENGTH_LONG).show() + ctx.restartApplication() + } + setNegativeButton(android.R.string.cancel, null) + }.show() } private inline fun Host.withContext(block: Context.() -> Unit) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/ReorderableListAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/ReorderableListAdapter.kt index 738309af5..97543d692 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/ReorderableListAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/ReorderableListAdapter.kt @@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.FlowCollector import kotlinx.coroutines.withContext import org.koitharu.kotatsu.list.ui.adapter.ListItemType import org.koitharu.kotatsu.list.ui.model.ListModel -import java.util.Collections +import org.koitharu.kotatsu.parsers.util.move import java.util.LinkedList open class ReorderableListAdapter : ListDelegationAdapter>(), FlowCollector?> { @@ -36,7 +36,9 @@ open class ReorderableListAdapter : ListDelegationAdapter override fun setItems(items: List?) = super.setItems(items) 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) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Android.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Android.kt index d86bcd5c2..7f1a649e0 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Android.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Android.kt @@ -7,10 +7,12 @@ import android.app.ActivityManager import android.app.ActivityManager.MemoryInfo import android.app.ActivityOptions import android.app.LocaleConfig +import android.content.ComponentName import android.content.Context import android.content.Context.ACTIVITY_SERVICE import android.content.Context.POWER_SERVICE import android.content.ContextWrapper +import android.content.Intent import android.content.OperationApplicationException import android.content.SharedPreferences import android.content.SyncResult @@ -33,6 +35,7 @@ import androidx.annotation.IntegerRes import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDialog +import androidx.core.app.ActivityCompat import androidx.core.app.ActivityOptionsCompat import androidx.core.app.NotificationManagerCompat import androidx.core.content.ContextCompat @@ -61,6 +64,7 @@ import okio.use import org.json.JSONException import org.jsoup.internal.StringUtil.StringJoiner import org.koitharu.kotatsu.BuildConfig +import org.koitharu.kotatsu.main.ui.MainActivity import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParserException @@ -274,3 +278,10 @@ fun WebView.configureForParser(userAgentOverride: String?) = with(settings) { userAgentString = userAgentOverride } } + +fun Context.restartApplication() { + val activity = findActivity() + val intent = Intent.makeRestartActivityTask(ComponentName(this, MainActivity::class.java)) + startActivity(intent) + activity?.finishAndRemoveTask() +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadNotificationFactory.kt b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadNotificationFactory.kt index 03f23e5cf..ece4835a5 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadNotificationFactory.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadNotificationFactory.kt @@ -213,13 +213,15 @@ class DownloadNotificationFactory @AssistedInject constructor( builder.setWhen(System.currentTimeMillis()) builder.setStyle(NotificationCompat.BigTextStyle().bigText(state.errorMessage)) if (state.error.isReportable()) { - builder.addAction( - NotificationCompat.Action( - 0, - context.getString(R.string.report), - ErrorReporterReceiver.getPendingIntent(context, state.error), - ), - ) + ErrorReporterReceiver.getPendingIntent(context, state.error)?.let { reportIntent -> + builder.addAction( + NotificationCompat.Action( + 0, + context.getString(R.string.report), + reportIntent, + ), + ) + } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/ImportService.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/ImportService.kt index f033847c2..8ff8ca534 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/ImportService.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/ImportService.kt @@ -139,11 +139,13 @@ class ImportService : CoroutineIntentService() { notification.setContentTitle(applicationContext.getString(R.string.error_occurred)) .setContentText(error.getDisplayMessage(applicationContext.resources)) .setSmallIcon(android.R.drawable.stat_notify_error) - .addAction( + ErrorReporterReceiver.getPendingIntent(applicationContext, error)?.let { reportIntent -> + notification.addAction( R.drawable.ic_alert_outline, applicationContext.getString(R.string.report), - ErrorReporterReceiver.getPendingIntent(applicationContext, error), + reportIntent, ) + } } return notification.build() } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/main/domain/CoverRestoreInterceptor.kt b/app/src/main/kotlin/org/koitharu/kotatsu/main/domain/CoverRestoreInterceptor.kt index fcf77379c..9f27b6147 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/main/domain/CoverRestoreInterceptor.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/main/domain/CoverRestoreInterceptor.kt @@ -10,9 +10,9 @@ import org.jsoup.HttpStatusException import org.koitharu.kotatsu.bookmarks.domain.Bookmark import org.koitharu.kotatsu.bookmarks.domain.BookmarksRepository 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.MangaRepository -import org.koitharu.kotatsu.core.parser.ParserMangaRepository import org.koitharu.kotatsu.core.util.ext.ifNullOrEmpty import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.parsers.exception.ParseException @@ -70,10 +70,10 @@ class CoverRestoreInterceptor @Inject constructor( } private suspend fun restoreMangaImpl(manga: Manga): Boolean { - if (dataRepository.findMangaById(manga.id) == null) { + if (dataRepository.findMangaById(manga.id) == null || manga.isLocal) { 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 return if (fixed != manga) { dataRepository.storeManga(fixed) @@ -100,7 +100,10 @@ class CoverRestoreInterceptor @Inject constructor( } 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 page = repo.getPages(chapter)[bookmark.page] val imageUrl = page.preview.ifNullOrEmpty { page.url } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/MainActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/MainActivity.kt index 384fbd453..3e567ede1 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/MainActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/MainActivity.kt @@ -55,7 +55,7 @@ import org.koitharu.kotatsu.details.service.MangaPrefetchService import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.favourites.ui.container.FavouritesContainerFragment 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.main.ui.owners.AppBarOwner import org.koitharu.kotatsu.main.ui.owners.BottomNavOwner @@ -352,7 +352,7 @@ class MainActivity : BaseActivity(), AppBarOwner, BottomNav MangaPrefetchService.prefetchLast(this@MainActivity) requestNotificationsPermission() } - startService(Intent(this@MainActivity, LocalMangaIndex::class.java)) + startService(Intent(this@MainActivity, LocalIndexUpdateService::class.java)) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt index 2f8d648d1..1112924e2 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt @@ -10,6 +10,7 @@ import androidx.core.graphics.Insets import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentFactory import androidx.fragment.app.FragmentTransaction import androidx.fragment.app.commit import androidx.preference.Preference @@ -70,10 +71,12 @@ class SettingsActivity : caller: PreferenceFragmentCompat, pref: Preference, ): Boolean { - val fm = supportFragmentManager - val fragment = fm.fragmentFactory.instantiate(classLoader, pref.fragment ?: return false) - fragment.arguments = pref.extras - openFragment(fragment, isFromRoot = caller is RootSettingsFragment) + val fragmentName = pref.fragment ?: return false + openFragment( + fragmentClass = FragmentFactory.loadFragmentClass(classLoader, fragmentName), + args = pref.peekExtras(), + isFromRoot = caller is RootSettingsFragment, + ) return true } @@ -93,11 +96,11 @@ class SettingsActivity : } ?: setTitle(title ?: getString(R.string.settings)) } - fun openFragment(fragment: Fragment, isFromRoot: Boolean) { + fun openFragment(fragmentClass: Class, args: Bundle?, isFromRoot: Boolean) { val hasFragment = supportFragmentManager.findFragmentById(R.id.container) != null supportFragmentManager.commit { setReorderingAllowed(true) - replace(R.id.container, fragment) + replace(R.id.container, fragmentClass, args) setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) if (!isMasterDetails || (hasFragment && !isFromRoot)) { addToBackStack(null) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesListProducer.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesListProducer.kt index 85b06587b..7df5129f6 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesListProducer.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesListProducer.kt @@ -18,10 +18,13 @@ import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.db.TABLE_SOURCES import org.koitharu.kotatsu.core.model.getTitle 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.util.ext.lifecycleScope import org.koitharu.kotatsu.explore.data.MangaSourcesRepository 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 javax.inject.Inject @@ -63,8 +66,8 @@ class SourcesListProducer @Inject constructor( } private suspend fun buildList(): List { - val enabledSources = repository.getEnabledSources() - val pinned = repository.getPinnedSources() + val enabledSources = repository.getEnabledSources().filter { it.unwrap() is MangaParserSource } + val pinned = repository.getPinnedSources().mapToSet { it.name } val isNsfwDisabled = settings.isNsfwContentDisabled val isReorderAvailable = settings.sourcesSortOrder == SourcesSortOrder.MANUAL val withTip = isReorderAvailable && settings.isTipEnabled(TIP_REORDER) @@ -79,7 +82,7 @@ class SourcesListProducer @Inject constructor( isEnabled = it in enabledSet, isDraggable = false, isAvailable = !isNsfwDisabled || !it.isNsfw(), - isPinned = it in pinned, + isPinned = it.name in pinned, ) }.ifEmpty { listOf(SourceConfigItem.EmptySearchResult) @@ -100,7 +103,7 @@ class SourcesListProducer @Inject constructor( isEnabled = true, isDraggable = isReorderAvailable, isAvailable = false, - isPinned = it in pinned, + isPinned = it.name in pinned, ) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesManageFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesManageFragment.kt index b970465cc..d0b19015b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesManageFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesManageFragment.kt @@ -106,8 +106,11 @@ class SourcesManageFragment : } override fun onItemSettingsClick(item: SourceConfigItem.SourceItem) { - val fragment = SourceSettingsFragment.newInstance(item.source) - (activity as? SettingsActivity)?.openFragment(fragment, false) + (activity as? SettingsActivity)?.openFragment( + fragmentClass = SourceSettingsFragment::class.java, + args = Bundle(1).apply { putString(SourceSettingsFragment.EXTRA_SOURCE, item.source.name) }, + isFromRoot = false, + ) } override fun onItemLiftClick(item: SourceConfigItem.SourceItem) {