diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativesActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativesActivity.kt index 22a918928..22f39d347 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativesActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativesActivity.kt @@ -1,7 +1,5 @@ package org.koitharu.kotatsu.alternatives.ui -import android.content.Context -import android.content.Intent import android.os.Bundle import android.view.View import android.widget.Toast @@ -13,8 +11,7 @@ import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver import org.koitharu.kotatsu.core.model.getTitle -import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga -import org.koitharu.kotatsu.core.parser.MangaIntent +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.ui.BaseListAdapter import org.koitharu.kotatsu.core.ui.dialog.buildAlertDialog @@ -22,7 +19,6 @@ import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.databinding.ActivityAlternativesBinding -import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.list.ui.adapter.ListItemType import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration import org.koitharu.kotatsu.list.ui.adapter.emptyStateListAD @@ -30,8 +26,6 @@ import org.koitharu.kotatsu.list.ui.adapter.loadingFooterAD import org.koitharu.kotatsu.list.ui.adapter.loadingStateAD import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaListFilter -import org.koitharu.kotatsu.search.ui.MangaListActivity import javax.inject.Inject @AndroidEntryPoint @@ -65,7 +59,7 @@ class AlternativesActivity : BaseActivity(), viewModel.content.observe(this, listAdapter) viewModel.onMigrated.observeEvent(this) { Toast.makeText(this, R.string.migration_completed, Toast.LENGTH_SHORT).show() - startActivity(DetailsActivity.newIntent(this, it)) + router.openDetails(it) finishAfterTransition() } } @@ -82,16 +76,9 @@ class AlternativesActivity : BaseActivity(), override fun onItemClick(item: MangaAlternativeModel, view: View) { when (view.id) { - R.id.chip_source -> startActivity( - MangaListActivity.newIntent( - this, - item.manga.source, - MangaListFilter(query = viewModel.manga.title), - ), - ) - + R.id.chip_source -> router.openSearch(item.manga.source, viewModel.manga.title) R.id.button_migrate -> confirmMigration(item.manga) - else -> startActivity(DetailsActivity.newIntent(this, item.manga)) + else -> router.openDetails(item.manga) } } @@ -114,10 +101,4 @@ class AlternativesActivity : BaseActivity(), } }.show() } - - companion object { - - fun newIntent(context: Context, manga: Manga) = Intent(context, AlternativesActivity::class.java) - .putExtra(MangaIntent.KEY_MANGA, ParcelableManga(manga)) - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativesViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativesViewModel.kt index 918227088..42ec2ac97 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativesViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativesViewModel.kt @@ -13,7 +13,7 @@ import org.koitharu.kotatsu.alternatives.domain.AlternativesUseCase import org.koitharu.kotatsu.alternatives.domain.MigrateUseCase import org.koitharu.kotatsu.core.model.chaptersCount import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga -import org.koitharu.kotatsu.core.parser.MangaIntent +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.BaseViewModel @@ -40,7 +40,7 @@ class AlternativesViewModel @Inject constructor( private val settings: AppSettings, ) : BaseViewModel() { - val manga = savedStateHandle.require(MangaIntent.KEY_MANGA).manga + val manga = savedStateHandle.require(AppRouter.KEY_MANGA).manga val onMigrated = MutableEventFlow() val content = MutableStateFlow>(listOf(LoadingState)) 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 3e7f8b49e..e286d641e 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 @@ -10,7 +10,6 @@ import androidx.core.app.NotificationChannelCompat import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.core.app.PendingIntentCompat -import androidx.core.app.ServiceCompat import androidx.core.content.ContextCompat import coil3.ImageLoader import coil3.request.ImageRequest @@ -20,13 +19,13 @@ import org.koitharu.kotatsu.R import org.koitharu.kotatsu.alternatives.domain.AutoFixUseCase import org.koitharu.kotatsu.core.ErrorReporterReceiver import org.koitharu.kotatsu.core.model.getTitle +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.ui.CoroutineIntentService import org.koitharu.kotatsu.core.util.ext.checkNotificationPermission import org.koitharu.kotatsu.core.util.ext.getDisplayMessage import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.core.util.ext.toBitmapOrNull -import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import javax.inject.Inject @@ -122,7 +121,7 @@ class AutoFixService : CoroutineIntentService() { ).toBitmapOrNull(), ) notification.setSubText(replacement.title) - val intent = DetailsActivity.newIntent(applicationContext, replacement) + val intent = AppRouter.detailsIntent(applicationContext, replacement) notification.setContentIntent( PendingIntentCompat.getActivity( applicationContext, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/AllBookmarksActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/AllBookmarksActivity.kt index 85591a372..7aad6f547 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/AllBookmarksActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/AllBookmarksActivity.kt @@ -1,7 +1,5 @@ package org.koitharu.kotatsu.bookmarks.ui -import android.content.Context -import android.content.Intent import android.os.Bundle import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.graphics.Insets @@ -46,9 +44,4 @@ class AllBookmarksActivity : right = insets.right, ) } - - companion object { - - fun newIntent(context: Context) = Intent(context, AllBookmarksActivity::class.java) - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/AllBookmarksFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/AllBookmarksFragment.kt index 5670eb82a..3bea2b4d3 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/AllBookmarksFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/AllBookmarksFragment.kt @@ -20,6 +20,8 @@ import org.koitharu.kotatsu.R import org.koitharu.kotatsu.bookmarks.domain.Bookmark import org.koitharu.kotatsu.bookmarks.ui.adapter.BookmarksAdapter import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver +import org.koitharu.kotatsu.core.nav.ReaderIntent +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.BaseFragment import org.koitharu.kotatsu.core.ui.list.ListSelectionController @@ -30,7 +32,6 @@ import org.koitharu.kotatsu.core.util.ext.findAppCompatDelegate import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.databinding.FragmentListSimpleBinding -import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.list.ui.GridSpanResolver import org.koitharu.kotatsu.list.ui.adapter.ListHeaderClickListener import org.koitharu.kotatsu.list.ui.adapter.ListItemType @@ -39,7 +40,6 @@ import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration import org.koitharu.kotatsu.list.ui.model.ListHeader import org.koitharu.kotatsu.main.ui.owners.AppBarOwner import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.reader.ui.ReaderActivity import javax.inject.Inject @AndroidEntryPoint @@ -115,26 +115,26 @@ class AllBookmarksFragment : override fun onItemClick(item: Bookmark, view: View) { if (selectionController?.onItemClick(item.pageId) != true) { - val intent = ReaderActivity.IntentBuilder(view.context) + val intent = ReaderIntent.Builder(view.context) .bookmark(item) .incognito(true) .build() - startActivity(intent) + router.openReader(intent) Toast.makeText(view.context, R.string.incognito_mode, Toast.LENGTH_SHORT).show() } } override fun onListHeaderClick(item: ListHeader, view: View) { val manga = item.payload as? Manga ?: return - startActivity(DetailsActivity.newIntent(view.context, manga)) + router.openDetails(manga) } override fun onItemLongClick(item: Bookmark, view: View): Boolean { - return selectionController?.onItemLongClick(view, item.pageId) ?: false + return selectionController?.onItemLongClick(view, item.pageId) == true } override fun onItemContextClick(item: Bookmark, view: View): Boolean { - return selectionController?.onItemContextClick(view, item.pageId) ?: false + return selectionController?.onItemContextClick(view, item.pageId) == true } override fun onRetryClick(error: Throwable) = Unit @@ -208,16 +208,4 @@ class AllBookmarksFragment : invalidateSpanIndexCache() } } - - companion object { - - @Deprecated( - "", - ReplaceWith( - "BookmarksFragment()", - "org.koitharu.kotatsu.bookmarks.ui.BookmarksFragment", - ), - ) - fun newInstance() = AllBookmarksFragment() - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/browser/BrowserActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/browser/BrowserActivity.kt index 159550134..2e5ad85db 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/browser/BrowserActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/browser/BrowserActivity.kt @@ -1,9 +1,5 @@ package org.koitharu.kotatsu.browser -import android.content.ActivityNotFoundException -import android.content.Context -import android.content.Intent -import android.net.Uri import android.os.Bundle import android.view.Menu import android.view.MenuItem @@ -11,17 +7,18 @@ import android.webkit.CookieManager import androidx.core.graphics.Insets import androidx.core.view.isVisible import androidx.core.view.updatePadding +import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.MangaSource +import org.koitharu.kotatsu.core.nav.AppRouter +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.network.CommonHeaders import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.ParserMangaRepository import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.util.ext.configureForParser -import org.koitharu.kotatsu.core.util.ext.toUriOrNull import org.koitharu.kotatsu.databinding.ActivityBrowserBinding -import org.koitharu.kotatsu.parsers.model.MangaSource import javax.inject.Inject import com.google.android.material.R as materialR @@ -42,7 +39,7 @@ class BrowserActivity : BaseActivity(), BrowserCallback setDisplayHomeAsUpEnabled(true) setHomeAsUpIndicator(materialR.drawable.abc_ic_clear_material) } - val mangaSource = MangaSource(intent?.getStringExtra(EXTRA_SOURCE)) + val mangaSource = MangaSource(intent?.getStringExtra(AppRouter.KEY_SOURCE)) val repository = mangaRepositoryFactory.create(mangaSource) as? ParserMangaRepository val userAgent = repository?.getRequestHeaders()?.get(CommonHeaders.USER_AGENT) viewBinding.webView.configureForParser(userAgent) @@ -59,7 +56,7 @@ class BrowserActivity : BaseActivity(), BrowserCallback finishAfterTransition() } else { onTitleChanged( - intent?.getStringExtra(EXTRA_TITLE) ?: getString(R.string.loading_), + intent?.getStringExtra(AppRouter.KEY_TITLE) ?: getString(R.string.loading_), url, ) viewBinding.webView.loadUrl(url) @@ -80,14 +77,8 @@ class BrowserActivity : BaseActivity(), BrowserCallback } R.id.action_browser -> { - val url = viewBinding.webView.url?.toUriOrNull() - if (url != null) { - val intent = Intent(Intent.ACTION_VIEW) - intent.data = url - try { - startActivity(Intent.createChooser(intent, item.title)) - } catch (_: ActivityNotFoundException) { - } + if (!router.openExternalBrowser(viewBinding.webView.url.orEmpty(), item.title)) { + Snackbar.make(viewBinding.webView, R.string.operation_not_supported, Snackbar.LENGTH_SHORT).show() } true } @@ -136,17 +127,4 @@ class BrowserActivity : BaseActivity(), BrowserCallback bottom = insets.bottom, ) } - - companion object { - - private const val EXTRA_TITLE = "title" - private const val EXTRA_SOURCE = "source" - - fun newIntent(context: Context, url: String, source: MangaSource?, title: String?): Intent { - return Intent(context, BrowserActivity::class.java) - .setData(Uri.parse(url)) - .putExtra(EXTRA_TITLE, title) - .putExtra(EXTRA_SOURCE, source?.name) - } - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/browser/cloudflare/CaptchaNotifier.kt b/app/src/main/kotlin/org/koitharu/kotatsu/browser/cloudflare/CaptchaNotifier.kt index 48d900c10..c889e11dd 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/browser/cloudflare/CaptchaNotifier.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/browser/cloudflare/CaptchaNotifier.kt @@ -17,6 +17,7 @@ import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException import org.koitharu.kotatsu.core.model.getTitle import org.koitharu.kotatsu.core.model.isNsfw +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.util.ext.checkNotificationPermission import org.koitharu.kotatsu.parsers.model.MangaSource @@ -38,7 +39,7 @@ class CaptchaNotifier( .build() manager.createNotificationChannel(channel) - val intent = CloudFlareActivity.newIntent(context, exception) + val intent = AppRouter.cloudFlareResolveIntent(context, exception) .setData(exception.url.toUri()) val notification = NotificationCompat.Builder(context, CHANNEL_ID) .setContentTitle(channel.name) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/browser/cloudflare/CloudFlareActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/browser/cloudflare/CloudFlareActivity.kt index 09738c113..4d768100e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/browser/cloudflare/CloudFlareActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/browser/cloudflare/CloudFlareActivity.kt @@ -1,6 +1,5 @@ package org.koitharu.kotatsu.browser.cloudflare -import android.app.Activity import android.content.Context import android.content.Intent import android.os.Bundle @@ -9,7 +8,6 @@ import android.view.MenuItem import android.webkit.CookieManager import androidx.activity.result.contract.ActivityResultContract import androidx.core.graphics.Insets -import androidx.core.net.toUri import androidx.core.view.isInvisible import androidx.core.view.isVisible import androidx.core.view.updatePadding @@ -19,19 +17,17 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.runInterruptible import kotlinx.coroutines.yield -import okhttp3.Headers import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import org.koitharu.kotatsu.R import org.koitharu.kotatsu.browser.WebViewBackPressedCallback import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException import org.koitharu.kotatsu.core.model.MangaSource -import org.koitharu.kotatsu.core.network.CommonHeaders +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.util.ext.configureForParser import org.koitharu.kotatsu.databinding.ActivityBrowserBinding -import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.network.CloudFlareHelper import javax.inject.Inject import com.google.android.material.R as materialR @@ -62,7 +58,7 @@ class CloudFlareActivity : BaseActivity(), CloudFlareCal return } cfClient = CloudFlareClient(cookieJar, this, url) - viewBinding.webView.configureForParser(intent?.getStringExtra(ARG_UA)) + viewBinding.webView.configureForParser(intent?.getStringExtra(AppRouter.KEY_USER_AGENT)) viewBinding.webView.webViewClient = cfClient onBackPressedCallback = WebViewBackPressedCallback(viewBinding.webView).also { onBackPressedDispatcher.addCallback(it) @@ -140,7 +136,7 @@ class CloudFlareActivity : BaseActivity(), CloudFlareCal override fun onCheckPassed() { pendingResult = RESULT_OK - val source = intent?.getStringExtra(ARG_SOURCE) + val source = intent?.getStringExtra(AppRouter.KEY_SOURCE) if (source != null) { CaptchaNotifier(this).dismiss(MangaSource(source)) } @@ -182,38 +178,16 @@ class CloudFlareActivity : BaseActivity(), CloudFlareCal class Contract : ActivityResultContract() { override fun createIntent(context: Context, input: CloudFlareProtectedException): Intent { - return newIntent(context, input) + return AppRouter.cloudFlareResolveIntent(context, input) } override fun parseResult(resultCode: Int, intent: Intent?): Boolean { - return resultCode == Activity.RESULT_OK + return resultCode == RESULT_OK } } companion object { const val TAG = "CloudFlareActivity" - private const val ARG_UA = "ua" - private const val ARG_SOURCE = "_source" - - fun newIntent(context: Context, exception: CloudFlareProtectedException) = newIntent( - context = context, - url = exception.url, - source = exception.source, - headers = exception.headers, - ) - - private fun newIntent( - context: Context, - url: String, - source: MangaSource?, - headers: Headers?, - ) = Intent(context, CloudFlareActivity::class.java).apply { - data = url.toUri() - putExtra(ARG_SOURCE, source?.name) - headers?.get(CommonHeaders.USER_AGENT)?.let { - putExtra(ARG_UA, it) - } - } } } 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 628093428..27e42a2a5 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/ErrorReporterReceiver.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ErrorReporterReceiver.kt @@ -8,6 +8,7 @@ import android.net.Uri import android.os.BadParcelableException import androidx.core.app.PendingIntentCompat import org.koitharu.kotatsu.BuildConfig +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.util.ext.getSerializableExtraCompat import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.core.util.ext.report @@ -15,20 +16,19 @@ import org.koitharu.kotatsu.core.util.ext.report class ErrorReporterReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { - val e = intent?.getSerializableExtraCompat(EXTRA_ERROR) ?: return + val e = intent?.getSerializableExtraCompat(AppRouter.KEY_ERROR) ?: return e.report() } companion object { - private const val EXTRA_ERROR = "err" private const val ACTION_REPORT = "${BuildConfig.APPLICATION_ID}.action.REPORT_ERROR" 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) + intent.putExtra(AppRouter.KEY_ERROR, e) PendingIntentCompat.getBroadcast(context, 0, intent, 0, false) } catch (e: BadParcelableException) { e.printStackTraceDebug() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/backup/TelegramBackupUploader.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/backup/TelegramBackupUploader.kt index 94fe19ee0..7dd8706d0 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/backup/TelegramBackupUploader.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/backup/TelegramBackupUploader.kt @@ -1,11 +1,7 @@ package org.koitharu.kotatsu.core.backup -import android.annotation.SuppressLint import android.content.Context -import android.content.Intent -import android.widget.Toast -import androidx.annotation.UiContext -import androidx.core.net.toUri +import androidx.annotation.CheckResult import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -17,6 +13,7 @@ import okhttp3.RequestBody.Companion.asRequestBody import okhttp3.Response import okhttp3.internal.closeQuietly import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.network.BaseHttpClient import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.parsers.util.await @@ -56,16 +53,11 @@ class TelegramBackupUploader @Inject constructor( sendMessage(context.getString(R.string.backup_tg_echo)) } - @SuppressLint("UnsafeImplicitIntentLaunch") - fun openBotInApp(@UiContext context: Context): Boolean { + @CheckResult + fun openBotInApp(router: AppRouter): Boolean { val botUsername = context.getString(R.string.tg_backup_bot_name) - return runCatching { - context.startActivity(Intent(Intent.ACTION_VIEW, "tg://resolve?domain=$botUsername".toUri())) - }.recoverCatching { - context.startActivity(Intent(Intent.ACTION_VIEW, "https://t.me/$botUsername".toUri())) - }.onFailure { - Toast.makeText(context, R.string.operation_not_supported, Toast.LENGTH_SHORT).show() - }.isSuccess + return router.openExternalBrowser("tg://resolve?domain=$botUsername") || + router.openExternalBrowser("https://t.me/$botUsername") } private suspend fun sendMessage(message: String) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/resolve/DialogErrorObserver.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/resolve/DialogErrorObserver.kt index 8d2e34b9f..27757b557 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/resolve/DialogErrorObserver.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/resolve/DialogErrorObserver.kt @@ -6,7 +6,6 @@ import androidx.core.util.Consumer import androidx.fragment.app.Fragment import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.core.ui.dialog.ErrorDetailsDialog import org.koitharu.kotatsu.core.util.ext.getDisplayMessage import org.koitharu.kotatsu.core.util.ext.isSerializable import org.koitharu.kotatsu.parsers.exception.ParseException @@ -32,10 +31,10 @@ class DialogErrorObserver( if (canResolve(value)) { dialogBuilder.setPositiveButton(ExceptionResolver.getResolveStringId(value), listener) } else if (value is ParseException) { - val fm = fragmentManager - if (fm != null && value.isSerializable()) { + val router = router() + if (router != null && value.isSerializable()) { dialogBuilder.setPositiveButton(R.string.details) { _, _ -> - ErrorDetailsDialog.show(fm, value, value.url) + router.showErrorDialog(value) } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/resolve/ErrorObserver.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/resolve/ErrorObserver.kt index 097c562a5..8b91c0efa 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/resolve/ErrorObserver.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/resolve/ErrorObserver.kt @@ -4,6 +4,7 @@ import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.core.util.Consumer import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentManager import androidx.lifecycle.LifecycleCoroutineScope import androidx.lifecycle.LifecycleOwner @@ -11,6 +12,7 @@ import androidx.lifecycle.coroutineScope import kotlinx.coroutines.flow.FlowCollector import kotlinx.coroutines.isActive import kotlinx.coroutines.launch +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.util.ext.findActivity import org.koitharu.kotatsu.core.util.ext.viewLifecycleScope @@ -33,6 +35,8 @@ abstract class ErrorObserver( return resolver != null && ExceptionResolver.canResolve(error) } + protected fun router() = fragment?.router ?: (activity as? FragmentActivity)?.router + private fun isAlive(): Boolean { return when { fragment != null -> fragment.view != null @@ -44,7 +48,7 @@ abstract class ErrorObserver( protected fun resolve(error: Throwable) { if (isAlive()) { lifecycleScope.launch { - val isResolved = resolver?.resolve(error) ?: false + val isResolved = resolver?.resolve(error) == true if (isActive) { onResolved?.accept(isResolved) } 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 d843dd0d6..6031e255f 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 @@ -5,19 +5,20 @@ import android.widget.Toast import androidx.activity.result.ActivityResultCaller import androidx.annotation.StringRes import androidx.collection.MutableScatterMap +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentManager import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.alternatives.ui.AlternativesActivity -import org.koitharu.kotatsu.browser.BrowserActivity import org.koitharu.kotatsu.browser.cloudflare.CloudFlareActivity import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException import org.koitharu.kotatsu.core.exceptions.ProxyConfigException import org.koitharu.kotatsu.core.exceptions.UnsupportedSourceException +import org.koitharu.kotatsu.core.nav.AppRouter +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.prefs.AppSettings -import org.koitharu.kotatsu.core.ui.dialog.ErrorDetailsDialog import org.koitharu.kotatsu.core.ui.dialog.buildAlertDialog import org.koitharu.kotatsu.core.util.ext.restartApplication import org.koitharu.kotatsu.parsers.exception.AuthRequiredException @@ -26,7 +27,6 @@ import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.scrobbling.common.domain.ScrobblerAuthRequiredException import org.koitharu.kotatsu.scrobbling.common.ui.ScrobblerAuthHelper -import org.koitharu.kotatsu.settings.SettingsActivity import org.koitharu.kotatsu.settings.sources.auth.SourceAuthActivity import java.security.cert.CertPathValidatorException import javax.inject.Provider @@ -49,8 +49,8 @@ class ExceptionResolver @AssistedInject constructor( handleActivityResult(CloudFlareActivity.TAG, it) } - fun showDetails(e: Throwable, url: String?) { - ErrorDetailsDialog.show(host.getChildFragmentManager(), e, url) + fun showErrorDetails(e: Throwable, url: String? = null) { + host.router()?.showErrorDialog(e, url) } suspend fun resolve(e: Throwable): Boolean = when (e) { @@ -63,9 +63,7 @@ class ExceptionResolver @AssistedInject constructor( } is ProxyConfigException -> { - host.withContext { - startActivity(SettingsActivity.newProxySettingsIntent(this)) - } + host.router()?.openProxySettings() false } @@ -85,9 +83,7 @@ class ExceptionResolver @AssistedInject constructor( true } else { host.withContext { - authHelper.startAuth(this, e.scrobbler).onFailure { - showDetails(it, null) - } + authHelper.startAuth(this, e.scrobbler).onFailure(::showErrorDetails) } false } @@ -106,12 +102,12 @@ class ExceptionResolver @AssistedInject constructor( sourceAuthContract.launch(source) } - private fun openInBrowser(url: String) = host.withContext { - startActivity(BrowserActivity.newIntent(this, url, null, null)) + private fun openInBrowser(url: String) { + host.router()?.openBrowser(url, null, null) } - private fun openAlternatives(manga: Manga) = host.withContext { - startActivity(AlternativesActivity.newIntent(this, manga)) + private fun openAlternatives(manga: Manga) { + host.router()?.openAlternatives(manga) } private fun handleActivityResult(tag: String, result: Boolean) { @@ -140,6 +136,12 @@ class ExceptionResolver @AssistedInject constructor( getContext()?.apply(block) } + private fun Host.router(): AppRouter? = when (this) { + is FragmentActivity -> router + is Fragment -> router + else -> null + } + interface Host : ActivityResultCaller { fun getChildFragmentManager(): FragmentManager diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/resolve/SnackbarErrorObserver.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/resolve/SnackbarErrorObserver.kt index d5b55b750..bec64d18c 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/resolve/SnackbarErrorObserver.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/resolve/SnackbarErrorObserver.kt @@ -5,7 +5,6 @@ import androidx.core.util.Consumer import androidx.fragment.app.Fragment import com.google.android.material.snackbar.Snackbar import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.core.ui.dialog.ErrorDetailsDialog import org.koitharu.kotatsu.core.util.ext.getDisplayMessage import org.koitharu.kotatsu.core.util.ext.isSerializable import org.koitharu.kotatsu.main.ui.owners.BottomNavOwner @@ -33,10 +32,10 @@ class SnackbarErrorObserver( resolve(value) } } else if (value is ParseException) { - val fm = fragmentManager - if (fm != null && value.isSerializable()) { + val router = router() + if (router != null && value.isSerializable()) { snackbar.setAction(R.string.details) { - ErrorDetailsDialog.show(fm, value, value.url) + router.showErrorDialog(value) } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/nav/AppRouter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/nav/AppRouter.kt new file mode 100644 index 000000000..fd7a6d72e --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/nav/AppRouter.kt @@ -0,0 +1,602 @@ +package org.koitharu.kotatsu.core.nav + +import android.accounts.Account +import android.app.Activity +import android.content.ActivityNotFoundException +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.provider.Settings +import android.view.View +import androidx.annotation.CheckResult +import androidx.core.net.toUri +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.findFragment +import androidx.lifecycle.LifecycleOwner +import org.koitharu.kotatsu.BuildConfig +import org.koitharu.kotatsu.alternatives.ui.AlternativesActivity +import org.koitharu.kotatsu.bookmarks.ui.AllBookmarksActivity +import org.koitharu.kotatsu.browser.BrowserActivity +import org.koitharu.kotatsu.browser.cloudflare.CloudFlareActivity +import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException +import org.koitharu.kotatsu.core.model.FavouriteCategory +import org.koitharu.kotatsu.core.model.MangaSourceInfo +import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga +import org.koitharu.kotatsu.core.model.parcelable.ParcelableMangaListFilter +import org.koitharu.kotatsu.core.model.parcelable.ParcelableMangaPage +import org.koitharu.kotatsu.core.network.CommonHeaders +import org.koitharu.kotatsu.core.parser.external.ExternalMangaSource +import org.koitharu.kotatsu.core.prefs.ReaderMode +import org.koitharu.kotatsu.core.ui.dialog.ErrorDetailsDialog +import org.koitharu.kotatsu.core.util.ext.findActivity +import org.koitharu.kotatsu.core.util.ext.mapToArray +import org.koitharu.kotatsu.core.util.ext.toUriOrNull +import org.koitharu.kotatsu.core.util.ext.withArgs +import org.koitharu.kotatsu.details.ui.DetailsActivity +import org.koitharu.kotatsu.details.ui.pager.ChaptersPagesSheet +import org.koitharu.kotatsu.details.ui.related.RelatedMangaActivity +import org.koitharu.kotatsu.details.ui.scrobbling.ScrobblingInfoSheet +import org.koitharu.kotatsu.download.ui.dialog.DownloadDialogFragment +import org.koitharu.kotatsu.download.ui.list.DownloadsActivity +import org.koitharu.kotatsu.favourites.ui.FavouritesActivity +import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity +import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity +import org.koitharu.kotatsu.favourites.ui.categories.select.FavoriteDialog +import org.koitharu.kotatsu.filter.ui.FilterCoordinator +import org.koitharu.kotatsu.filter.ui.sheet.FilterSheetFragment +import org.koitharu.kotatsu.filter.ui.tags.TagsCatalogSheet +import org.koitharu.kotatsu.history.ui.HistoryActivity +import org.koitharu.kotatsu.image.ui.ImageActivity +import org.koitharu.kotatsu.list.ui.config.ListConfigBottomSheet +import org.koitharu.kotatsu.list.ui.config.ListConfigSection +import org.koitharu.kotatsu.local.ui.ImportDialogFragment +import org.koitharu.kotatsu.local.ui.info.LocalInfoDialog +import org.koitharu.kotatsu.main.ui.welcome.WelcomeSheet +import org.koitharu.kotatsu.parsers.model.Manga +import org.koitharu.kotatsu.parsers.model.MangaListFilter +import org.koitharu.kotatsu.parsers.model.MangaPage +import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.model.MangaTag +import org.koitharu.kotatsu.parsers.util.isNullOrEmpty +import org.koitharu.kotatsu.reader.ui.colorfilter.ColorFilterConfigActivity +import org.koitharu.kotatsu.reader.ui.config.ReaderConfigSheet +import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService +import org.koitharu.kotatsu.scrobbling.common.ui.config.ScrobblerConfigActivity +import org.koitharu.kotatsu.scrobbling.common.ui.selector.ScrobblingSelectorSheet +import org.koitharu.kotatsu.search.ui.MangaListActivity +import org.koitharu.kotatsu.search.ui.multi.SearchActivity +import org.koitharu.kotatsu.settings.SettingsActivity +import org.koitharu.kotatsu.settings.about.AppUpdateActivity +import org.koitharu.kotatsu.settings.backup.BackupDialogFragment +import org.koitharu.kotatsu.settings.backup.RestoreDialogFragment +import org.koitharu.kotatsu.settings.reader.ReaderTapGridConfigActivity +import org.koitharu.kotatsu.settings.sources.auth.SourceAuthActivity +import org.koitharu.kotatsu.settings.sources.catalog.SourcesCatalogActivity +import org.koitharu.kotatsu.settings.storage.MangaDirectorySelectDialog +import org.koitharu.kotatsu.settings.storage.directories.MangaDirectoriesActivity +import org.koitharu.kotatsu.settings.tracker.categories.TrackerCategoriesConfigSheet +import org.koitharu.kotatsu.stats.ui.StatsActivity +import org.koitharu.kotatsu.stats.ui.sheet.MangaStatsSheet +import org.koitharu.kotatsu.suggestions.ui.SuggestionsActivity +import org.koitharu.kotatsu.tracker.ui.updates.UpdatesActivity + +class AppRouter private constructor( + private val activity: FragmentActivity?, + private val fragment: Fragment?, +) { + + constructor(activity: FragmentActivity) : this(activity, null) + + constructor(fragment: Fragment) : this(null, fragment) + + /** Activities **/ + + fun openList(source: MangaSource, filter: MangaListFilter?) { + startActivity(listIntent(contextOrNull() ?: return, source, filter)) + } + + fun openList(tag: MangaTag) = openList(tag.source, MangaListFilter(tags = setOf(tag))) + + fun openSearch(query: String) { + startActivity( + Intent(contextOrNull() ?: return, SearchActivity::class.java) + .putExtra(KEY_QUERY, query), + ) + } + + fun openSearch(source: MangaSource, query: String) = openList(source, MangaListFilter(query = query)) + + fun openDetails(manga: Manga) { + startActivity(detailsIntent(contextOrNull() ?: return, manga)) + } + + fun openDetails(mangaId: Long) { + startActivity(detailsIntent(contextOrNull() ?: return, mangaId)) + } + + fun openReader(manga: Manga, anchor: View? = null) { + openReader( + ReaderIntent.Builder(contextOrNull() ?: return) + .manga(manga) + .build(), + anchor, + ) + } + + fun openReader(intent: ReaderIntent, anchor: View? = null) { + startActivity(intent.intent, anchor?.let { view -> scaleUpActivityOptionsOf(view) }) + } + + fun openAlternatives(manga: Manga) { + startActivity( + Intent(contextOrNull() ?: return, AlternativesActivity::class.java) + .putExtra(KEY_MANGA, ParcelableManga(manga)), + ) + } + + fun openRelated(manga: Manga) { + startActivity( + Intent(contextOrNull(), RelatedMangaActivity::class.java) + .putExtra(KEY_MANGA, ParcelableManga(manga)), + ) + } + + fun openImage(url: String, source: MangaSource?, anchor: View? = null) { + startActivity( + Intent(contextOrNull(), ImageActivity::class.java) + .setData(url.toUri()) + .putExtra(KEY_SOURCE, source?.name), + anchor?.let { scaleUpActivityOptionsOf(it) }, + ) + } + + fun openBookmarks() = startActivity(AllBookmarksActivity::class.java) + + fun openAppUpdate() = startActivity(AppUpdateActivity::class.java) + + fun openSuggestions() { + startActivity(suggestionsIntent(contextOrNull() ?: return)) + } + + fun openSourcesCatalog() = startActivity(SourcesCatalogActivity::class.java) + + fun openDownloads() = startActivity(DownloadsActivity::class.java) + + fun openDirectoriesSettings() = startActivity(MangaDirectoriesActivity::class.java) + + fun openBrowser(url: String, source: MangaSource?, title: String?) { + startActivity( + Intent(contextOrNull() ?: return, BrowserActivity::class.java) + .setData(url.toUri()) + .putExtra(KEY_TITLE, title) + .putExtra(KEY_SOURCE, source?.name), + ) + } + + fun openColorFilterConfig(manga: Manga, page: MangaPage) { + startActivity( + Intent(contextOrNull(), ColorFilterConfigActivity::class.java) + .putExtra(KEY_MANGA, ParcelableManga(manga)) + .putExtra(KEY_PAGES, ParcelableMangaPage(page)), + ) + } + + fun openHistory() = startActivity(HistoryActivity::class.java) + + fun openFavorites() = startActivity(FavouritesActivity::class.java) + + fun openFavorites(category: FavouriteCategory) { + startActivity( + Intent(contextOrNull() ?: return, FavouritesActivity::class.java) + .putExtra(KEY_ID, category.id) + .putExtra(KEY_TITLE, category.title), + ) + } + + fun openFavoriteCategories() = startActivity(FavouriteCategoriesActivity::class.java) + + fun openFavoriteCategoryEdit(categoryId: Long) { + startActivity( + Intent(contextOrNull() ?: return, FavouritesCategoryEditActivity::class.java) + .putExtra(KEY_ID, categoryId), + ) + } + + fun openFavoriteCategoryCreate() = openFavoriteCategoryEdit(FavouritesCategoryEditActivity.NO_ID) + + fun openMangaUpdates() { + startActivity(mangaUpdatesIntent(contextOrNull() ?: return)) + } + + fun openSettings() = startActivity(SettingsActivity::class.java) + + fun openReaderSettings() { + startActivity(readerSettingsIntent(contextOrNull() ?: return)) + } + + fun openProxySettings() { + startActivity(proxySettingsIntent(contextOrNull() ?: return)) + } + + fun openDownloadsSetting() { + startActivity(downloadsSettingsIntent(contextOrNull() ?: return)) + } + + fun openSourceSettings(source: MangaSource) { + startActivity(sourceSettingsIntent(contextOrNull() ?: return, source)) + } + + fun openSuggestionsSettings() { + startActivity(suggestionsSettingsIntent(contextOrNull() ?: return)) + } + + fun openSourcesSettings() { + startActivity(sourcesSettingsIntent(contextOrNull() ?: return)) + } + + fun openReaderTapGridSettings() = startActivity(ReaderTapGridConfigActivity::class.java) + + fun openScrobblerSettings(scrobbler: ScrobblerService) { + startActivity( + Intent(contextOrNull() ?: return, ScrobblerConfigActivity::class.java) + .putExtra(KEY_ID, scrobbler.id), + ) + } + + fun openSourceAuth(source: MangaSource) { + startActivity(sourceAuthIntent(contextOrNull() ?: return, source)) + } + + fun openManageSources() { + startActivity( + manageSourcesIntent(contextOrNull() ?: return), + ) + } + + fun openStatistic() = startActivity(StatsActivity::class.java) + + @CheckResult + fun openExternalBrowser(url: String, chooserTitle: CharSequence? = null): Boolean { + val intent = Intent(Intent.ACTION_VIEW) + intent.data = url.toUriOrNull() ?: return false + return startActivitySafe( + if (!chooserTitle.isNullOrEmpty()) { + Intent.createChooser(intent, chooserTitle) + } else { + intent + }, + ) + } + + @CheckResult + fun openSystemSyncSettings(account: Account): Boolean { + val args = Bundle(1) + args.putParcelable(ACCOUNT_KEY, account) + val intent = Intent(ACTION_ACCOUNT_SYNC_SETTINGS) + intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args) + return startActivitySafe(intent) + } + + /** Dialogs **/ + + fun showDownloadDialog(manga: Manga, snackbarHost: View?) = showDownloadDialog(setOf(manga), snackbarHost) + + fun showDownloadDialog(manga: Collection, snackbarHost: View?) { + if (manga.isEmpty()) { + return + } + val fm = getFragmentManager() ?: return + if (snackbarHost != null) { + getLifecycleOwner()?.let { lifecycleOwner -> + DownloadDialogFragment.registerCallback(fm, lifecycleOwner, snackbarHost) + } + } else { + DownloadDialogFragment.unregisterCallback(fm) + } + DownloadDialogFragment().withArgs(1) { + putParcelableArray(KEY_MANGA, manga.mapToArray { ParcelableManga(it) }) + }.showDistinct() + } + + fun showLocalInfoDialog(manga: Manga) { + LocalInfoDialog().withArgs(1) { + putParcelable(KEY_MANGA, ParcelableManga(manga)) + }.showDistinct() + } + + fun showDirectorySelectDialog() { + MangaDirectorySelectDialog().showDistinct() + } + + fun showFavoriteDialog(manga: Manga) = showFavoriteDialog(setOf(manga)) + + fun showFavoriteDialog(manga: Collection) { + if (manga.isEmpty()) { + return + } + FavoriteDialog().withArgs(1) { + putParcelableArrayList( + KEY_MANGA_LIST, + manga.mapTo(ArrayList(manga.size), ::ParcelableManga), + ) + }.showDistinct() + } + + fun showErrorDialog(error: Throwable, url: String? = null) { + ErrorDetailsDialog().withArgs(2) { + putSerializable(KEY_ERROR, error) + putString(KEY_URL, url) + }.show() + } + + fun showBackupRestoreDialog(fileUri: Uri) { + RestoreDialogFragment().withArgs(1) { + putString(KEY_FILE, fileUri.toString()) + }.show() + } + + fun showBackupCreateDialog() { + BackupDialogFragment().show() + } + + fun showImportDialog() { + ImportDialogFragment().showDistinct() + } + + fun showFilterSheet(): Boolean = if (isFilterSupported()) { + FilterSheetFragment().showDistinct() + } else { + false + } + + fun showTagsCatalogSheet(excludeMode: Boolean) { + if (!isFilterSupported()) { + return + } + TagsCatalogSheet().withArgs(1) { + putBoolean(KEY_EXCLUDE, excludeMode) + }.showDistinct() + } + + fun showListConfigSheet(section: ListConfigSection) { + ListConfigBottomSheet().withArgs(1) { + putParcelable(KEY_LIST_SECTION, section) + }.showDistinct() + } + + fun showStatisticSheet(manga: Manga) { + MangaStatsSheet().withArgs(1) { + putParcelable(KEY_MANGA, ParcelableManga(manga)) + }.showDistinct() + } + + fun showReaderConfigSheet(mode: ReaderMode) { + ReaderConfigSheet().withArgs(1) { + putInt(KEY_READER_MODE, mode.id) + }.showDistinct() + } + + fun showWelcomeSheet() { + WelcomeSheet().showDistinct() + } + + fun showChapterPagesSheet() { + ChaptersPagesSheet().showDistinct() + } + + fun showChapterPagesSheet(defaultTab: Int) { + ChaptersPagesSheet().withArgs(1) { + putInt(KEY_TAB, defaultTab) + }.showDistinct() + } + + fun showScrobblingSelectorSheet(manga: Manga, scrobblerService: ScrobblerService?) { + ScrobblingSelectorSheet().withArgs(2) { + putParcelable(KEY_MANGA, ParcelableManga(manga)) + if (scrobblerService != null) { + putInt(KEY_ID, scrobblerService.id) + } + }.show() + } + + fun showScrobblingInfoSheet(index: Int) { + ScrobblingInfoSheet().withArgs(1) { + putInt(KEY_INDEX, index) + }.showDistinct() + } + + fun showTrackerCategoriesConfigSheet() { + TrackerCategoriesConfigSheet().showDistinct() + } + + /** Public utils **/ + + fun isFilterSupported(): Boolean = when { + fragment != null -> fragment.activity is FilterCoordinator.Owner + activity != null -> activity is FilterCoordinator.Owner + else -> false + } + + fun isChapterPagesSheetShown(): Boolean { + val sheet = getFragmentManager()?.findFragmentByTag(fragmentTag()) as? ChaptersPagesSheet + return sheet?.dialog?.isShowing == true + } + + fun closeWelcomeSheet(): Boolean { + val fm = fragment?.parentFragmentManager ?: activity?.supportFragmentManager ?: return false + val sheet = fm.findFragmentByTag(fragmentTag()) as? WelcomeSheet ?: return false + sheet.dismissAllowingStateLoss() + return true + } + + /** Private utils **/ + + private fun startActivity(intent: Intent, options: Bundle? = null) { + fragment?.startActivity(intent, options) + ?: activity?.startActivity(intent, options) + } + + private fun startActivitySafe(intent: Intent): Boolean = try { + startActivity(intent) + true + } catch (_: ActivityNotFoundException) { + false + } + + private fun startActivity(activityClass: Class) { + startActivity(Intent(contextOrNull() ?: return, activityClass)) + } + + private fun getFragmentManager(): FragmentManager? { + return fragment?.childFragmentManager ?: activity?.supportFragmentManager + } + + private fun contextOrNull(): Context? = activity ?: fragment?.context + + private fun getLifecycleOwner(): LifecycleOwner? = activity ?: fragment?.viewLifecycleOwner + + private fun DialogFragment.showDistinct(): Boolean { + val fm = this@AppRouter.getFragmentManager() ?: return false + val tag = javaClass.fragmentTag() + val existing = fm.findFragmentByTag(tag) as? DialogFragment? + if (existing != null && existing.isVisible && existing.arguments == this.arguments) { + return false + } + show(fm, tag) + return true + } + + private fun DialogFragment.show() { + show( + this@AppRouter.getFragmentManager() ?: return, + javaClass.fragmentTag(), + ) + } + + companion object { + + fun from(view: View): AppRouter? = runCatching { + AppRouter(view.findFragment()) + }.getOrElse { + (view.context.findActivity() as? FragmentActivity)?.let(::AppRouter) + } + + fun detailsIntent(context: Context, manga: Manga) = Intent(context, DetailsActivity::class.java) + .putExtra(KEY_MANGA, ParcelableManga(manga)) + + fun detailsIntent(context: Context, mangaId: Long) = Intent(context, DetailsActivity::class.java) + .putExtra(KEY_ID, mangaId) + + fun listIntent(context: Context, source: MangaSource, filter: MangaListFilter?): Intent = + Intent(context, MangaListActivity::class.java) + .setAction(ACTION_MANGA_EXPLORE) + .putExtra(KEY_SOURCE, source.name) + .apply { + if (!filter.isNullOrEmpty()) { + putExtra(KEY_FILTER, ParcelableMangaListFilter(filter)) + } + } + + fun cloudFlareResolveIntent(context: Context, exception: CloudFlareProtectedException): Intent = + Intent(context, CloudFlareActivity::class.java).apply { + data = exception.url.toUri() + putExtra(KEY_SOURCE, exception.source?.name) + exception.headers.get(CommonHeaders.USER_AGENT)?.let { + putExtra(KEY_USER_AGENT, it) + } + } + + fun suggestionsIntent(context: Context) = Intent(context, SuggestionsActivity::class.java) + + fun mangaUpdatesIntent(context: Context) = Intent(context, UpdatesActivity::class.java) + + fun readerSettingsIntent(context: Context) = + Intent(context, SettingsActivity::class.java) + .setAction(ACTION_READER) + + fun suggestionsSettingsIntent(context: Context) = + Intent(context, SettingsActivity::class.java) + .setAction(ACTION_SUGGESTIONS) + + fun trackerSettingsIntent(context: Context) = + Intent(context, SettingsActivity::class.java) + .setAction(ACTION_TRACKER) + + fun proxySettingsIntent(context: Context) = + Intent(context, SettingsActivity::class.java) + .setAction(ACTION_PROXY) + + fun historySettingsIntent(context: Context) = + Intent(context, SettingsActivity::class.java) + .setAction(ACTION_HISTORY) + + fun sourcesSettingsIntent(context: Context) = + Intent(context, SettingsActivity::class.java) + .setAction(ACTION_SOURCES) + + fun manageSourcesIntent(context: Context) = + Intent(context, SettingsActivity::class.java) + .setAction(ACTION_MANAGE_SOURCES) + + fun downloadsSettingsIntent(context: Context) = + Intent(context, SettingsActivity::class.java) + .setAction(ACTION_MANAGE_DOWNLOADS) + + fun sourceSettingsIntent(context: Context, source: MangaSource): Intent = when (source) { + is MangaSourceInfo -> sourceSettingsIntent(context, source.mangaSource) + is ExternalMangaSource -> Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) + .setData(Uri.fromParts("package", source.packageName, null)) + + else -> Intent(context, SettingsActivity::class.java) + .setAction(ACTION_SOURCE) + .putExtra(KEY_SOURCE, source.name) + } + + fun sourceAuthIntent(context: Context, source: MangaSource): Intent { + return Intent(context, SourceAuthActivity::class.java) + .putExtra(KEY_SOURCE, source.name) + } + + const val KEY_EXCLUDE = "exclude" + const val KEY_FILTER = "filter" + const val KEY_ID = "id" + const val KEY_LIST_SECTION = "list_section" + const val KEY_MANGA = "manga" + const val KEY_MANGA_LIST = "manga_list" + const val KEY_PAGES = "pages" + const val KEY_QUERY = "query" + const val KEY_READER_MODE = "reader_mode" + const val KEY_SOURCE = "source" + const val KEY_TAB = "tab" + const val KEY_TITLE = "title" + const val KEY_USER_AGENT = "user_agent" + const val KEY_URL = "url" + const val KEY_ERROR = "error" + const val KEY_FILE = "file" + const val KEY_INDEX = "index" + const val KEY_DATA = "data" + + const val ACTION_HISTORY = "${BuildConfig.APPLICATION_ID}.action.MANAGE_HISTORY" + const val ACTION_MANAGE_DOWNLOADS = "${BuildConfig.APPLICATION_ID}.action.MANAGE_DOWNLOADS" + const val ACTION_MANAGE_SOURCES = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SOURCES_LIST" + const val ACTION_MANGA_EXPLORE = "${BuildConfig.APPLICATION_ID}.action.EXPLORE_MANGA" + const val ACTION_PROXY = "${BuildConfig.APPLICATION_ID}.action.MANAGE_PROXY" + const val ACTION_READER = "${BuildConfig.APPLICATION_ID}.action.MANAGE_READER_SETTINGS" + const val ACTION_SOURCE = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SOURCE_SETTINGS" + const val ACTION_SOURCES = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SOURCES" + const val ACTION_SUGGESTIONS = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SUGGESTIONS" + const val ACTION_TRACKER = "${BuildConfig.APPLICATION_ID}.action.MANAGE_TRACKER" + + private const val ACCOUNT_KEY = "account" + private const val ACTION_ACCOUNT_SYNC_SETTINGS = "android.settings.ACCOUNT_SYNC_SETTINGS" + private const val EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args" + + private fun Class.fragmentTag() = name // TODO + + private inline fun fragmentTag() = F::class.java.fragmentTag() + } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaIntent.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/nav/MangaIntent.kt similarity index 85% rename from app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaIntent.kt rename to app/src/main/kotlin/org/koitharu/kotatsu/core/nav/MangaIntent.kt index 25dd7749e..d39d56d2f 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaIntent.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/nav/MangaIntent.kt @@ -1,11 +1,12 @@ -package org.koitharu.kotatsu.core.parser +package org.koitharu.kotatsu.core.nav import android.content.Intent import android.net.Uri import android.os.Bundle import androidx.lifecycle.SavedStateHandle import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga -import org.koitharu.kotatsu.core.ui.BaseActivity +import org.koitharu.kotatsu.core.nav.AppRouter.Companion.KEY_ID +import org.koitharu.kotatsu.core.nav.AppRouter.Companion.KEY_MANGA import org.koitharu.kotatsu.core.util.ext.getParcelableCompat import org.koitharu.kotatsu.core.util.ext.getParcelableExtraCompat import org.koitharu.kotatsu.parsers.model.Manga @@ -25,7 +26,7 @@ class MangaIntent private constructor( constructor(savedStateHandle: SavedStateHandle) : this( manga = savedStateHandle.get(KEY_MANGA)?.manga, id = savedStateHandle[KEY_ID] ?: ID_NONE, - uri = savedStateHandle[BaseActivity.EXTRA_DATA], + uri = savedStateHandle[AppRouter.KEY_DATA], ) constructor(args: Bundle?) : this( @@ -41,9 +42,6 @@ class MangaIntent private constructor( const val ID_NONE = 0L - const val KEY_MANGA = "manga" - const val KEY_ID = "id" - fun of(manga: Manga) = MangaIntent(manga, manga.id, null) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/nav/NavUtil.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/nav/NavUtil.kt new file mode 100644 index 000000000..1d0122d1e --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/nav/NavUtil.kt @@ -0,0 +1,39 @@ +package org.koitharu.kotatsu.core.nav + +import android.app.ActivityOptions +import android.os.Bundle +import android.view.View +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import org.koitharu.kotatsu.core.util.ext.isAnimationsEnabled + +inline val FragmentActivity.router: AppRouter + get() = AppRouter(this) + +inline val Fragment.router: AppRouter + get() = AppRouter(this) + +tailrec fun Fragment.dismissParentDialog(): Boolean { + return when (val parent = parentFragment) { + null -> return false + is DialogFragment -> { + parent.dismiss() + true + } + + else -> parent.dismissParentDialog() + } +} + +fun scaleUpActivityOptionsOf(view: View): Bundle? = if (view.context.isAnimationsEnabled) { + ActivityOptions.makeScaleUpAnimation( + view, + 0, + 0, + view.width, + view.height, + ).toBundle() +} else { + null +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/nav/ReaderIntent.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/nav/ReaderIntent.kt new file mode 100644 index 000000000..d1607a55a --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/nav/ReaderIntent.kt @@ -0,0 +1,61 @@ +package org.koitharu.kotatsu.core.nav + +import android.content.Context +import android.content.Intent +import org.koitharu.kotatsu.BuildConfig +import org.koitharu.kotatsu.bookmarks.domain.Bookmark +import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga +import org.koitharu.kotatsu.parsers.model.Manga +import org.koitharu.kotatsu.reader.ui.ReaderActivity +import org.koitharu.kotatsu.reader.ui.ReaderState + +@JvmInline +value class ReaderIntent private constructor( + val intent: Intent, +) { + + class Builder(context: Context) { + + private val intent = Intent(context, ReaderActivity::class.java) + .setAction(ACTION_MANGA_READ) + + fun manga(manga: Manga) = apply { + intent.putExtra(AppRouter.KEY_MANGA, ParcelableManga(manga)) + } + + fun mangaId(mangaId: Long) = apply { + intent.putExtra(AppRouter.KEY_ID, mangaId) + } + + fun incognito(incognito: Boolean) = apply { + intent.putExtra(EXTRA_INCOGNITO, incognito) + } + + fun branch(branch: String?) = apply { + intent.putExtra(EXTRA_BRANCH, branch) + } + + fun state(state: ReaderState?) = apply { + intent.putExtra(EXTRA_STATE, state) + } + + fun bookmark(bookmark: Bookmark) = manga( + bookmark.manga, + ).state( + ReaderState( + chapterId = bookmark.chapterId, + page = bookmark.page, + scroll = bookmark.scroll, + ), + ) + + fun build() = ReaderIntent(intent) + } + + companion object { + const val ACTION_MANGA_READ = "${BuildConfig.APPLICATION_ID}.action.READ_MANGA" + const val EXTRA_STATE = "state" + const val EXTRA_BRANCH = "branch" + const val EXTRA_INCOGNITO = "incognito" + } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/os/AppShortcutManager.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/os/AppShortcutManager.kt index e406d8e55..ab44b1819 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/os/AppShortcutManager.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/os/AppShortcutManager.kt @@ -23,6 +23,8 @@ import kotlinx.coroutines.withContext import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.db.TABLE_HISTORY import org.koitharu.kotatsu.core.model.getTitle +import org.koitharu.kotatsu.core.nav.AppRouter +import org.koitharu.kotatsu.core.nav.ReaderIntent import org.koitharu.kotatsu.core.parser.MangaDataRepository import org.koitharu.kotatsu.core.parser.favicon.faviconUri import org.koitharu.kotatsu.core.prefs.AppSettings @@ -36,8 +38,6 @@ import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.util.mapNotNullToSet import org.koitharu.kotatsu.parsers.util.runCatchingCancellable -import org.koitharu.kotatsu.reader.ui.ReaderActivity -import org.koitharu.kotatsu.search.ui.MangaListActivity import javax.inject.Inject import javax.inject.Singleton @@ -155,9 +155,10 @@ class AppShortcutManager @Inject constructor( .setIcon(icon) .setLongLived(true) .setIntent( - ReaderActivity.IntentBuilder(context) + ReaderIntent.Builder(context) .mangaId(manga.id) - .build(), + .build() + .intent, ) .build() } @@ -181,7 +182,7 @@ class AppShortcutManager @Inject constructor( .setLongLabel(title) .setIcon(icon) .setLongLived(true) - .setIntent(MangaListActivity.newIntent(context, source, null)) + .setIntent(AppRouter.listIntent(context, source, null)) .build() } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaDataRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaDataRepository.kt index 25be07122..35b7a63ac 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaDataRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaDataRepository.kt @@ -14,6 +14,7 @@ import org.koitharu.kotatsu.core.db.entity.toManga import org.koitharu.kotatsu.core.db.entity.toMangaTags import org.koitharu.kotatsu.core.model.LocalMangaSource import org.koitharu.kotatsu.core.model.isLocal +import org.koitharu.kotatsu.core.nav.MangaIntent import org.koitharu.kotatsu.core.prefs.ReaderMode import org.koitharu.kotatsu.core.util.ext.toFileOrNull import org.koitharu.kotatsu.parsers.model.Manga @@ -45,8 +46,8 @@ class MangaDataRepository @Inject constructor( entity.copy( cfBrightness = colorFilter?.brightness ?: 0f, cfContrast = colorFilter?.contrast ?: 0f, - cfInvert = colorFilter?.isInverted ?: false, - cfGrayscale = colorFilter?.isGrayscale ?: false, + cfInvert = colorFilter?.isInverted == true, + cfGrayscale = colorFilter?.isGrayscale == true, ), ) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/BaseActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/BaseActivity.kt index f9c89b131..0f03503c5 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/BaseActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/BaseActivity.kt @@ -21,6 +21,7 @@ import kotlinx.coroutines.flow.flowOf import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.ui.util.ActionModeDelegate import org.koitharu.kotatsu.core.ui.util.WindowInsetsDelegate import org.koitharu.kotatsu.core.util.ext.isWebViewUnavailable @@ -159,7 +160,7 @@ abstract class BaseActivity : override fun isNsfwContent(): Flow = flowOf(false) private fun putDataToExtras(intent: Intent?) { - intent?.putExtra(EXTRA_DATA, intent.data) + intent?.putExtra(AppRouter.KEY_DATA, intent.data) } protected fun setContentViewWebViewSafe(viewBindingProducer: () -> B): Boolean { @@ -178,9 +179,4 @@ abstract class BaseActivity : } protected fun hasViewBinding() = ::viewBinding.isInitialized - - companion object { - - const val EXTRA_DATA = "data" - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/BasePreferenceFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/BasePreferenceFragment.kt index db75d16e7..32338846e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/BasePreferenceFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/BasePreferenceFragment.kt @@ -1,8 +1,6 @@ package org.koitharu.kotatsu.core.ui -import android.content.ActivityNotFoundException import android.content.Context -import android.content.Intent import android.os.Bundle import android.view.View import androidx.annotation.CallSuper @@ -14,10 +12,8 @@ import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceScreen import androidx.preference.get import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.EntryPointAccessors -import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.util.RecyclerViewOwner @@ -89,14 +85,6 @@ abstract class BasePreferenceFragment(@StringRes private val titleId: Int) : (activity as? SettingsActivity)?.setSectionTitle(title) } - protected fun startActivitySafe(intent: Intent): Boolean = try { - startActivity(intent) - true - } catch (_: ActivityNotFoundException) { - Snackbar.make(listView, R.string.operation_not_supported, Snackbar.LENGTH_SHORT).show() - false - } - private fun focusPreference(key: String) { val pref = findPreference(key) if (pref == null) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/dialog/ErrorDetailsDialog.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/dialog/ErrorDetailsDialog.kt index 690e2c769..5cfbd992a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/dialog/ErrorDetailsDialog.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/dialog/ErrorDetailsDialog.kt @@ -10,14 +10,14 @@ import androidx.core.text.HtmlCompat import androidx.core.text.htmlEncode import androidx.core.text.method.LinkMovementMethodCompat import androidx.core.text.parseAsHtml -import androidx.fragment.app.FragmentManager import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.ui.AlertDialogFragment +import org.koitharu.kotatsu.core.util.ext.getCauseUrl import org.koitharu.kotatsu.core.util.ext.isReportable import org.koitharu.kotatsu.core.util.ext.report import org.koitharu.kotatsu.core.util.ext.requireSerializable -import org.koitharu.kotatsu.core.util.ext.withArgs import org.koitharu.kotatsu.databinding.DialogErrorDetailsBinding class ErrorDetailsDialog : AlertDialogFragment() { @@ -27,7 +27,7 @@ class ErrorDetailsDialog : AlertDialogFragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val args = requireArguments() - exception = args.requireSerializable(ARG_ERROR) + exception = args.requireSerializable(AppRouter.KEY_ERROR) } override fun onCreateViewBinding(inflater: LayoutInflater, container: ViewGroup?): DialogErrorDetailsBinding { @@ -41,7 +41,7 @@ class ErrorDetailsDialog : AlertDialogFragment() { text = context.getString( R.string.manga_error_description_pattern, exception.message?.htmlEncode().orEmpty(), - arguments?.getString(ARG_URL), + arguments?.getString(AppRouter.KEY_URL) ?: exception.getCauseUrl(), ).parseAsHtml(HtmlCompat.FROM_HTML_MODE_LEGACY) } } @@ -71,16 +71,4 @@ class ErrorDetailsDialog : AlertDialogFragment() { ClipData.newPlainText(getString(R.string.error), exception.stackTraceToString()), ) } - - companion object { - - private const val TAG = "ErrorDetailsDialog" - private const val ARG_ERROR = "error" - private const val ARG_URL = "url" - - fun show(fm: FragmentManager, error: Throwable, url: String?) = ErrorDetailsDialog().withArgs(2) { - putSerializable(ARG_ERROR, error) - putString(ARG_URL, url) - }.show(fm, TAG) - } } 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 7f1a649e0..3cba6819f 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 @@ -5,7 +5,6 @@ import android.annotation.SuppressLint import android.app.Activity 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 @@ -23,19 +22,17 @@ import android.graphics.Bitmap import android.graphics.Color import android.net.ConnectivityManager import android.os.Build -import android.os.Bundle import android.os.PowerManager import android.provider.Settings -import android.view.View import android.view.ViewPropertyAnimator import android.view.Window import android.webkit.WebView import androidx.activity.result.ActivityResultLauncher +import androidx.annotation.CheckResult 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 @@ -86,12 +83,14 @@ suspend fun CoroutineWorker.trySetForeground(): Boolean = runCatchingCancellable setForeground(info) }.isSuccess +@CheckResult fun ActivityResultLauncher.resolve(context: Context, input: I): ResolveInfo? { val pm = context.packageManager val intent = contract.createIntent(context, input) return pm.resolveActivity(intent, 0) } +@CheckResult fun ActivityResultLauncher.tryLaunch( input: I, options: ActivityOptionsCompat? = null, @@ -171,7 +170,7 @@ fun Context.getAnimationDuration(@IntegerRes resId: Int): Long { } fun Context.isLowRamDevice(): Boolean { - return activityManager?.isLowRamDevice ?: false + return activityManager?.isLowRamDevice == true } fun Context.isPowerSaveMode(): Boolean { @@ -185,18 +184,6 @@ val Context.ramAvailable: Long return result.availMem } -fun scaleUpActivityOptionsOf(view: View): Bundle? = if (view.context.isAnimationsEnabled) { - ActivityOptions.makeScaleUpAnimation( - view, - 0, - 0, - view.width, - view.height, - ).toBundle() -} else { - null -} - @SuppressLint("DiscouragedApi") fun Context.getLocalesConfig(): LocaleListCompat { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Bundle.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Bundle.kt index e89ec9053..3a892c297 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Bundle.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Bundle.kt @@ -110,3 +110,5 @@ fun Parcelable.Creator.unmarshall(bytes: ByteArray): T { parcel.recycle() } } + +inline fun buildBundle(capacity: Int, block: Bundle.() -> Unit): Bundle = Bundle(capacity).apply(block) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Fragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Fragment.kt index 6b7bde9a5..dc166a96a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Fragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Fragment.kt @@ -2,9 +2,7 @@ package org.koitharu.kotatsu.core.util.ext import android.os.Bundle import androidx.core.view.MenuProvider -import androidx.fragment.app.DialogFragment import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager import androidx.lifecycle.Lifecycle import androidx.lifecycle.coroutineScope @@ -18,36 +16,10 @@ inline fun T.withArgs(size: Int, block: Bundle.() -> Unit): T { val Fragment.viewLifecycleScope inline get() = viewLifecycleOwner.lifecycle.coroutineScope -fun DialogFragment.showAllowStateLoss(manager: FragmentManager, tag: String?) { - if (!manager.isStateSaved) { - show(manager, tag) - } -} - fun Fragment.addMenuProvider(provider: MenuProvider) { requireActivity().addMenuProvider(provider, viewLifecycleOwner, Lifecycle.State.RESUMED) } -fun DialogFragment.showDistinct(fm: FragmentManager, tag: String) { - val existing = fm.findFragmentByTag(tag) as? DialogFragment? - if (existing != null && existing.isVisible && existing.arguments == this.arguments) { - return - } - show(fm, tag) -} - -tailrec fun Fragment.dismissParentDialog(): Boolean { - return when (val parent = parentFragment) { - null -> return false - is DialogFragment -> { - parent.dismiss() - true - } - - else -> parent.dismissParentDialog() - } -} - @Suppress("UNCHECKED_CAST") tailrec fun Fragment.findParentCallback(cls: Class): T? { val parent = parentFragment diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/iterator/MappingIterator.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/iterator/MappingIterator.kt index 8ac9f9d41..6f5f34a1b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/iterator/MappingIterator.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/iterator/MappingIterator.kt @@ -1,7 +1,5 @@ package org.koitharu.kotatsu.core.util.iterator -import org.koitharu.kotatsu.R - class MappingIterator( private val upstream: Iterator, private val mapper: (T) -> R, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/domain/DetailsLoadUseCase.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/domain/DetailsLoadUseCase.kt index ff73e6105..4fc541e2d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/domain/DetailsLoadUseCase.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/domain/DetailsLoadUseCase.kt @@ -14,8 +14,8 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.runInterruptible import okio.IOException import org.koitharu.kotatsu.core.model.isLocal +import org.koitharu.kotatsu.core.nav.MangaIntent import org.koitharu.kotatsu.core.parser.MangaDataRepository -import org.koitharu.kotatsu.core.parser.MangaIntent import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.util.ext.peek import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt index a4d2071a3..3c764ecf1 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt @@ -1,7 +1,6 @@ package org.koitharu.kotatsu.details.ui import android.content.Context -import android.content.Intent import android.os.Bundle import android.transition.TransitionManager import android.view.Gravity @@ -50,10 +49,10 @@ import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.core.model.LocalMangaSource import org.koitharu.kotatsu.core.model.UnknownMangaSource import org.koitharu.kotatsu.core.model.getTitle -import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga import org.koitharu.kotatsu.core.model.titleResId +import org.koitharu.kotatsu.core.nav.ReaderIntent +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.os.AppShortcutManager -import org.koitharu.kotatsu.core.parser.MangaIntent import org.koitharu.kotatsu.core.parser.favicon.faviconUri import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.ui.BaseListAdapter @@ -78,7 +77,6 @@ import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.core.util.ext.parentView -import org.koitharu.kotatsu.core.util.ext.scaleUpActivityOptionsOf import org.koitharu.kotatsu.core.util.ext.setNavigationBarTransparentCompat import org.koitharu.kotatsu.core.util.ext.textAndVisible import org.koitharu.kotatsu.databinding.ActivityDetailsBinding @@ -88,28 +86,18 @@ import org.koitharu.kotatsu.details.data.ReadingTime import org.koitharu.kotatsu.details.service.MangaPrefetchService import org.koitharu.kotatsu.details.ui.model.ChapterListItem import org.koitharu.kotatsu.details.ui.model.HistoryInfo -import org.koitharu.kotatsu.details.ui.pager.ChaptersPagesSheet -import org.koitharu.kotatsu.details.ui.related.RelatedMangaActivity import org.koitharu.kotatsu.details.ui.scrobbling.ScrobblingItemDecoration import org.koitharu.kotatsu.details.ui.scrobbling.ScrollingInfoAdapter -import org.koitharu.kotatsu.download.ui.dialog.DownloadDialogFragment import org.koitharu.kotatsu.download.ui.worker.DownloadStartedObserver -import org.koitharu.kotatsu.favourites.ui.categories.select.FavoriteDialog -import org.koitharu.kotatsu.image.ui.ImageActivity import org.koitharu.kotatsu.list.domain.MangaListMapper import org.koitharu.kotatsu.list.ui.adapter.ListItemType import org.koitharu.kotatsu.list.ui.adapter.mangaGridItemAD import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.model.MangaListModel import org.koitharu.kotatsu.list.ui.size.StaticItemSizeResolver -import org.koitharu.kotatsu.local.ui.info.LocalInfoDialog import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaListFilter import org.koitharu.kotatsu.parsers.model.MangaTag -import org.koitharu.kotatsu.reader.ui.ReaderActivity import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingInfo -import org.koitharu.kotatsu.scrobbling.common.ui.selector.ScrobblingSelectorSheet -import org.koitharu.kotatsu.search.ui.MangaListActivity import javax.inject.Inject import kotlin.math.roundToInt import com.google.android.material.R as materialR @@ -164,13 +152,14 @@ class DetailsActivity : } TitleExpandListener(viewBinding.textViewTitle).attach() + val appRouter = router viewModel.mangaDetails.filterNotNull().observe(this, ::onMangaUpdated) viewModel.onMangaRemoved.observeEvent(this, ::onMangaRemoved) viewModel.onError - .filterNot { ChaptersPagesSheet.isShown(supportFragmentManager) } + .filterNot { appRouter.isChapterPagesSheetShown() } .observeEvent(this, DetailsErrorObserver(this, viewModel, exceptionResolver)) viewModel.onActionDone - .filterNot { ChaptersPagesSheet.isShown(supportFragmentManager) } + .filterNot { appRouter.isChapterPagesSheetShown() } .observeEvent(this, ReversibleActionObserver(viewBinding.scrollView, null)) combine(viewModel.historyInfo, viewModel.isLoading, ::Pair).observe(this) { onHistoryChanged(it.first, it.second) @@ -189,10 +178,8 @@ class DetailsActivity : } viewModel.chapters.observe(this, PrefetchObserver(this)) viewModel.onDownloadStarted - .filterNot { ChaptersPagesSheet.isShown(supportFragmentManager) } + .filterNot { appRouter.isChapterPagesSheetShown() } .observeEvent(this, DownloadStartedObserver(viewBinding.scrollView)) - - DownloadDialogFragment.registerCallback(this, viewBinding.scrollView) menuProvider = DetailsMenuProvider( activity = this, viewModel = viewModel, @@ -210,54 +197,30 @@ class DetailsActivity : R.id.textView_author -> { val manga = viewModel.manga.value ?: return - startActivity( - MangaListActivity.newIntent( - context = v.context, - source = manga.source, - filter = MangaListFilter(query = manga.author), - ), - ) + router.openSearch(manga.source, manga.author ?: return) } R.id.textView_source -> { val manga = viewModel.manga.value ?: return - startActivity( - MangaListActivity.newIntent( - context = v.context, - source = manga.source, - filter = null, - ), - ) + router.openList(manga.source, null) } R.id.textView_local -> { val manga = viewModel.manga.value ?: return - LocalInfoDialog.show(supportFragmentManager, manga) + router.showLocalInfoDialog(manga) } R.id.chip_favorite -> { val manga = viewModel.manga.value ?: return - FavoriteDialog.show(supportFragmentManager, manga) + router.showFavoriteDialog(manga) } - // R.id.chip_time -> { - // if (viewModel.isStatsAvailable.value) { - // val manga = viewModel.manga.value ?: return - // MangaStatsSheet.show(supportFragmentManager, manga) - // } else { - // // TODO - // } - // } - R.id.imageView_cover -> { val manga = viewModel.manga.value ?: return - startActivity( - ImageActivity.newIntent( - v.context, - manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl }, - manga.source, - ), - scaleUpActivityOptionsOf(v), + router.openImage( + url = manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl }, + source = manga.source, + anchor = v, ) } @@ -273,12 +236,12 @@ class DetailsActivity : R.id.button_scrobbling_more -> { val manga = viewModel.manga.value ?: return - ScrobblingSelectorSheet.show(supportFragmentManager, manga, null) + router.showScrobblingSelectorSheet(manga, null) } R.id.button_related_more -> { val manga = viewModel.manga.value ?: return - startActivity(RelatedMangaActivity.newIntent(v.context, manga)) + router.openRelated(manga) } } } @@ -286,7 +249,7 @@ class DetailsActivity : override fun onChipClick(chip: Chip, data: Any?) { val tag = data as? MangaTag ?: return // TODO dialog - startActivity(MangaListActivity.newIntent(this, tag.source, MangaListFilter(tags = setOf(tag)))) + router.openList(tag) } override fun onContextClick(v: View): Boolean = onLongClick(v) @@ -324,9 +287,7 @@ class DetailsActivity : } override fun onItemClick(item: Bookmark, view: View) { - startActivity( - ReaderActivity.IntentBuilder(view.context).bookmark(item).incognito(true).build(), - ) + router.openReader(ReaderIntent.Builder(view.context).bookmark(item).incognito(true).build()) Toast.makeText(view.context, R.string.incognito_mode, Toast.LENGTH_SHORT).show() } @@ -393,7 +354,7 @@ class DetailsActivity : coil, this, StaticItemSizeResolver(resources.getDimensionPixelSize(R.dimen.smaller_grid_width)), ) { item, view -> - startActivity(newIntent(view.context, item)) + router.openDetails(item) }, ).also { rv.adapter = it } adapter.items = related @@ -410,7 +371,7 @@ class DetailsActivity : if (adapter != null) { adapter.items = scrobblings } else { - adapter = ScrollingInfoAdapter(this, coil, supportFragmentManager) + adapter = ScrollingInfoAdapter(this, coil, router) adapter.items = scrobblings viewBinding.recyclerViewScrobbling.adapter = adapter viewBinding.recyclerViewScrobbling.addItemDecoration(ScrobblingItemDecoration()) @@ -531,8 +492,8 @@ class DetailsActivity : Snackbar.make(viewBinding.scrollView, R.string.chapter_is_missing, Snackbar.LENGTH_SHORT) .show() } else { - startActivity( - ReaderActivity.IntentBuilder(this) + router.openReader( + ReaderIntent.Builder(this) .manga(manga) .branch(viewModel.selectedBranchValue) .incognito(isIncognitoMode) @@ -604,15 +565,5 @@ class DetailsActivity : companion object { private const val FAV_LABEL_LIMIT = 16 - - fun newIntent(context: Context, manga: Manga): Intent { - return Intent(context, DetailsActivity::class.java) - .putExtra(MangaIntent.KEY_MANGA, ParcelableManga(manga)) - } - - fun newIntent(context: Context, mangaId: Long): Intent { - return Intent(context, DetailsActivity::class.java) - .putExtra(MangaIntent.KEY_ID, mangaId) - } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsErrorObserver.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsErrorObserver.kt index 745703330..0b6616569 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsErrorObserver.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsErrorObserver.kt @@ -5,7 +5,6 @@ import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.UnsupportedSourceException import org.koitharu.kotatsu.core.exceptions.resolve.ErrorObserver import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver -import org.koitharu.kotatsu.core.ui.dialog.ErrorDetailsDialog import org.koitharu.kotatsu.core.util.ext.getDisplayMessage import org.koitharu.kotatsu.core.util.ext.isNetworkError import org.koitharu.kotatsu.core.util.ext.isSerializable @@ -38,10 +37,10 @@ class DetailsErrorObserver( } value is ParseException -> { - val fm = fragmentManager - if (fm != null && value.isSerializable()) { + val router = router() + if (router != null && value.isSerializable()) { snackbar.setAction(R.string.details) { - ErrorDetailsDialog.show(fm, value, value.url) + router.showErrorDialog(value) } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsMenuProvider.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsMenuProvider.kt index 0f7765a87..ceedcfdd8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsMenuProvider.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsMenuProvider.kt @@ -14,16 +14,11 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar import kotlinx.coroutines.launch import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.alternatives.ui.AlternativesActivity -import org.koitharu.kotatsu.browser.BrowserActivity import org.koitharu.kotatsu.core.model.LocalMangaSource import org.koitharu.kotatsu.core.model.isLocal +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.os.AppShortcutManager import org.koitharu.kotatsu.core.util.ShareHelper -import org.koitharu.kotatsu.download.ui.dialog.DownloadDialogFragment -import org.koitharu.kotatsu.scrobbling.common.ui.selector.ScrobblingSelectorSheet -import org.koitharu.kotatsu.search.ui.multi.SearchActivity -import org.koitharu.kotatsu.stats.ui.sheet.MangaStatsSheet class DetailsMenuProvider( private val activity: FragmentActivity, @@ -49,23 +44,21 @@ class DetailsMenuProvider( } override fun onMenuItemSelected(menuItem: MenuItem): Boolean { + val manga = viewModel.getMangaOrNull() ?: return false when (menuItem.itemId) { R.id.action_share -> { - viewModel.manga.value?.let { - val shareHelper = ShareHelper(activity) - if (it.isLocal) { - shareHelper.shareCbz(listOf(it.url.toUri().toFile())) - } else { - shareHelper.shareMangaLink(it) - } + val shareHelper = ShareHelper(activity) + if (manga.isLocal) { + shareHelper.shareCbz(listOf(manga.url.toUri().toFile())) + } else { + shareHelper.shareMangaLink(manga) } } R.id.action_delete -> { - val title = viewModel.manga.value?.title.orEmpty() MaterialAlertDialogBuilder(activity) .setTitle(R.string.delete_manga) - .setMessage(activity.getString(R.string.text_delete_local_manga, title)) + .setMessage(activity.getString(R.string.text_delete_local_manga, manga.title)) .setPositiveButton(R.string.delete) { _, _ -> viewModel.deleteLocal() } @@ -74,52 +67,38 @@ class DetailsMenuProvider( } R.id.action_save -> { - DownloadDialogFragment.show(activity.supportFragmentManager, listOfNotNull(viewModel.manga.value)) + activity.router.showDownloadDialog(manga, snackbarHost) } R.id.action_browser -> { - viewModel.manga.value?.let { - activity.startActivity(BrowserActivity.newIntent(activity, it.publicUrl, it.source, it.title)) - } + activity.router.openBrowser(url = manga.publicUrl, source = manga.source, title = manga.title) } R.id.action_online -> { - viewModel.remoteManga.value?.let { - activity.startActivity(DetailsActivity.newIntent(activity, it)) - } + activity.router.openDetails(manga) } R.id.action_related -> { - viewModel.manga.value?.let { - activity.startActivity(SearchActivity.newIntent(activity, it.title)) - } + activity.router.openSearch(manga.title) } R.id.action_alternatives -> { - viewModel.manga.value?.let { - activity.startActivity(AlternativesActivity.newIntent(activity, it)) - } + activity.router.openAlternatives(manga) } R.id.action_stats -> { - viewModel.manga.value?.let { - MangaStatsSheet.show(activity.supportFragmentManager, it) - } + activity.router.showStatisticSheet(manga) } R.id.action_scrobbling -> { - viewModel.manga.value?.let { - ScrobblingSelectorSheet.show(activity.supportFragmentManager, it, null) - } + activity.router.showScrobblingSelectorSheet(manga, null) } R.id.action_shortcut -> { - viewModel.manga.value?.let { - activity.lifecycleScope.launch { - if (!appShortcutManager.requestPinShortcut(it)) { - Snackbar.make(snackbarHost, R.string.operation_not_supported, Snackbar.LENGTH_SHORT) - .show() - } + activity.lifecycleScope.launch { + if (!appShortcutManager.requestPinShortcut(manga)) { + Snackbar.make(snackbarHost, R.string.operation_not_supported, Snackbar.LENGTH_SHORT) + .show() } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt index 712daa0bf..230df1f37 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt @@ -21,7 +21,7 @@ import kotlinx.coroutines.plus import org.koitharu.kotatsu.R import org.koitharu.kotatsu.bookmarks.domain.BookmarksRepository import org.koitharu.kotatsu.core.model.getPreferredBranch -import org.koitharu.kotatsu.core.parser.MangaIntent +import org.koitharu.kotatsu.core.nav.MangaIntent import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.ListMode import org.koitharu.kotatsu.core.ui.util.ReversibleAction diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ReadButtonDelegate.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ReadButtonDelegate.kt index 5457fbd44..6a06a447f 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ReadButtonDelegate.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ReadButtonDelegate.kt @@ -15,7 +15,6 @@ import androidx.core.text.buildSpannedString import androidx.core.text.inSpans import androidx.core.view.MenuCompat import androidx.core.view.get -import androidx.fragment.app.FragmentActivity import androidx.lifecycle.LifecycleOwner import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialSplitButton @@ -23,16 +22,16 @@ import com.google.android.material.snackbar.Snackbar import kotlinx.coroutines.flow.combine import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.isLocal -import org.koitharu.kotatsu.core.util.ext.findActivity +import org.koitharu.kotatsu.core.nav.AppRouter +import org.koitharu.kotatsu.core.nav.ReaderIntent import org.koitharu.kotatsu.core.util.ext.getThemeColor import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.details.ui.model.HistoryInfo -import org.koitharu.kotatsu.download.ui.dialog.DownloadDialogFragment -import org.koitharu.kotatsu.reader.ui.ReaderActivity class ReadButtonDelegate( private val splitButton: MaterialSplitButton, private val viewModel: DetailsViewModel, + private val router: AppRouter, ) : View.OnClickListener, PopupMenu.OnMenuItemClickListener, PopupMenu.OnDismissListener { private val buttonRead = splitButton[0] as MaterialButton @@ -52,10 +51,12 @@ class ReadButtonDelegate( when (item.itemId) { R.id.action_incognito -> openReader(isIncognitoMode = true) R.id.action_forget -> viewModel.removeFromHistory() - R.id.action_download -> DownloadDialogFragment.show( - fm = (context.findActivity() as? FragmentActivity)?.supportFragmentManager ?: return false, - manga = setOf(viewModel.getMangaOrNull() ?: return false), - ) + R.id.action_download -> { + router.showDownloadDialog( + manga = setOf(viewModel.getMangaOrNull() ?: return false), + snackbarHost = splitButton, + ) + } Menu.NONE -> { val branch = viewModel.branches.value.getOrNull(item.order) ?: return false @@ -106,8 +107,8 @@ class ReadButtonDelegate( Snackbar.make(buttonRead, R.string.chapter_is_missing, Snackbar.LENGTH_SHORT) .show() // TODO } else { - context.startActivity( - ReaderActivity.IntentBuilder(context) + router.openReader( + ReaderIntent.Builder(context) .manga(manga) .branch(viewModel.selectedBranchValue) .incognito(isIncognitoMode) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/ChaptersPagesSheet.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/ChaptersPagesSheet.kt index 87a3b36d6..ab31b68dd 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/ChaptersPagesSheet.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/ChaptersPagesSheet.kt @@ -6,10 +6,11 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.view.ActionMode import androidx.core.view.isVisible -import androidx.fragment.app.FragmentManager import com.google.android.material.tabs.TabLayoutMediator import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver +import org.koitharu.kotatsu.core.nav.AppRouter +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.sheet.AdaptiveSheetBehavior.Companion.STATE_COLLAPSED import org.koitharu.kotatsu.core.ui.sheet.AdaptiveSheetBehavior.Companion.STATE_DRAGGING @@ -26,8 +27,6 @@ import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.core.util.ext.recyclerView import org.koitharu.kotatsu.core.util.ext.setTabsEnabled -import org.koitharu.kotatsu.core.util.ext.showDistinct -import org.koitharu.kotatsu.core.util.ext.withArgs import org.koitharu.kotatsu.databinding.SheetChaptersPagesBinding import org.koitharu.kotatsu.details.ui.DetailsViewModel import org.koitharu.kotatsu.details.ui.ReadButtonDelegate @@ -51,13 +50,13 @@ class ChaptersPagesSheet : BaseAdaptiveSheet(), Actio disableFitToContents() val args = arguments ?: Bundle.EMPTY - var defaultTab = args.getInt(ARG_TAB, settings.defaultDetailsTab) + var defaultTab = args.getInt(AppRouter.KEY_TAB, settings.defaultDetailsTab) val adapter = ChaptersPagesAdapter(this, settings.isPagesTabEnabled) if (!adapter.isPagesTabEnabled) { defaultTab = (defaultTab - 1).coerceAtLeast(TAB_CHAPTERS) } (viewModel as? DetailsViewModel)?.let { dvm -> - ReadButtonDelegate(binding.splitButtonRead, dvm).attach(viewLifecycleOwner) + ReadButtonDelegate(binding.splitButtonRead, dvm, router).attach(viewLifecycleOwner) } binding.pager.offscreenPageLimit = adapter.itemCount binding.pager.recyclerView?.isNestedScrollingEnabled = false @@ -145,22 +144,5 @@ class ChaptersPagesSheet : BaseAdaptiveSheet(), Actio const val TAB_CHAPTERS = 0 const val TAB_PAGES = 1 const val TAB_BOOKMARKS = 2 - private const val ARG_TAB = "tag" - private const val TAG = "ChaptersPagesSheet" - - fun show(fm: FragmentManager) { - ChaptersPagesSheet().showDistinct(fm, TAG) - } - - fun show(fm: FragmentManager, defaultTab: Int) { - ChaptersPagesSheet().withArgs(1) { - putInt(ARG_TAB, defaultTab) - }.showDistinct(fm, TAG) - } - - fun isShown(fm: FragmentManager): Boolean { - val sheet = fm.findFragmentByTag(TAG) as? ChaptersPagesSheet - return sheet?.dialog?.isShowing == true - } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/bookmarks/BookmarksFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/bookmarks/BookmarksFragment.kt index b554b645a..5c201c9b5 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/bookmarks/BookmarksFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/bookmarks/BookmarksFragment.kt @@ -18,13 +18,15 @@ import org.koitharu.kotatsu.bookmarks.domain.Bookmark import org.koitharu.kotatsu.bookmarks.ui.BookmarksSelectionDecoration import org.koitharu.kotatsu.bookmarks.ui.adapter.BookmarksAdapter import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver +import org.koitharu.kotatsu.core.nav.ReaderIntent +import org.koitharu.kotatsu.core.nav.dismissParentDialog +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.BaseFragment import org.koitharu.kotatsu.core.ui.list.ListSelectionController import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.ui.util.PagerNestedScrollHelper import org.koitharu.kotatsu.core.ui.util.ReversibleActionObserver -import org.koitharu.kotatsu.core.util.ext.dismissParentDialog import org.koitharu.kotatsu.core.util.ext.findAppCompatDelegate import org.koitharu.kotatsu.core.util.ext.findParentCallback import org.koitharu.kotatsu.core.util.ext.observe @@ -34,7 +36,6 @@ import org.koitharu.kotatsu.details.ui.pager.ChaptersPagesViewModel import org.koitharu.kotatsu.list.ui.GridSpanResolver import org.koitharu.kotatsu.list.ui.adapter.ListItemType import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration -import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder import org.koitharu.kotatsu.reader.ui.ReaderNavigationCallback import javax.inject.Inject @@ -124,21 +125,21 @@ class BookmarksFragment : BaseFragment(), if (listener != null && listener.onBookmarkSelected(item)) { dismissParentDialog() } else { - val intent = IntentBuilder(view.context) + val intent = ReaderIntent.Builder(view.context) .manga(activityViewModel.getMangaOrNull() ?: return) .bookmark(item) .incognito(true) .build() - startActivity(intent) + router.openReader(intent) } } override fun onItemLongClick(item: Bookmark, view: View): Boolean { - return selectionController?.onItemLongClick(view, item.pageId) ?: false + return selectionController?.onItemLongClick(view, item.pageId) == true } override fun onItemContextClick(item: Bookmark, view: View): Boolean { - return selectionController?.onItemContextClick(view, item.pageId) ?: false + return selectionController?.onItemContextClick(view, item.pageId) == true } override fun onSelectionChanged(controller: ListSelectionController, count: Int) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/chapters/ChaptersFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/chapters/ChaptersFragment.kt index f365b0d8b..aefa857e8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/chapters/ChaptersFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/chapters/ChaptersFragment.kt @@ -15,6 +15,9 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.ReaderIntent +import org.koitharu.kotatsu.core.nav.dismissParentDialog +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.BaseFragment import org.koitharu.kotatsu.core.ui.dialog.CommonAlertDialogs import org.koitharu.kotatsu.core.ui.list.ListSelectionController @@ -22,7 +25,6 @@ import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.ui.util.PagerNestedScrollHelper import org.koitharu.kotatsu.core.ui.widgets.ChipsView import org.koitharu.kotatsu.core.util.RecyclerViewScrollCallback -import org.koitharu.kotatsu.core.util.ext.dismissParentDialog import org.koitharu.kotatsu.core.util.ext.findAppCompatDelegate import org.koitharu.kotatsu.core.util.ext.findParentCallback import org.koitharu.kotatsu.core.util.ext.observe @@ -35,7 +37,6 @@ import org.koitharu.kotatsu.details.ui.withVolumeHeaders import org.koitharu.kotatsu.list.domain.ListFilterOption import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration import org.koitharu.kotatsu.list.ui.model.ListModel -import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder import org.koitharu.kotatsu.reader.ui.ReaderNavigationCallback import org.koitharu.kotatsu.reader.ui.ReaderState import javax.inject.Inject @@ -111,8 +112,8 @@ class ChaptersFragment : if (listener != null && listener.onChapterSelected(item.chapter)) { dismissParentDialog() } else { - startActivity( - IntentBuilder(view.context) + router.openReader( + ReaderIntent.Builder(view.context) .manga(viewModel.getMangaOrNull() ?: return) .state(ReaderState(item.chapter.id, 0, 0)) .build(), diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/PagesFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/PagesFragment.kt index eadeec114..73c709830 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/PagesFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/PagesFragment.kt @@ -22,6 +22,9 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOn import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver +import org.koitharu.kotatsu.core.nav.ReaderIntent +import org.koitharu.kotatsu.core.nav.dismissParentDialog +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.BaseFragment import org.koitharu.kotatsu.core.ui.list.BoundsScrollListener @@ -29,7 +32,6 @@ import org.koitharu.kotatsu.core.ui.list.ListSelectionController import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.ui.util.PagerNestedScrollHelper import org.koitharu.kotatsu.core.util.RecyclerViewScrollCallback -import org.koitharu.kotatsu.core.util.ext.dismissParentDialog import org.koitharu.kotatsu.core.util.ext.findAppCompatDelegate import org.koitharu.kotatsu.core.util.ext.findParentCallback import org.koitharu.kotatsu.core.util.ext.observe @@ -42,7 +44,6 @@ import org.koitharu.kotatsu.list.ui.adapter.ListItemType import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.reader.ui.PageSaveHelper -import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder import org.koitharu.kotatsu.reader.ui.ReaderNavigationCallback import org.koitharu.kotatsu.reader.ui.ReaderState import org.koitharu.kotatsu.reader.ui.pager.ReaderPage @@ -151,8 +152,8 @@ class PagesFragment : if (listener != null && listener.onPageSelected(item.page)) { dismissParentDialog() } else { - startActivity( - IntentBuilder(view.context) + router.openReader( + ReaderIntent.Builder(view.context) .manga(parentViewModel.getMangaOrNull() ?: return) .state(ReaderState(item.page.chapterId, item.page.index, 0)) .build(), @@ -161,11 +162,11 @@ class PagesFragment : } override fun onItemLongClick(item: PageThumbnail, view: View): Boolean { - return selectionController?.onItemLongClick(view, item.page.id) ?: false + return selectionController?.onItemLongClick(view, item.page.id) == true } override fun onItemContextClick(item: PageThumbnail, view: View): Boolean { - return selectionController?.onItemContextClick(view, item.page.id) ?: false + return selectionController?.onItemContextClick(view, item.page.id) == true } override fun onSelectionChanged(controller: ListSelectionController, count: Int) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedListViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedListViewModel.kt index 679f36693..3a9e30e9b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedListViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedListViewModel.kt @@ -13,7 +13,7 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.plus import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga -import org.koitharu.kotatsu.core.parser.MangaIntent +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.util.ext.call @@ -37,7 +37,7 @@ class RelatedListViewModel @Inject constructor( downloadScheduler: DownloadWorker.Scheduler, ) : MangaListViewModel(settings, downloadScheduler) { - private val seed = savedStateHandle.require(MangaIntent.KEY_MANGA).manga + private val seed = savedStateHandle.require(AppRouter.KEY_MANGA).manga private val repository = mangaRepositoryFactory.create(seed.source) private val mangaList = MutableStateFlow?>(null) private val listError = MutableStateFlow(null) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedMangaActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedMangaActivity.kt index fc8806f64..6803cbf10 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedMangaActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedMangaActivity.kt @@ -1,7 +1,5 @@ package org.koitharu.kotatsu.details.ui.related -import android.content.Context -import android.content.Intent import android.os.Bundle import androidx.core.graphics.Insets import androidx.core.view.updatePadding @@ -9,12 +7,9 @@ import androidx.fragment.app.commit import com.google.android.material.appbar.AppBarLayout import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga -import org.koitharu.kotatsu.core.parser.MangaIntent import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.databinding.ActivityContainerBinding import org.koitharu.kotatsu.main.ui.owners.AppBarOwner -import org.koitharu.kotatsu.parsers.model.Manga @AndroidEntryPoint class RelatedMangaActivity : BaseActivity(), AppBarOwner { @@ -41,10 +36,4 @@ class RelatedMangaActivity : BaseActivity(), AppBarOwn right = insets.right, ) } - - companion object { - - fun newIntent(context: Context, seed: Manga) = Intent(context, RelatedMangaActivity::class.java) - .putExtra(MangaIntent.KEY_MANGA, ParcelableManga(seed)) - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoAD.kt index 6b629ec8d..1e1990a15 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoAD.kt @@ -1,10 +1,10 @@ package org.koitharu.kotatsu.details.ui.scrobbling -import androidx.fragment.app.FragmentManager import androidx.lifecycle.LifecycleOwner import coil3.ImageLoader import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.util.ext.defaultPlaceholders import org.koitharu.kotatsu.core.util.ext.enqueueWith import org.koitharu.kotatsu.core.util.ext.newImageRequest @@ -15,12 +15,12 @@ import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingInfo fun scrobblingInfoAD( lifecycleOwner: LifecycleOwner, coil: ImageLoader, - fragmentManager: FragmentManager, + router: AppRouter, ) = adapterDelegateViewBinding( { layoutInflater, parent -> ItemScrobblingInfoBinding.inflate(layoutInflater, parent, false) }, ) { binding.root.setOnClickListener { - ScrobblingInfoSheet.show(fragmentManager, bindingAdapterPosition) + router.showScrobblingInfoSheet(bindingAdapterPosition) } bind { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoSheet.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoSheet.kt index 69b25e63e..c90797e18 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoSheet.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoSheet.kt @@ -1,6 +1,5 @@ package org.koitharu.kotatsu.details.ui.scrobbling -import android.content.Intent import android.os.Bundle import android.view.LayoutInflater import android.view.MenuItem @@ -10,13 +9,14 @@ import android.widget.AdapterView import android.widget.RatingBar import android.widget.Toast import androidx.appcompat.widget.PopupMenu -import androidx.core.net.toUri import androidx.core.text.method.LinkMovementMethodCompat -import androidx.fragment.app.FragmentManager import androidx.fragment.app.activityViewModels import coil3.ImageLoader +import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.AppRouter +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet import org.koitharu.kotatsu.core.util.ext.defaultPlaceholders import org.koitharu.kotatsu.core.util.ext.enqueueWith @@ -25,14 +25,10 @@ import org.koitharu.kotatsu.core.util.ext.newImageRequest import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.core.util.ext.sanitize -import org.koitharu.kotatsu.core.util.ext.scaleUpActivityOptionsOf -import org.koitharu.kotatsu.core.util.ext.withArgs import org.koitharu.kotatsu.databinding.SheetScrobblingBinding import org.koitharu.kotatsu.details.ui.DetailsViewModel -import org.koitharu.kotatsu.image.ui.ImageActivity import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingInfo import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingStatus -import org.koitharu.kotatsu.scrobbling.common.ui.selector.ScrobblingSelectorSheet import javax.inject.Inject @AndroidEntryPoint @@ -53,7 +49,7 @@ class ScrobblingInfoSheet : override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - scrobblerIndex = requireArguments().getInt(ARG_INDEX, scrobblerIndex) + scrobblerIndex = requireArguments().getInt(AppRouter.KEY_INDEX, scrobblerIndex) } override fun onCreateViewBinding(inflater: LayoutInflater, container: ViewGroup?): SheetScrobblingBinding { @@ -108,11 +104,11 @@ class ScrobblingInfoSheet : override fun onClick(v: View) { when (v.id) { R.id.button_menu -> menu?.show() - R.id.imageView_cover -> { - val coverUrl = viewModel.scrobblingInfo.value.getOrNull(scrobblerIndex)?.coverUrl ?: return - val options = scaleUpActivityOptionsOf(v) - startActivity(ImageActivity.newIntent(v.context, coverUrl, null), options) - } + R.id.imageView_cover -> router.openImage( + url = viewModel.scrobblingInfo.value.getOrNull(scrobblerIndex)?.coverUrl ?: return, + source = null, + anchor = v, + ) } } @@ -139,10 +135,13 @@ class ScrobblingInfoSheet : when (item.itemId) { R.id.action_browser -> { val url = viewModel.scrobblingInfo.value.getOrNull(scrobblerIndex)?.externalUrl ?: return false - val intent = Intent(Intent.ACTION_VIEW, url.toUri()) - startActivity( - Intent.createChooser(intent, getString(R.string.open_in_browser)), - ) + if (!router.openExternalBrowser(url, getString(R.string.open_in_browser))) { + Snackbar.make( + viewBinding?.textViewDescription ?: return false, + R.string.operation_not_supported, + Snackbar.LENGTH_SHORT, + ).show() + } } R.id.action_unregister -> { @@ -153,20 +152,10 @@ class ScrobblingInfoSheet : R.id.action_edit -> { val manga = viewModel.manga.value ?: return false val scrobblerService = viewModel.scrobblingInfo.value.getOrNull(scrobblerIndex)?.scrobbler - ScrobblingSelectorSheet.show(parentFragmentManager, manga, scrobblerService) + router.showScrobblingSelectorSheet(manga, scrobblerService) dismiss() } } return true } - - companion object { - - private const val TAG = "ScrobblingInfoBottomSheet" - private const val ARG_INDEX = "index" - - fun show(fm: FragmentManager, index: Int) = ScrobblingInfoSheet().withArgs(1) { - putInt(ARG_INDEX, index) - }.show(fm, TAG) - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrollingInfoAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrollingInfoAdapter.kt index fe205632a..f9ab7a6d0 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrollingInfoAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrollingInfoAdapter.kt @@ -1,18 +1,18 @@ package org.koitharu.kotatsu.details.ui.scrobbling -import androidx.fragment.app.FragmentManager import androidx.lifecycle.LifecycleOwner import coil3.ImageLoader +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.ui.BaseListAdapter import org.koitharu.kotatsu.list.ui.model.ListModel class ScrollingInfoAdapter( lifecycleOwner: LifecycleOwner, coil: ImageLoader, - fragmentManager: FragmentManager, + router: AppRouter, ) : BaseListAdapter() { init { - delegatesManager.addDelegate(scrobblingInfoAD(lifecycleOwner, coil, fragmentManager)) + delegatesManager.addDelegate(scrobblingInfoAD(lifecycleOwner, coil, router)) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/dialog/DownloadDialogFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/dialog/DownloadDialogFragment.kt index 304c58ef3..63e745275 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/dialog/DownloadDialogFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/dialog/DownloadDialogFragment.kt @@ -1,6 +1,5 @@ package org.koitharu.kotatsu.download.ui.dialog -import android.content.Intent import android.os.Bundle import android.view.LayoutInflater import android.view.Menu @@ -9,17 +8,16 @@ import android.view.ViewGroup import android.widget.Spinner import androidx.appcompat.widget.PopupMenu import androidx.core.view.isVisible -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentResultListener import androidx.fragment.app.setFragmentResult import androidx.fragment.app.viewModels +import androidx.lifecycle.LifecycleOwner import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.prefs.DownloadFormat import org.koitharu.kotatsu.core.ui.AlertDialogFragment import org.koitharu.kotatsu.core.ui.dialog.CommonAlertDialogs @@ -27,17 +25,12 @@ import org.koitharu.kotatsu.core.ui.widgets.TwoLinesItemView import org.koitharu.kotatsu.core.util.ext.findActivity import org.koitharu.kotatsu.core.util.ext.getDisplayMessage import org.koitharu.kotatsu.core.util.ext.joinToStringWithLimit -import org.koitharu.kotatsu.core.util.ext.mapToArray import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.core.util.ext.parentView -import org.koitharu.kotatsu.core.util.ext.showDistinct import org.koitharu.kotatsu.core.util.ext.showOrHide -import org.koitharu.kotatsu.core.util.ext.withArgs import org.koitharu.kotatsu.databinding.DialogDownloadBinding -import org.koitharu.kotatsu.download.ui.list.DownloadsActivity import org.koitharu.kotatsu.main.ui.owners.BottomNavOwner -import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.util.format import org.koitharu.kotatsu.settings.storage.DirectoryModel import javax.inject.Inject @@ -325,7 +318,9 @@ class DownloadDialogFragment : AlertDialogFragment(), Vie } } - private class SnackbarResultListener(private val host: View) : FragmentResultListener { + private class SnackbarResultListener( + private val host: View, + ) : FragmentResultListener { override fun onFragmentResult(requestKey: String, result: Bundle) { val isStarted = result.getBoolean(ARG_STARTED, true) @@ -337,8 +332,9 @@ class DownloadDialogFragment : AlertDialogFragment(), Vie (host.context.findActivity() as? BottomNavOwner)?.let { snackbar.anchorView = it.bottomNav } - snackbar.setAction(R.string.details) { - it.context.startActivity(Intent(it.context, DownloadsActivity::class.java)) + val router = AppRouter.from(host) + if (router != null) { + snackbar.setAction(R.string.details) { router.openDownloads() } } snackbar.show() } @@ -346,28 +342,16 @@ class DownloadDialogFragment : AlertDialogFragment(), Vie companion object { - private const val TAG = "DownloadDialogFragment" private const val RESULT_KEY = "DOWNLOAD_STARTED" private const val ARG_STARTED = "started" private const val KEY_CHECKED_OPTION = "checked_opt" - const val ARG_MANGA = "manga" - fun show(fm: FragmentManager, manga: Collection) = DownloadDialogFragment().withArgs(1) { - putParcelableArray(ARG_MANGA, manga.mapToArray { ParcelableManga(it) }) - }.showDistinct(fm, TAG) + fun registerCallback( + fm: FragmentManager, + lifecycleOwner: LifecycleOwner, + snackbarHost: View + ) = fm.setFragmentResultListener(RESULT_KEY, lifecycleOwner, SnackbarResultListener(snackbarHost)) - fun registerCallback(activity: FragmentActivity, snackbarHost: View) = - activity.supportFragmentManager.setFragmentResultListener( - RESULT_KEY, - activity, - SnackbarResultListener(snackbarHost), - ) - - fun registerCallback(fragment: Fragment, snackbarHost: View) = - fragment.childFragmentManager.setFragmentResultListener( - RESULT_KEY, - fragment.viewLifecycleOwner, - SnackbarResultListener(snackbarHost), - ) + fun unregisterCallback(fm: FragmentManager) = fm.clearFragmentResultListener(RESULT_KEY) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/dialog/DownloadDialogViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/dialog/DownloadDialogViewModel.kt index 6dd2ebf06..91812c09e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/dialog/DownloadDialogViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/dialog/DownloadDialogViewModel.kt @@ -13,6 +13,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.getPreferredBranch import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.DownloadFormat @@ -45,7 +46,7 @@ class DownloadDialogViewModel @Inject constructor( private val settings: AppSettings, ) : BaseViewModel() { - val manga = savedStateHandle.require>(DownloadDialogFragment.ARG_MANGA).map { + val manga = savedStateHandle.require>(AppRouter.KEY_MANGA).map { it.manga } private val mangaDetails = suspendLazy { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadsActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadsActivity.kt index 0c823471e..3fdf55b60 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadsActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadsActivity.kt @@ -12,6 +12,7 @@ import androidx.core.view.updatePadding import coil3.ImageLoader import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.ui.list.ListSelectionController import org.koitharu.kotatsu.core.ui.list.RecyclerScrollKeeper @@ -20,7 +21,6 @@ import org.koitharu.kotatsu.core.ui.util.ReversibleActionObserver import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.databinding.ActivityDownloadsBinding -import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.download.ui.worker.DownloadWorker import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration import javax.inject.Inject @@ -84,7 +84,7 @@ class DownloadsActivity : BaseActivity(), if (selectionController.onItemClick(item.id.mostSignificantBits)) { return } - startActivity(DetailsActivity.newIntent(view.context, item.manga ?: return)) + router.openDetails(item.manga ?: return) } override fun onItemLongClick(item: DownloadItemModel, view: View): Boolean { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadsMenuProvider.kt b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadsMenuProvider.kt index 128412c2f..933948a93 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadsMenuProvider.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadsMenuProvider.kt @@ -1,16 +1,16 @@ package org.koitharu.kotatsu.download.ui.list -import android.content.Context import android.view.Menu import android.view.MenuInflater import android.view.MenuItem import androidx.core.view.MenuProvider +import androidx.fragment.app.FragmentActivity import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.dialog.buildAlertDialog -import org.koitharu.kotatsu.settings.SettingsActivity class DownloadsMenuProvider( - private val context: Context, + private val activity: FragmentActivity, private val viewModel: DownloadsViewModel, ) : MenuProvider { @@ -24,10 +24,7 @@ class DownloadsMenuProvider( R.id.action_resume -> viewModel.resumeAll() R.id.action_cancel_all -> confirmCancelAll() R.id.action_remove_completed -> confirmRemoveCompleted() - R.id.action_settings -> { - context.startActivity(SettingsActivity.newDownloadsSettingsIntent(context)) - } - + R.id.action_settings -> activity.router.openDownloadsSetting() else -> return false } return true @@ -41,7 +38,7 @@ class DownloadsMenuProvider( } private fun confirmCancelAll() { - buildAlertDialog(context, isCentered = true) { + buildAlertDialog(activity, isCentered = true) { setTitle(R.string.cancel_all) setMessage(R.string.cancel_all_downloads_confirm) setIcon(R.drawable.ic_cancel_multiple) @@ -51,7 +48,7 @@ class DownloadsMenuProvider( } private fun confirmRemoveCompleted() { - buildAlertDialog(context, isCentered = true) { + buildAlertDialog(activity, isCentered = true) { setTitle(R.string.remove_completed) setMessage(R.string.remove_completed_downloads_confirm) setIcon(R.drawable.ic_clear_all) 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 016bb14f2..3cd30a1aa 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 @@ -25,17 +25,16 @@ import kotlinx.coroutines.sync.withLock import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.ErrorReporterReceiver import org.koitharu.kotatsu.core.model.LocalMangaSource +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.util.ext.getDrawableOrThrow import org.koitharu.kotatsu.core.util.ext.isReportable import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug -import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.download.domain.DownloadState import org.koitharu.kotatsu.download.ui.list.DownloadsActivity import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.util.format import org.koitharu.kotatsu.parsers.util.runCatchingCancellable -import org.koitharu.kotatsu.search.ui.MangaListActivity import java.util.UUID import com.google.android.material.R as materialR @@ -267,9 +266,9 @@ class DownloadNotificationFactory @AssistedInject constructor( context, manga.hashCode(), if (manga != null) { - DetailsActivity.newIntent(context, manga) + AppRouter.detailsIntent(context, manga) } else { - MangaListActivity.newIntent(context, LocalMangaSource, null) + AppRouter.listIntent(context, LocalMangaSource, null) }, PendingIntent.FLAG_CANCEL_CURRENT, false, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadStartedObserver.kt b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadStartedObserver.kt index 7ec69616e..a418c0a7c 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadStartedObserver.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadStartedObserver.kt @@ -1,12 +1,11 @@ package org.koitharu.kotatsu.download.ui.worker -import android.content.Intent import android.view.View import com.google.android.material.snackbar.Snackbar import kotlinx.coroutines.flow.FlowCollector import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.util.ext.findActivity -import org.koitharu.kotatsu.download.ui.list.DownloadsActivity import org.koitharu.kotatsu.main.ui.owners.BottomNavOwner class DownloadStartedObserver( @@ -18,8 +17,9 @@ class DownloadStartedObserver( (snackbarHost.context.findActivity() as? BottomNavOwner)?.let { snackbar.anchorView = it.bottomNav } - snackbar.setAction(R.string.details) { - it.context.startActivity(Intent(it.context, DownloadsActivity::class.java)) + val router = AppRouter.from(snackbarHost) + if (router != null) { + snackbar.setAction(R.string.details) { router.openDownloads() } } snackbar.show() } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt index de68d0922..52f9c5f77 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt @@ -21,9 +21,9 @@ import androidx.recyclerview.widget.RecyclerView import coil3.ImageLoader import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.bookmarks.ui.AllBookmarksActivity import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver import org.koitharu.kotatsu.core.model.LocalMangaSource +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.parser.external.ExternalMangaSource import org.koitharu.kotatsu.core.ui.BaseFragment import org.koitharu.kotatsu.core.ui.dialog.BigButtonsAlertDialog @@ -37,8 +37,6 @@ import org.koitharu.kotatsu.core.util.ext.findAppCompatDelegate import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.databinding.FragmentExploreBinding -import org.koitharu.kotatsu.details.ui.DetailsActivity -import org.koitharu.kotatsu.download.ui.list.DownloadsActivity import org.koitharu.kotatsu.explore.ui.adapter.ExploreAdapter import org.koitharu.kotatsu.explore.ui.adapter.ExploreListEventListener import org.koitharu.kotatsu.explore.ui.model.MangaSourceItem @@ -46,10 +44,6 @@ import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration import org.koitharu.kotatsu.list.ui.model.ListHeader import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaParserSource -import org.koitharu.kotatsu.search.ui.MangaListActivity -import org.koitharu.kotatsu.settings.SettingsActivity -import org.koitharu.kotatsu.settings.sources.catalog.SourcesCatalogActivity -import org.koitharu.kotatsu.suggestions.ui.SuggestionsActivity import javax.inject.Inject @AndroidEntryPoint @@ -76,7 +70,7 @@ class ExploreFragment : override fun onViewBindingCreated(binding: FragmentExploreBinding, savedInstanceState: Bundle?) { super.onViewBindingCreated(binding, savedInstanceState) exploreAdapter = ExploreAdapter(coil, viewLifecycleOwner, this, this) { manga, view -> - startActivity(DetailsActivity.newIntent(view.context, manga)) + router.openDetails(manga) } sourceSelectionController = ListSelectionController( appCompatDelegate = checkNotNull(findAppCompatDelegate()), @@ -91,7 +85,7 @@ class ExploreFragment : addItemDecoration(TypedListSpacingDecoration(context, false)) checkNotNull(sourceSelectionController).attachToRecyclerView(this) } - addMenuProvider(ExploreMenuProvider(binding.root.context)) + addMenuProvider(ExploreMenuProvider(router)) viewModel.content.observe(viewLifecycleOwner, checkNotNull(exploreAdapter)) viewModel.onError.observeEvent(viewLifecycleOwner, SnackbarErrorObserver(binding.recyclerView, this)) viewModel.onOpenManga.observeEvent(viewLifecycleOwner, ::onOpenManga) @@ -117,49 +111,40 @@ class ExploreFragment : override fun onListHeaderClick(item: ListHeader, view: View) { if (item.payload == R.id.nav_suggestions) { - startActivity(SuggestionsActivity.newIntent(view.context)) + router.openSuggestions() } else { - startActivity(Intent(view.context, SourcesCatalogActivity::class.java)) + router.openSourcesCatalog() } } override fun onClick(v: View) { - val intent = when (v.id) { - R.id.button_local -> MangaListActivity.newIntent(v.context, LocalMangaSource, null) - R.id.button_bookmarks -> AllBookmarksActivity.newIntent(v.context) - R.id.button_more -> SuggestionsActivity.newIntent(v.context) - R.id.button_downloads -> Intent(v.context, DownloadsActivity::class.java) - R.id.button_random -> { - viewModel.openRandom() - return - } - - else -> return + when (v.id) { + R.id.button_local -> router.openList(LocalMangaSource, null) + R.id.button_bookmarks -> router.openBookmarks() + R.id.button_more -> router.openSuggestions() + R.id.button_downloads -> router.openDownloads() + R.id.button_random -> viewModel.openRandom() } - startActivity(intent) } override fun onItemClick(item: MangaSourceItem, view: View) { if (sourceSelectionController?.onItemClick(item.id) == true) { return } - val intent = MangaListActivity.newIntent(view.context, item.source, null) - startActivity(intent) + router.openList(item.source, null) } override fun onItemLongClick(item: MangaSourceItem, view: View): Boolean { - return sourceSelectionController?.onItemLongClick(view, item.id) ?: false + return sourceSelectionController?.onItemLongClick(view, item.id) == true } override fun onItemContextClick(item: MangaSourceItem, view: View): Boolean { - return sourceSelectionController?.onItemContextClick(view, item.id) ?: false + return sourceSelectionController?.onItemContextClick(view, item.id) == true } override fun onRetryClick(error: Throwable) = Unit - override fun onEmptyActionClick() { - startActivity(Intent(context ?: return, SourcesCatalogActivity::class.java)) - } + override fun onEmptyActionClick() = router.openSourcesCatalog() override fun onSelectionChanged(controller: ListSelectionController, count: Int) { viewBinding?.recyclerView?.invalidateItemDecorations() @@ -194,7 +179,7 @@ class ExploreFragment : when (item.itemId) { R.id.action_settings -> { val source = selectedSources.singleOrNull() ?: return false - startActivity(SettingsActivity.newSourceSettingsIntent(requireContext(), source)) + router.openSourceSettings(source) mode?.finish() } @@ -232,8 +217,7 @@ class ExploreFragment : } private fun onOpenManga(manga: Manga) { - val intent = DetailsActivity.newIntent(context ?: return, manga) - startActivity(intent) + router.openDetails(manga) } private fun onGridModeChanged(isGrid: Boolean) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreMenuProvider.kt b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreMenuProvider.kt index 86211b090..869ed767c 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreMenuProvider.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreMenuProvider.kt @@ -1,15 +1,14 @@ package org.koitharu.kotatsu.explore.ui -import android.content.Context import android.view.Menu import android.view.MenuInflater import android.view.MenuItem import androidx.core.view.MenuProvider import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.settings.SettingsActivity +import org.koitharu.kotatsu.core.nav.AppRouter class ExploreMenuProvider( - private val context: Context, + private val router: AppRouter, ) : MenuProvider { override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { @@ -19,7 +18,7 @@ class ExploreMenuProvider( override fun onMenuItemSelected(menuItem: MenuItem): Boolean { return when (menuItem.itemId) { R.id.action_manage -> { - context.startActivity(SettingsActivity.newSourcesSettingsIntent(context)) + router.openSourcesSettings() true } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/FavouritesActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/FavouritesActivity.kt index dd275369d..2e0ae4937 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/FavouritesActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/FavouritesActivity.kt @@ -1,14 +1,12 @@ package org.koitharu.kotatsu.favourites.ui -import android.content.Context -import android.content.Intent import android.os.Bundle import androidx.core.graphics.Insets import androidx.core.view.updatePadding import androidx.fragment.app.commit import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.core.model.FavouriteCategory +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.databinding.ActivityContainerBinding import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment @@ -21,7 +19,7 @@ class FavouritesActivity : BaseActivity() { super.onCreate(savedInstanceState) setContentView(ActivityContainerBinding.inflate(layoutInflater)) supportActionBar?.setDisplayHomeAsUpEnabled(true) - val categoryTitle = intent.getStringExtra(EXTRA_TITLE) + val categoryTitle = intent.getStringExtra(AppRouter.KEY_TITLE) if (categoryTitle != null) { title = categoryTitle } @@ -29,7 +27,7 @@ class FavouritesActivity : BaseActivity() { if (fm.findFragmentById(R.id.container) == null) { fm.commit { setReorderingAllowed(true) - val fragment = FavouritesListFragment.newInstance(intent.getLongExtra(EXTRA_CATEGORY_ID, NO_ID)) + val fragment = FavouritesListFragment.newInstance(intent.getLongExtra(AppRouter.KEY_ID, NO_ID)) replace(R.id.container, fragment) } } @@ -41,16 +39,4 @@ class FavouritesActivity : BaseActivity() { right = insets.right, ) } - - companion object { - - private const val EXTRA_CATEGORY_ID = "cat_id" - private const val EXTRA_TITLE = "title" - - fun newIntent(context: Context) = Intent(context, FavouritesActivity::class.java) - - fun newIntent(context: Context, category: FavouriteCategory) = Intent(context, FavouritesActivity::class.java) - .putExtra(EXTRA_CATEGORY_ID, category.id) - .putExtra(EXTRA_TITLE, category.title) - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/CategoriesSelectionCallback.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/CategoriesSelectionCallback.kt index e8366ad69..641622af9 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/CategoriesSelectionCallback.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/CategoriesSelectionCallback.kt @@ -46,16 +46,6 @@ class CategoriesSelectionCallback( override fun onActionItemClicked(controller: ListSelectionController, mode: ActionMode?, item: MenuItem): Boolean { return when (item.itemId) { - /*R.id.action_view -> { - val id = controller.peekCheckedIds().singleOrNull() ?: return false - val context = recyclerView.context - val category = viewModel.getCategory(id) ?: return false - val intent = FavouritesActivity.newIntent(context, category) - context.startActivity(intent) - mode.finish() - true - }*/ - R.id.action_show -> { viewModel.setIsVisible(controller.snapshot(), true) mode?.finish() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt index b9073cf47..9965b7be6 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt @@ -15,14 +15,13 @@ import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver import org.koitharu.kotatsu.core.model.FavouriteCategory +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.ui.list.ListSelectionController import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.databinding.ActivityCategoriesBinding -import org.koitharu.kotatsu.favourites.ui.FavouritesActivity import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoriesAdapter -import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity import org.koitharu.kotatsu.list.ui.adapter.ListStateHolderListener import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration import org.koitharu.kotatsu.list.ui.model.ListModel @@ -71,30 +70,28 @@ class FavouriteCategoriesActivity : override fun onClick(v: View) { when (v.id) { - R.id.fab_add -> startActivity(FavouritesCategoryEditActivity.newIntent(this)) + R.id.fab_add -> router.openFavoriteCategoryCreate() } } override fun onItemClick(item: FavouriteCategory?, view: View) { if (item == null) { if (selectionController.count == 0) { - startActivity(FavouritesActivity.newIntent(view.context)) + router.openFavorites() } return } if (selectionController.onItemClick(item.id)) { return } - val intent = FavouritesActivity.newIntent(view.context, item) - startActivity(intent) + router.openFavorites(item) } override fun onEditClick(item: FavouriteCategory, view: View) { if (selectionController.onItemClick(item.id)) { return } - val intent = FavouritesCategoryEditActivity.newIntent(view.context, item.id) - startActivity(intent) + router.openFavoriteCategoryEdit(item.id) } override fun onItemLongClick(item: FavouriteCategory?, view: View): Boolean { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditActivity.kt index c309c2df9..0c651a94e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditActivity.kt @@ -1,7 +1,6 @@ package org.koitharu.kotatsu.favourites.ui.categories.edit import android.content.Context -import android.content.Intent import android.os.Bundle import android.text.Editable import android.view.View @@ -68,8 +67,8 @@ class FavouritesCategoryEditActivity : override fun onRestoreInstanceState(savedInstanceState: Bundle) { super.onRestoreInstanceState(savedInstanceState) - savedInstanceState.getSerializableCompat(KEY_SORT_ORDER)?.let { - selectedSortOrder = it + savedInstanceState.getSerializableCompat(KEY_SORT_ORDER)?.let { + selectedSortOrder = it } } @@ -114,8 +113,8 @@ class FavouritesCategoryEditActivity : selectedSortOrder = category?.order val sortText = getString((category?.order ?: ListSortOrder.NEWEST).titleResId) viewBinding.editSort.setText(sortText, false) - viewBinding.switchTracker.setChecked(category?.isTrackingEnabled ?: true, false) - viewBinding.switchShelf.setChecked(category?.isVisibleInLibrary ?: true, false) + viewBinding.switchTracker.setChecked(category?.isTrackingEnabled != false, false) + viewBinding.switchShelf.setChecked(category?.isVisibleInLibrary != false, false) } private fun onError(e: Throwable) { @@ -162,13 +161,7 @@ class FavouritesCategoryEditActivity : companion object { - const val EXTRA_ID = "id" const val NO_ID = -1L private const val KEY_SORT_ORDER = "sort" - - fun newIntent(context: Context, id: Long = NO_ID): Intent { - return Intent(context, FavouritesCategoryEditActivity::class.java) - .putExtra(EXTRA_ID, id) - } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditViewModel.kt index 6c9e8161f..c77c3320d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditViewModel.kt @@ -10,12 +10,12 @@ import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.plus import org.koitharu.kotatsu.core.model.FavouriteCategory +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.BaseViewModel import org.koitharu.kotatsu.core.util.ext.MutableEventFlow import org.koitharu.kotatsu.core.util.ext.call import org.koitharu.kotatsu.favourites.domain.FavouritesRepository -import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity.Companion.EXTRA_ID import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity.Companion.NO_ID import org.koitharu.kotatsu.list.domain.ListSortOrder import javax.inject.Inject @@ -27,7 +27,7 @@ class FavouritesCategoryEditViewModel @Inject constructor( private val settings: AppSettings, ) : BaseViewModel() { - private val categoryId = savedStateHandle[EXTRA_ID] ?: NO_ID + private val categoryId = savedStateHandle[AppRouter.KEY_ID] ?: NO_ID val onSaved = MutableEventFlow() val category = MutableStateFlow(null) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteDialog.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteDialog.kt index 47210e74d..73b64a2d9 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteDialog.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteDialog.kt @@ -1,7 +1,6 @@ package org.koitharu.kotatsu.favourites.ui.categories.select import android.content.DialogInterface -import android.content.Intent import android.content.res.ColorStateList import android.graphics.Color import android.graphics.drawable.ColorDrawable @@ -13,7 +12,6 @@ import android.widget.Toast import androidx.core.graphics.ColorUtils import androidx.core.view.isVisible import androidx.core.widget.ImageViewCompat -import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels import coil3.ImageLoader import coil3.request.allowRgb565 @@ -25,7 +23,7 @@ import com.google.android.material.checkbox.MaterialCheckBox import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.AlertDialogFragment import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.util.ext.disposeImageRequest @@ -38,20 +36,16 @@ import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.newImageRequest import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent -import org.koitharu.kotatsu.core.util.ext.showDistinct -import org.koitharu.kotatsu.core.util.ext.withArgs import org.koitharu.kotatsu.databinding.SheetFavoriteCategoriesBinding -import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity import org.koitharu.kotatsu.favourites.ui.categories.select.adapter.MangaCategoriesAdapter import org.koitharu.kotatsu.favourites.ui.categories.select.model.MangaCategoryItem -import org.koitharu.kotatsu.parsers.model.Manga import javax.inject.Inject @AndroidEntryPoint class FavoriteDialog : AlertDialogFragment(), OnListItemClickListener, DialogInterface.OnClickListener { - private val viewModel by viewModels() + private val viewModel by viewModels() @Inject lateinit var coil: ImageLoader @@ -84,7 +78,7 @@ class FavoriteDialog : AlertDialogFragment(), } override fun onClick(dialog: DialogInterface?, which: Int) { - startActivity(Intent(context ?: return, FavouriteCategoriesActivity::class.java)) + router.openFavoriteCategories() } private fun onError(e: Throwable) { @@ -132,19 +126,4 @@ class FavoriteDialog : AlertDialogFragment(), } } } - - companion object { - - private const val TAG = "FavoriteSheet" - const val KEY_MANGA_LIST = "manga_list" - - fun show(fm: FragmentManager, manga: Manga) = show(fm, setOf(manga)) - - fun show(fm: FragmentManager, manga: Collection) = FavoriteDialog().withArgs(1) { - putParcelableArrayList( - KEY_MANGA_LIST, - manga.mapTo(ArrayList(manga.size), ::ParcelableManga), - ) - }.showDistinct(fm, TAG) - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteSheetViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteDialogViewModel.kt similarity index 94% rename from app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteSheetViewModel.kt rename to app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteDialogViewModel.kt index 9a0ceb29d..f1e914e2f 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteSheetViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteDialogViewModel.kt @@ -16,6 +16,7 @@ import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.core.model.ids import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.observeAsFlow import org.koitharu.kotatsu.core.ui.BaseViewModel @@ -28,13 +29,13 @@ import org.koitharu.kotatsu.list.ui.model.LoadingState import javax.inject.Inject @HiltViewModel -class FavoriteSheetViewModel @Inject constructor( +class FavoriteDialogViewModel @Inject constructor( savedStateHandle: SavedStateHandle, private val favouritesRepository: FavouritesRepository, settings: AppSettings, ) : BaseViewModel() { - val manga = savedStateHandle.require>(FavoriteDialog.KEY_MANGA_LIST).map { + val manga = savedStateHandle.require>(AppRouter.KEY_MANGA_LIST).map { it.manga } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/container/FavouriteTabPopupMenuProvider.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/container/FavouriteTabPopupMenuProvider.kt index dbd29e6c4..9fcaa78c9 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/container/FavouriteTabPopupMenuProvider.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/container/FavouriteTabPopupMenuProvider.kt @@ -6,12 +6,13 @@ import android.view.MenuInflater import android.view.MenuItem import androidx.core.view.MenuProvider import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.ui.dialog.buildAlertDialog -import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment.Companion.NO_ID class FavouriteTabPopupMenuProvider( private val context: Context, + private val router: AppRouter, private val viewModel: FavouritesContainerViewModel, private val categoryId: Long ) : MenuProvider { @@ -28,12 +29,8 @@ class FavouriteTabPopupMenuProvider( override fun onMenuItemSelected(menuItem: MenuItem): Boolean { when (menuItem.itemId) { R.id.action_hide -> viewModel.hide(categoryId) - R.id.action_edit -> context.startActivity( - FavouritesCategoryEditActivity.newIntent(context, categoryId), - ) - + R.id.action_edit -> router.openFavoriteCategoryEdit(categoryId) R.id.action_delete -> confirmDelete() - else -> return false } return true diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/container/FavouritesContainerFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/container/FavouritesContainerFragment.kt index 217a4edb9..05f77fd83 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/container/FavouritesContainerFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/container/FavouritesContainerFragment.kt @@ -1,6 +1,5 @@ package org.koitharu.kotatsu.favourites.ui.container -import android.content.Intent import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -16,6 +15,7 @@ import coil3.ImageLoader import com.google.android.material.tabs.TabLayoutMediator import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.BaseFragment import org.koitharu.kotatsu.core.ui.util.ActionModeListener import org.koitharu.kotatsu.core.ui.util.ReversibleActionObserver @@ -29,7 +29,6 @@ import org.koitharu.kotatsu.core.util.ext.setTabsEnabled import org.koitharu.kotatsu.core.util.ext.setTextAndVisible import org.koitharu.kotatsu.databinding.FragmentFavouritesContainerBinding import org.koitharu.kotatsu.databinding.ItemEmptyStateBinding -import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity import javax.inject.Inject @AndroidEntryPoint @@ -55,13 +54,13 @@ class FavouritesContainerFragment : BaseFragment startActivity( - Intent(v.context, FavouriteCategoriesActivity::class.java), - ) + R.id.button_retry -> router.openFavoriteCategories() } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/container/FavouritesContainerMenuProvider.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/container/FavouritesContainerMenuProvider.kt index 1605204e4..f8c011f29 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/container/FavouritesContainerMenuProvider.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/container/FavouritesContainerMenuProvider.kt @@ -1,16 +1,14 @@ package org.koitharu.kotatsu.favourites.ui.container -import android.content.Context -import android.content.Intent import android.view.Menu import android.view.MenuInflater import android.view.MenuItem import androidx.core.view.MenuProvider import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity +import org.koitharu.kotatsu.core.nav.AppRouter class FavouritesContainerMenuProvider( - private val context: Context, + private val router: AppRouter, ) : MenuProvider { override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { @@ -20,7 +18,7 @@ class FavouritesContainerMenuProvider( override fun onMenuItemSelected(menuItem: MenuItem): Boolean { when (menuItem.itemId) { R.id.action_manage -> { - context.startActivity(Intent(context, FavouriteCategoriesActivity::class.java)) + router.openFavoriteCategories() } else -> return false diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/container/FavouritesTabConfigurationStrategy.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/container/FavouritesTabConfigurationStrategy.kt index c3fc540b1..2d3c9e74f 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/container/FavouritesTabConfigurationStrategy.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/container/FavouritesTabConfigurationStrategy.kt @@ -3,17 +3,19 @@ package org.koitharu.kotatsu.favourites.ui.container import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayoutMediator.TabConfigurationStrategy import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.ui.util.PopupMenuMediator class FavouritesTabConfigurationStrategy( private val adapter: FavouritesContainerAdapter, private val viewModel: FavouritesContainerViewModel, + private val router: AppRouter, ) : TabConfigurationStrategy { override fun onConfigureTab(tab: TabLayout.Tab, position: Int) { val item = adapter.getItem(position) tab.text = item.title ?: tab.view.context.getString(R.string.all_favourites) tab.tag = item - PopupMenuMediator(FavouriteTabPopupMenuProvider(tab.view.context, viewModel, item.id)).attach(tab.view) + PopupMenuMediator(FavouriteTabPopupMenuProvider(tab.view.context, router, viewModel, item.id)).attach(tab.view) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/FilterHeaderFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/FilterHeaderFragment.kt index ff7d30917..ca1e30837 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/FilterHeaderFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/FilterHeaderFragment.kt @@ -9,13 +9,13 @@ import com.google.android.material.chip.Chip import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.flowOn +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.BaseFragment import org.koitharu.kotatsu.core.ui.widgets.ChipsView import org.koitharu.kotatsu.core.util.ext.isAnimationsEnabled import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.databinding.FragmentFilterHeaderBinding import org.koitharu.kotatsu.filter.ui.model.FilterHeaderModel -import org.koitharu.kotatsu.filter.ui.tags.TagsCatalogSheet import org.koitharu.kotatsu.parsers.model.ContentRating import org.koitharu.kotatsu.parsers.model.ContentType import org.koitharu.kotatsu.parsers.model.Demographic @@ -54,7 +54,7 @@ class FilterHeaderFragment : BaseFragment(), ChipsV when (data) { is MangaTag -> filter.toggleTag(data, !chip.isChecked) is String -> Unit - null -> TagsCatalogSheet.show(parentFragmentManager, isExcludeTag = false) + null -> router.showTagsCatalogSheet(excludeMode = false) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/sheet/FilterSheetFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/sheet/FilterSheetFragment.kt index f85bd4485..ab69c39a7 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/sheet/FilterSheetFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/sheet/FilterSheetFragment.kt @@ -9,13 +9,12 @@ import android.widget.AdapterView import android.widget.ArrayAdapter import androidx.core.view.isGone import androidx.core.view.updatePadding -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager import com.google.android.material.chip.Chip import com.google.android.material.slider.RangeSlider import com.google.android.material.slider.Slider import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.titleResId +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.model.titleRes import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet import org.koitharu.kotatsu.core.ui.widgets.ChipsView @@ -25,11 +24,9 @@ import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.parentView import org.koitharu.kotatsu.core.util.ext.setValueRounded import org.koitharu.kotatsu.core.util.ext.setValuesRounded -import org.koitharu.kotatsu.core.util.ext.showDistinct import org.koitharu.kotatsu.databinding.SheetFilterBinding import org.koitharu.kotatsu.filter.ui.FilterCoordinator import org.koitharu.kotatsu.filter.ui.model.FilterProperty -import org.koitharu.kotatsu.filter.ui.tags.TagsCatalogSheet import org.koitharu.kotatsu.parsers.model.ContentRating import org.koitharu.kotatsu.parsers.model.ContentType import org.koitharu.kotatsu.parsers.model.Demographic @@ -88,10 +85,10 @@ class FilterSheetFragment : BaseAdaptiveSheet(), binding.sliderYear.addOnChangeListener(this::onSliderValueChange) binding.sliderYearsRange.addOnChangeListener(this::onRangeSliderValueChange) binding.layoutGenres.setOnMoreButtonClickListener { - TagsCatalogSheet.show(getChildFragmentManager(), isExcludeTag = false) + router.showTagsCatalogSheet(excludeMode = false) } binding.layoutGenresExclude.setOnMoreButtonClickListener { - TagsCatalogSheet.show(getChildFragmentManager(), isExcludeTag = true) + router.showTagsCatalogSheet(excludeMode = true) } } @@ -153,7 +150,7 @@ class FilterSheetFragment : BaseAdaptiveSheet(), is ContentType -> filter.toggleContentType(data, !chip.isChecked) is ContentRating -> filter.toggleContentRating(data, !chip.isChecked) is Demographic -> filter.toggleDemographic(data, !chip.isChecked) - null -> TagsCatalogSheet.show(getChildFragmentManager(), chip.parentView?.id == R.id.chips_genresExclude) + null -> router.showTagsCatalogSheet(excludeMode = chip.parentView?.id == R.id.chips_genresExclude) } } @@ -351,13 +348,4 @@ class FilterSheetFragment : BaseAdaptiveSheet(), } private fun requireFilter() = (requireActivity() as FilterCoordinator.Owner).filterCoordinator - - companion object { - - private const val TAG = "FilterSheet" - - fun show(fm: FragmentManager) = FilterSheetFragment().showDistinct(fm, TAG) - - fun isSupported(fragment: Fragment) = fragment.activity is FilterCoordinator.Owner - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/tags/TagsCatalogSheet.kt b/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/tags/TagsCatalogSheet.kt index 24e1e529e..e6c8ae481 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/tags/TagsCatalogSheet.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/tags/TagsCatalogSheet.kt @@ -9,17 +9,15 @@ import android.view.View import android.view.ViewGroup import android.view.inputmethod.EditorInfo import android.widget.TextView -import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.lifecycle.withCreationCallback +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.ui.sheet.AdaptiveSheetBehavior import org.koitharu.kotatsu.core.ui.sheet.AdaptiveSheetCallback import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet import org.koitharu.kotatsu.core.util.ext.observe -import org.koitharu.kotatsu.core.util.ext.showDistinct -import org.koitharu.kotatsu.core.util.ext.withArgs import org.koitharu.kotatsu.databinding.SheetTagsBinding import org.koitharu.kotatsu.filter.ui.FilterCoordinator import org.koitharu.kotatsu.filter.ui.model.TagCatalogItem @@ -33,7 +31,7 @@ class TagsCatalogSheet : BaseAdaptiveSheet(), OnListItemClickL defaultViewModelCreationExtras.withCreationCallback { factory -> factory.create( filter = (requireActivity() as FilterCoordinator.Owner).filterCoordinator, - isExcludeTag = requireArguments().getBoolean(ARG_EXCLUDE), + isExcludeTag = requireArguments().getBoolean(AppRouter.KEY_EXCLUDE), ) } }, @@ -89,14 +87,4 @@ class TagsCatalogSheet : BaseAdaptiveSheet(), OnListItemClickL override fun onStateChanged(sheet: View, newState: Int) { viewBinding?.recyclerView?.isFastScrollerEnabled = newState == AdaptiveSheetBehavior.STATE_EXPANDED } - - companion object { - - private const val TAG = "TagsCatalogSheet" - private const val ARG_EXCLUDE = "exclude" - - fun show(fm: FragmentManager, isExcludeTag: Boolean) = TagsCatalogSheet().withArgs(1) { - putBoolean(ARG_EXCLUDE, isExcludeTag) - }.showDistinct(fm, TAG) - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryActivity.kt index 5c05fbf46..2bc1f7cfb 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryActivity.kt @@ -1,7 +1,5 @@ package org.koitharu.kotatsu.history.ui -import android.content.Context -import android.content.Intent import android.os.Bundle import androidx.core.graphics.Insets import androidx.core.view.updatePadding @@ -41,9 +39,4 @@ class HistoryActivity : right = insets.right, ) } - - companion object { - - fun newIntent(context: Context) = Intent(context, HistoryActivity::class.java) - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListFragment.kt index c283b4fba..1af145c1b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListFragment.kt @@ -8,6 +8,7 @@ import androidx.appcompat.view.ActionMode import androidx.fragment.app.viewModels import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.dialog.buildAlertDialog import org.koitharu.kotatsu.core.ui.list.ListSelectionController import org.koitharu.kotatsu.core.ui.list.RecyclerScrollKeeper @@ -27,7 +28,7 @@ class HistoryListFragment : MangaListFragment() { override fun onViewBindingCreated(binding: FragmentListBinding, savedInstanceState: Bundle?) { super.onViewBindingCreated(binding, savedInstanceState) RecyclerScrollKeeper(binding.recyclerView).attach() - addMenuProvider(HistoryListMenuProvider(binding.root.context, viewModel)) + addMenuProvider(HistoryListMenuProvider(binding.root.context, router, viewModel)) viewModel.isStatsEnabled.observe(viewLifecycleOwner, MenuInvalidator(requireActivity())) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListMenuProvider.kt b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListMenuProvider.kt index 1c412c3ae..17dd20197 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListMenuProvider.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListMenuProvider.kt @@ -1,15 +1,14 @@ package org.koitharu.kotatsu.history.ui import android.content.Context -import android.content.Intent import android.view.Menu import android.view.MenuInflater import android.view.MenuItem import androidx.core.view.MenuProvider import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.ui.dialog.RememberSelectionDialogListener import org.koitharu.kotatsu.core.ui.dialog.buildAlertDialog -import org.koitharu.kotatsu.stats.ui.StatsActivity import java.time.Instant import java.time.LocalDate import java.time.ZoneId @@ -17,6 +16,7 @@ import java.time.temporal.ChronoUnit class HistoryListMenuProvider( private val context: Context, + private val router: AppRouter, private val viewModel: HistoryListViewModel, ) : MenuProvider { @@ -37,7 +37,7 @@ class HistoryListMenuProvider( } R.id.action_stats -> { - context.startActivity(Intent(context, StatsActivity::class.java)) + router.openStatistic() true } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/ImageActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/ImageActivity.kt index c030f6222..0a5b23bcf 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/ImageActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/ImageActivity.kt @@ -1,7 +1,5 @@ package org.koitharu.kotatsu.image.ui -import android.content.Context -import android.content.Intent import android.graphics.drawable.Drawable import android.net.Uri import android.os.Bundle @@ -29,6 +27,7 @@ import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver import org.koitharu.kotatsu.core.model.MangaSource +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.ui.util.PopupMenuMediator import org.koitharu.kotatsu.core.util.ShareHelper @@ -41,7 +40,6 @@ import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.databinding.ActivityImageBinding import org.koitharu.kotatsu.databinding.ItemErrorStateBinding -import org.koitharu.kotatsu.parsers.model.MangaSource import javax.inject.Inject import com.google.android.material.R as materialR @@ -123,7 +121,7 @@ class ImageActivity : BaseActivity(), ImageRequest.Listene .memoryCachePolicy(CachePolicy.DISABLED) .lifecycle(this) .listener(this) - .mangaSourceExtra(MangaSource(intent.getStringExtra(EXTRA_SOURCE))) + .mangaSourceExtra(MangaSource(intent.getStringExtra(AppRouter.KEY_SOURCE))) .target(SsivTarget(viewBinding.ssiv)) .enqueueWith(coil) } @@ -142,7 +140,7 @@ class ImageActivity : BaseActivity(), ImageRequest.Listene button.setImageDrawable( CircularProgressDrawable(this).also { it.setStyle(CircularProgressDrawable.LARGE) - it.setColorSchemeColors(getThemeColor(com.google.android.material.R.attr.colorControlNormal)) + it.setColorSchemeColors(getThemeColor(materialR.attr.colorControlNormal)) it.start() }, ) @@ -175,15 +173,4 @@ class ImageActivity : BaseActivity(), ImageRequest.Listene } } } - - companion object { - - const val EXTRA_SOURCE = "source" - - fun newIntent(context: Context, url: String, source: MangaSource?): Intent { - return Intent(context, ImageActivity::class.java) - .setData(Uri.parse(url)) - .putExtra(EXTRA_SOURCE, source?.name) - } - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/ImageViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/ImageViewModel.kt index 8839a6416..5b233f281 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/ImageViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/ImageViewModel.kt @@ -13,7 +13,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runInterruptible import org.koitharu.kotatsu.core.model.MangaSource -import org.koitharu.kotatsu.core.ui.BaseActivity +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.ui.BaseViewModel import org.koitharu.kotatsu.core.util.ext.MutableEventFlow import org.koitharu.kotatsu.core.util.ext.call @@ -35,9 +35,9 @@ class ImageViewModel @Inject constructor( launchLoadingJob(Dispatchers.Default) { val request = ImageRequest.Builder(context) .memoryCachePolicy(CachePolicy.READ_ONLY) - .data(savedStateHandle.require(BaseActivity.EXTRA_DATA)) + .data(savedStateHandle.require(AppRouter.KEY_DATA)) .memoryCachePolicy(CachePolicy.DISABLED) - .mangaSourceExtra(MangaSource(savedStateHandle[ImageActivity.EXTRA_SOURCE])) + .mangaSourceExtra(MangaSource(savedStateHandle[AppRouter.KEY_SOURCE])) .build() val bitmap = coil.execute(request).getDrawableOrThrow().toBitmap() runInterruptible(Dispatchers.IO) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListFragment.kt index 95a557ab4..6748514b2 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListFragment.kt @@ -24,6 +24,7 @@ import org.koitharu.kotatsu.alternatives.ui.AutoFixService import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver import org.koitharu.kotatsu.core.model.isLocal +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.ListMode import org.koitharu.kotatsu.core.ui.BaseFragment @@ -44,9 +45,6 @@ import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.core.util.ext.resolveDp import org.koitharu.kotatsu.core.util.ext.viewLifecycleScope import org.koitharu.kotatsu.databinding.FragmentListBinding -import org.koitharu.kotatsu.details.ui.DetailsActivity -import org.koitharu.kotatsu.download.ui.dialog.DownloadDialogFragment -import org.koitharu.kotatsu.favourites.ui.categories.select.FavoriteDialog import org.koitharu.kotatsu.list.domain.ListFilterOption import org.koitharu.kotatsu.list.domain.QuickFilterListener import org.koitharu.kotatsu.list.ui.adapter.ListItemType @@ -60,9 +58,7 @@ import org.koitharu.kotatsu.list.ui.size.DynamicItemSizeResolver import org.koitharu.kotatsu.main.ui.MainActivity import org.koitharu.kotatsu.main.ui.owners.AppBarOwner import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaListFilter import org.koitharu.kotatsu.parsers.model.MangaTag -import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder import org.koitharu.kotatsu.search.ui.MangaListActivity import javax.inject.Inject @@ -125,7 +121,6 @@ abstract class MangaListFragment : isEnabled = isSwipeRefreshEnabled } addMenuProvider(MangaListMenuProvider(this)) - DownloadDialogFragment.registerCallback(this, binding.recyclerView) viewModel.listMode.observe(viewLifecycleOwner, ::onListModeChanged) viewModel.gridScale.observe(viewLifecycleOwner, ::onGridScaleChanged) @@ -147,7 +142,7 @@ abstract class MangaListFragment : override fun onItemClick(item: Manga, view: View) { if (selectionController?.onItemClick(item.id) != true) { if ((activity as? MangaListActivity)?.showPreview(item) != true) { - startActivity(DetailsActivity.newIntent(context ?: return, item)) + router.openDetails(item) } } } @@ -162,16 +157,14 @@ abstract class MangaListFragment : override fun onReadClick(manga: Manga, view: View) { if (selectionController?.onItemClick(manga.id) != true) { - val intent = IntentBuilder(view.context).manga(manga).build() - startActivity(intent) + router.openReader(manga) } } override fun onTagClick(manga: Manga, tag: MangaTag, view: View) { if (selectionController?.onItemClick(manga.id) != true) { // TODO dialog - val intent = MangaListActivity.newIntent(view.context, tag.source, MangaListFilter(tags = setOf(tag))) - startActivity(intent) + router.openList(tag) } } @@ -317,13 +310,13 @@ abstract class MangaListFragment : } R.id.action_favourite -> { - FavoriteDialog.show(getChildFragmentManager(), selectedItems) + router.showFavoriteDialog(selectedItems) mode?.finish() true } R.id.action_save -> { - DownloadDialogFragment.show(childFragmentManager, selectedItems) + router.showDownloadDialog(selectedItems, viewBinding?.recyclerView) mode?.finish() true } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListMenuProvider.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListMenuProvider.kt index 055e79227..832e606e0 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListMenuProvider.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListMenuProvider.kt @@ -6,9 +6,9 @@ import android.view.MenuItem import androidx.core.view.MenuProvider import androidx.fragment.app.Fragment import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment import org.koitharu.kotatsu.history.ui.HistoryListFragment -import org.koitharu.kotatsu.list.ui.config.ListConfigBottomSheet import org.koitharu.kotatsu.list.ui.config.ListConfigSection import org.koitharu.kotatsu.suggestions.ui.SuggestionsFragment import org.koitharu.kotatsu.tracker.ui.updates.UpdatesFragment @@ -30,7 +30,7 @@ class MangaListMenuProvider( is UpdatesFragment -> ListConfigSection.Updated else -> ListConfigSection.General } - ListConfigBottomSheet.show(fragment.childFragmentManager, section) + fragment.router.showListConfigSheet(section) true } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/config/ListConfigBottomSheet.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/config/ListConfigBottomSheet.kt index bc7c45a5b..e8a8ff20a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/config/ListConfigBottomSheet.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/config/ListConfigBottomSheet.kt @@ -8,7 +8,6 @@ import android.widget.AdapterView import android.widget.ArrayAdapter import android.widget.CompoundButton import androidx.core.view.isVisible -import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels import com.google.android.material.button.MaterialButtonToggleGroup import com.google.android.material.slider.Slider @@ -17,8 +16,6 @@ import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.prefs.ListMode import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet import org.koitharu.kotatsu.core.util.ext.setValueRounded -import org.koitharu.kotatsu.core.util.ext.showDistinct -import org.koitharu.kotatsu.core.util.ext.withArgs import org.koitharu.kotatsu.core.util.progress.IntPercentLabelFormatter import org.koitharu.kotatsu.databinding.SheetListModeBinding @@ -113,14 +110,4 @@ class ListConfigBottomSheet : } override fun onNothingSelected(parent: AdapterView<*>?) = Unit - - companion object { - - private const val TAG = "ListModeSelectDialog" - const val ARG_SECTION = "section" - - fun show(fm: FragmentManager, section: ListConfigSection) = ListConfigBottomSheet().withArgs(1) { - putParcelable(ARG_SECTION, section) - }.showDistinct(fm, TAG) - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/config/ListConfigViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/config/ListConfigViewModel.kt index c82a39e69..75297e9f0 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/config/ListConfigViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/config/ListConfigViewModel.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.list.ui.config import androidx.lifecycle.SavedStateHandle import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.runBlocking +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.ListMode import org.koitharu.kotatsu.core.ui.BaseViewModel @@ -21,7 +22,7 @@ class ListConfigViewModel @Inject constructor( private val favouritesRepository: FavouritesRepository, ) : BaseViewModel() { - val section = savedStateHandle.require(ListConfigBottomSheet.ARG_SECTION) + val section = savedStateHandle.require(AppRouter.KEY_LIST_SECTION) var listMode: ListMode get() = when (section) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/preview/PreviewFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/preview/PreviewFragment.kt index 549544996..38aeb9465 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/preview/PreviewFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/preview/PreviewFragment.kt @@ -21,6 +21,7 @@ import coil3.util.CoilUtils import com.google.android.material.chip.Chip import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.BaseFragment import org.koitharu.kotatsu.core.ui.image.CoverSizeResolver import org.koitharu.kotatsu.core.ui.widgets.ChipsView @@ -31,16 +32,11 @@ import org.koitharu.kotatsu.core.util.ext.enqueueWith import org.koitharu.kotatsu.core.util.ext.ifNullOrEmpty import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.observe -import org.koitharu.kotatsu.core.util.ext.scaleUpActivityOptionsOf import org.koitharu.kotatsu.core.util.ext.textAndVisible import org.koitharu.kotatsu.databinding.FragmentPreviewBinding -import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.filter.ui.FilterCoordinator -import org.koitharu.kotatsu.image.ui.ImageActivity import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaListFilter import org.koitharu.kotatsu.parsers.model.MangaTag -import org.koitharu.kotatsu.reader.ui.ReaderActivity import org.koitharu.kotatsu.search.ui.MangaListActivity import javax.inject.Inject @@ -77,33 +73,18 @@ class PreviewFragment : BaseFragment(), View.OnClickList val manga = viewModel.manga.value when (v.id) { R.id.button_close -> closeSelf() - R.id.button_open -> startActivity( - DetailsActivity.newIntent(v.context, manga), + R.id.button_open -> router.openDetails(manga) + R.id.button_read -> router.openReader(manga) + + R.id.textView_author -> router.openSearch( + source = manga.source, + query = manga.author ?: return, ) - R.id.button_read -> { - startActivity( - ReaderActivity.IntentBuilder(v.context) - .manga(manga) - .build(), - ) - } - - R.id.textView_author -> startActivity( - MangaListActivity.newIntent( - context = v.context, - source = manga.source, - filter = MangaListFilter(query = manga.author), - ), - ) - - R.id.imageView_cover -> startActivity( - ImageActivity.newIntent( - v.context, - manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl }, - manga.source, - ), - scaleUpActivityOptionsOf(v), + R.id.imageView_cover -> router.openImage( + url = manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl }, + source = manga.source, + anchor = v, ) } } @@ -114,7 +95,7 @@ class PreviewFragment : BaseFragment(), View.OnClickList val tag = data as? MangaTag ?: return val filter = (activity as? FilterCoordinator.Owner)?.filterCoordinator if (filter == null) { - startActivity(MangaListActivity.newIntent(chip.context, tag.source, MangaListFilter(tags = setOf(tag)))) + router.openList(tag) } else { filter.toggleTag(tag, true) closeSelf() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/preview/PreviewViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/preview/PreviewViewModel.kt index 062c1892e..1dbb5cd4b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/preview/PreviewViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/preview/PreviewViewModel.kt @@ -22,7 +22,7 @@ import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.plus import org.koitharu.kotatsu.core.model.getPreferredBranch import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga -import org.koitharu.kotatsu.core.parser.MangaIntent +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.ui.BaseViewModel import org.koitharu.kotatsu.core.util.ext.require @@ -42,7 +42,7 @@ class PreviewViewModel @Inject constructor( ) : BaseViewModel() { val manga = MutableStateFlow( - savedStateHandle.require(MangaIntent.KEY_MANGA).manga, + savedStateHandle.require(AppRouter.KEY_MANGA).manga, ) val footer = combine( diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/ImportDialogFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/ImportDialogFragment.kt index fdc21e1d8..2deb31ee0 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/ImportDialogFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/ImportDialogFragment.kt @@ -7,7 +7,6 @@ import android.view.View import android.view.ViewGroup import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts -import androidx.fragment.app.FragmentManager import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R @@ -74,11 +73,4 @@ class ImportDialogFragment : AlertDialogFragment(), View.On Toast.makeText(ctx, msg, Toast.LENGTH_LONG).show() dismiss() } - - companion object { - - private const val TAG = "ImportDialogFragment" - - fun show(fm: FragmentManager) = ImportDialogFragment().show(fm, TAG) - } } 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 c1a6f010b..7a7bd3650 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 @@ -18,6 +18,7 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.runBlocking import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.ErrorReporterReceiver +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.ui.CoroutineIntentService import org.koitharu.kotatsu.core.util.ext.checkNotificationPermission import org.koitharu.kotatsu.core.util.ext.getDisplayMessage @@ -25,7 +26,6 @@ import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.core.util.ext.toBitmapOrNull import org.koitharu.kotatsu.core.util.ext.toUriOrNull -import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.local.data.importer.SingleMangaImporter import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.util.runCatchingCancellable @@ -113,7 +113,7 @@ class ImportService : CoroutineIntentService() { ).toBitmapOrNull(), ) notification.setSubText(manga.title) - val intent = DetailsActivity.newIntent(applicationContext, manga) + val intent = AppRouter.detailsIntent(applicationContext, manga) notification.setContentIntent( PendingIntentCompat.getActivity( applicationContext, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/LocalListFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/LocalListFragment.kt index af638e699..d781020d6 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/LocalListFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/LocalListFragment.kt @@ -16,6 +16,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.LocalMangaSource +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.list.ListSelectionController import org.koitharu.kotatsu.core.ui.widgets.TipView import org.koitharu.kotatsu.core.util.ShareHelper @@ -25,12 +26,10 @@ import org.koitharu.kotatsu.core.util.ext.tryLaunch import org.koitharu.kotatsu.core.util.ext.withArgs import org.koitharu.kotatsu.databinding.FragmentListBinding import org.koitharu.kotatsu.filter.ui.FilterCoordinator -import org.koitharu.kotatsu.filter.ui.sheet.FilterSheetFragment import org.koitharu.kotatsu.list.ui.MangaListFragment import org.koitharu.kotatsu.remotelist.ui.MangaSearchMenuProvider import org.koitharu.kotatsu.remotelist.ui.RemoteListFragment import org.koitharu.kotatsu.settings.storage.RequestStorageManagerPermissionContract -import org.koitharu.kotatsu.settings.storage.directories.MangaDirectoriesActivity class LocalListFragment : MangaListFragment(), FilterCoordinator.Owner { @@ -68,11 +67,11 @@ class LocalListFragment : MangaListFragment(), FilterCoordinator.Owner { } override fun onEmptyActionClick() { - ImportDialogFragment.show(getChildFragmentManager()) + router.showImportDialog() } override fun onFilterClick(view: View?) { - FilterSheetFragment.show(getChildFragmentManager()) + router.showFilterSheet() } override fun onPrimaryButtonClick(tipView: TipView) { @@ -82,7 +81,7 @@ class LocalListFragment : MangaListFragment(), FilterCoordinator.Owner { } override fun onSecondaryButtonClick(tipView: TipView) { - startActivity(MangaDirectoriesActivity.newIntent(tipView.context)) + router.openDirectoriesSettings() } override fun onScrolledToEnd() = viewModel.loadNextPage() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/LocalListMenuProvider.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/LocalListMenuProvider.kt index a1e3bd9b4..20136ac36 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/LocalListMenuProvider.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/LocalListMenuProvider.kt @@ -1,14 +1,12 @@ package org.koitharu.kotatsu.local.ui -import android.content.Intent import android.view.Menu import android.view.MenuInflater import android.view.MenuItem import androidx.core.view.MenuProvider import androidx.fragment.app.Fragment import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.filter.ui.sheet.FilterSheetFragment -import org.koitharu.kotatsu.settings.storage.directories.MangaDirectoriesActivity +import org.koitharu.kotatsu.core.nav.router class LocalListMenuProvider( private val fragment: Fragment, @@ -21,7 +19,7 @@ class LocalListMenuProvider( override fun onPrepareMenu(menu: Menu) { super.onPrepareMenu(menu) - menu.findItem(R.id.action_filter)?.isVisible = FilterSheetFragment.isSupported(fragment) + menu.findItem(R.id.action_filter)?.isVisible = fragment.router.isFilterSupported() } override fun onMenuItemSelected(menuItem: MenuItem): Boolean { @@ -32,14 +30,12 @@ class LocalListMenuProvider( } R.id.action_directories -> { - fragment.context?.run { - startActivity(Intent(this, MangaDirectoriesActivity::class.java)) - } + fragment.router.openDirectoriesSettings() true } R.id.action_filter -> { - FilterSheetFragment.show(fragment.childFragmentManager) + fragment.router.showFilterSheet() true } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/info/LocalInfoDialog.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/info/LocalInfoDialog.kt index 4afdb3ecb..9569350a8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/info/LocalInfoDialog.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/info/LocalInfoDialog.kt @@ -7,13 +7,11 @@ import android.view.View import android.view.ViewGroup import android.widget.Toast import androidx.core.widget.TextViewCompat -import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.combine import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga import org.koitharu.kotatsu.core.ui.AlertDialogFragment import org.koitharu.kotatsu.core.ui.widgets.SegmentedBarView import org.koitharu.kotatsu.core.util.FileSize @@ -21,10 +19,7 @@ import org.koitharu.kotatsu.core.util.KotatsuColors import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.core.util.ext.setProgressIcon -import org.koitharu.kotatsu.core.util.ext.showDistinct -import org.koitharu.kotatsu.core.util.ext.withArgs import org.koitharu.kotatsu.databinding.DialogLocalInfoBinding -import org.koitharu.kotatsu.parsers.model.Manga import com.google.android.material.R as materialR @AndroidEntryPoint @@ -108,16 +103,4 @@ class LocalInfoDialog : AlertDialogFragment(), View.OnCl ) view.animateSegments(listOf(segment)) } - - companion object { - - const val ARG_MANGA = "manga" - private const val TAG = "LocalInfoDialog" - - fun show(fm: FragmentManager, manga: Manga) { - LocalInfoDialog().withArgs(1) { - putParcelable(ARG_MANGA, ParcelableManga(manga)) - }.showDistinct(fm, TAG) - } - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/info/LocalInfoViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/info/LocalInfoViewModel.kt index 648a8383c..1f1c78bd8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/info/LocalInfoViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/info/LocalInfoViewModel.kt @@ -6,6 +6,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.ui.BaseViewModel import org.koitharu.kotatsu.core.util.ext.MutableEventFlow import org.koitharu.kotatsu.core.util.ext.call @@ -25,7 +26,7 @@ class LocalInfoViewModel @Inject constructor( private val deleteReadChaptersUseCase: DeleteReadChaptersUseCase, ) : BaseViewModel() { - private val manga = savedStateHandle.require(LocalInfoDialog.ARG_MANGA).manga + private val manga = savedStateHandle.require(AppRouter.KEY_MANGA).manga val isCleaningUp = MutableStateFlow(false) val onCleanedUp = MutableEventFlow>() 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 87181e871..330e4a06c 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 @@ -40,6 +40,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.NavItem import org.koitharu.kotatsu.core.ui.BaseActivity @@ -49,29 +50,20 @@ import org.koitharu.kotatsu.core.ui.util.OptionsMenuBadgeHelper import org.koitharu.kotatsu.core.ui.widgets.SlidingBottomNavigationView import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent -import org.koitharu.kotatsu.core.util.ext.scaleUpActivityOptionsOf import org.koitharu.kotatsu.databinding.ActivityMainBinding 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.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 -import org.koitharu.kotatsu.main.ui.welcome.WelcomeSheet import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaListFilter import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaTag -import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder -import org.koitharu.kotatsu.search.ui.MangaListActivity -import org.koitharu.kotatsu.search.ui.multi.SearchActivity import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionFragment import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionListener import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionViewModel -import org.koitharu.kotatsu.settings.SettingsActivity -import org.koitharu.kotatsu.settings.about.AppUpdateActivity import org.koitharu.kotatsu.settings.backup.PeriodicalBackupService import javax.inject.Inject import com.google.android.material.R as materialR @@ -137,9 +129,7 @@ class MainActivity : BaseActivity(), AppBarOwner, BottomNav viewModel.isResumeEnabled.observe(this, this::onResumeEnabledChanged) viewModel.feedCounter.observe(this, ::onFeedCounterChanged) viewModel.appUpdate.observe(this, MenuInvalidator(this)) - viewModel.onFirstStart.observeEvent(this) { - WelcomeSheet.show(supportFragmentManager) - } + viewModel.onFirstStart.observeEvent(this) { router.showWelcomeSheet() } viewModel.isBottomNavPinned.observe(this, ::setNavbarPinned) searchSuggestionViewModel.isIncognitoModeEnabled.observe(this, this::onIncognitoModeChanged) viewBinding.bottomNav?.addOnLayoutChangeListener(this) @@ -188,7 +178,7 @@ class MainActivity : BaseActivity(), AppBarOwner, BottomNav } R.id.action_settings -> { - startActivity(SettingsActivity.newIntent(this)) + router.openSettings() true } @@ -198,7 +188,7 @@ class MainActivity : BaseActivity(), AppBarOwner, BottomNav } R.id.action_app_update -> { - startActivity(Intent(this, AppUpdateActivity::class.java)) + router.openAppUpdate() true } @@ -241,7 +231,7 @@ class MainActivity : BaseActivity(), AppBarOwner, BottomNav if (fragment == null) { supportFragmentManager.commit { setReorderingAllowed(true) - add(R.id.container, SearchSuggestionFragment.newInstance(), TAG_SEARCH) + add(R.id.container, SearchSuggestionFragment(), TAG_SEARCH) navigationDelegate.primaryFragment?.let { setMaxLifecycle(it, Lifecycle.State.STARTED) } @@ -253,13 +243,13 @@ class MainActivity : BaseActivity(), AppBarOwner, BottomNav } override fun onMangaClick(manga: Manga) { - startActivity(DetailsActivity.newIntent(this, manga)) + router.openDetails(manga) } override fun onQueryClick(query: String, submit: Boolean) { viewBinding.searchView.query = query if (submit && query.isNotEmpty()) { - startActivity(SearchActivity.newIntent(this, query)) + router.openSearch(query) searchSuggestionViewModel.saveQuery(query) viewBinding.searchView.post { closeSearchCallback.handleOnBackPressed() @@ -268,7 +258,7 @@ class MainActivity : BaseActivity(), AppBarOwner, BottomNav } override fun onTagClick(tag: MangaTag) { - startActivity(MangaListActivity.newIntent(this, tag.source, MangaListFilter(tags = setOf(tag)))) + router.openList(tag) } override fun onQueryChanged(query: String) { @@ -280,8 +270,7 @@ class MainActivity : BaseActivity(), AppBarOwner, BottomNav } override fun onSourceClick(source: MangaSource) { - val intent = MangaListActivity.newIntent(this, source, null) - startActivity(intent) + router.openList(source, null) } override fun onSupportActionModeStarted(mode: ActionMode) { @@ -302,10 +291,7 @@ class MainActivity : BaseActivity(), AppBarOwner, BottomNav private fun onOpenReader(manga: Manga) { val fab = viewBinding.fab ?: viewBinding.navRail?.headerView - val options = fab?.let { - scaleUpActivityOptionsOf(it) - } - startActivity(IntentBuilder(this).manga(manga).build(), options) + router.openReader(manga, fab) } private fun onFeedCounterChanged(counter: Int) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/welcome/WelcomeSheet.kt b/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/welcome/WelcomeSheet.kt index 50bed1ffc..48d0cc61f 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/welcome/WelcomeSheet.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/welcome/WelcomeSheet.kt @@ -9,23 +9,21 @@ import android.view.ViewGroup import androidx.activity.result.ActivityResultCallback import androidx.activity.result.contract.ActivityResultContracts import androidx.core.view.isGone -import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels import com.google.android.material.chip.Chip import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.titleResId +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet import org.koitharu.kotatsu.core.ui.widgets.ChipsView import org.koitharu.kotatsu.core.util.ext.getDisplayName import org.koitharu.kotatsu.core.util.ext.observe -import org.koitharu.kotatsu.core.util.ext.showDistinct import org.koitharu.kotatsu.core.util.ext.tryLaunch import org.koitharu.kotatsu.databinding.SheetWelcomeBinding import org.koitharu.kotatsu.filter.ui.model.FilterProperty import org.koitharu.kotatsu.parsers.model.ContentType -import org.koitharu.kotatsu.settings.backup.RestoreDialogFragment import java.util.Locale @AndroidEntryPoint @@ -82,7 +80,7 @@ class WelcomeSheet : BaseAdaptiveSheet(), ChipsView.OnChipC override fun onActivityResult(result: Uri?) { if (result != null) { - RestoreDialogFragment.show(parentFragmentManager, result) + router.showBackupRestoreDialog(result) } } @@ -111,17 +109,4 @@ class WelcomeSheet : BaseAdaptiveSheet(), ChipsView.OnChipC }, ) } - - companion object { - - private const val TAG = "WelcomeSheet" - - fun show(fm: FragmentManager) = WelcomeSheet().showDistinct(fm, TAG) - - fun dismiss(fm: FragmentManager): Boolean { - val sheet = fm.findFragmentByTag(TAG) as? WelcomeSheet ?: return false - sheet.dismissAllowingStateLoss() - return true - } - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt index b463e014a..669270e42 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt @@ -1,6 +1,5 @@ package org.koitharu.kotatsu.reader.ui -import android.content.Context import android.content.Intent import android.os.Bundle import android.transition.Fade @@ -29,12 +28,10 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.bookmarks.domain.Bookmark import org.koitharu.kotatsu.core.exceptions.resolve.DialogErrorObserver -import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga -import org.koitharu.kotatsu.core.parser.MangaIntent +import org.koitharu.kotatsu.core.nav.AppRouter +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.ReaderMode import org.koitharu.kotatsu.core.ui.BaseFullscreenActivity @@ -50,9 +47,7 @@ import org.koitharu.kotatsu.core.util.ext.postDelayed import org.koitharu.kotatsu.core.util.ext.setValueRounded import org.koitharu.kotatsu.core.util.ext.zipWithPrevious import org.koitharu.kotatsu.databinding.ActivityReaderBinding -import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.details.ui.pager.pages.PagesSavedObserver -import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.reader.data.TapGridSettings import org.koitharu.kotatsu.reader.domain.TapGridArea @@ -169,7 +164,7 @@ class ReaderActivity : override fun getParentActivityIntent(): Intent? { val manga = viewModel.getMangaOrNull() ?: return null - return DetailsActivity.newIntent(this, manga) + return AppRouter.detailsIntent(this, manga) } override fun onUserInteraction() { @@ -358,7 +353,7 @@ class ReaderActivity : override fun openMenu() { viewModel.saveCurrentState(readerManager.currentReader?.getCurrentState()) val currentMode = readerManager.currentMode ?: return - ReaderConfigSheet.show(supportFragmentManager, currentMode) + router.showReaderConfigSheet(currentMode) } override fun scrollBy(delta: Int, smooth: Boolean): Boolean { @@ -413,50 +408,8 @@ class ReaderActivity : viewModel.uiState.value?.isSliderAvailable() == true } - class IntentBuilder(context: Context) { - - private val intent = Intent(context, ReaderActivity::class.java) - .setAction(ACTION_MANGA_READ) - - fun manga(manga: Manga) = apply { - intent.putExtra(MangaIntent.KEY_MANGA, ParcelableManga(manga)) - } - - fun mangaId(mangaId: Long) = apply { - intent.putExtra(MangaIntent.KEY_ID, mangaId) - } - - fun incognito(incognito: Boolean) = apply { - intent.putExtra(EXTRA_INCOGNITO, incognito) - } - - fun branch(branch: String?) = apply { - intent.putExtra(EXTRA_BRANCH, branch) - } - - fun state(state: ReaderState?) = apply { - intent.putExtra(EXTRA_STATE, state) - } - - fun bookmark(bookmark: Bookmark) = manga( - bookmark.manga, - ).state( - ReaderState( - chapterId = bookmark.chapterId, - page = bookmark.page, - scroll = bookmark.scroll, - ), - ) - - fun build() = intent - } - companion object { - const val ACTION_MANGA_READ = "${BuildConfig.APPLICATION_ID}.action.READ_MANGA" - const val EXTRA_STATE = "state" - const val EXTRA_BRANCH = "branch" - const val EXTRA_INCOGNITO = "incognito" private const val TOAST_DURATION = 1500L } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderMenuProvider.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderMenuProvider.kt index 610b517db..946852172 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderMenuProvider.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderMenuProvider.kt @@ -6,8 +6,7 @@ import android.view.MenuItem import androidx.core.view.MenuProvider import androidx.fragment.app.FragmentActivity import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.details.ui.pager.ChaptersPagesSheet -import org.koitharu.kotatsu.reader.ui.config.ReaderConfigSheet +import org.koitharu.kotatsu.core.nav.router class ReaderMenuProvider( private val activity: FragmentActivity, @@ -42,14 +41,14 @@ class ReaderMenuProvider( override fun onMenuItemSelected(menuItem: MenuItem): Boolean { return when (menuItem.itemId) { R.id.action_pages_thumbs -> { - ChaptersPagesSheet.show(activity.supportFragmentManager) + activity.router.showChapterPagesSheet() true } R.id.action_options -> { viewModel.saveCurrentState(readerManager.currentReader?.getCurrentState()) val currentMode = readerManager.currentMode ?: return false - ReaderConfigSheet.show(activity.supportFragmentManager, currentMode) + activity.router.showReaderConfigSheet(currentMode) true } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt index 93b2e42d7..030a56284 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt @@ -31,9 +31,10 @@ import org.koitharu.kotatsu.R import org.koitharu.kotatsu.bookmarks.domain.Bookmark import org.koitharu.kotatsu.bookmarks.domain.BookmarksRepository import org.koitharu.kotatsu.core.model.getPreferredBranch +import org.koitharu.kotatsu.core.nav.MangaIntent +import org.koitharu.kotatsu.core.nav.ReaderIntent import org.koitharu.kotatsu.core.os.AppShortcutManager import org.koitharu.kotatsu.core.parser.MangaDataRepository -import org.koitharu.kotatsu.core.parser.MangaIntent import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.ReaderMode import org.koitharu.kotatsu.core.prefs.observeAsFlow @@ -104,8 +105,8 @@ class ReaderViewModel @Inject constructor( private var stateChangeJob: Job? = null init { - selectedBranch.value = savedStateHandle.get(ReaderActivity.EXTRA_BRANCH) - readingState.value = savedStateHandle[ReaderActivity.EXTRA_STATE] + selectedBranch.value = savedStateHandle.get(ReaderIntent.EXTRA_BRANCH) + readingState.value = savedStateHandle[ReaderIntent.EXTRA_STATE] mangaDetails.value = intent.manga?.let { MangaDetails(it, null, null, false) } } @@ -114,7 +115,7 @@ class ReaderViewModel @Inject constructor( val onShowToast = MutableEventFlow() val uiState = MutableStateFlow(null) - val incognitoMode = if (savedStateHandle.get(ReaderActivity.EXTRA_INCOGNITO) == true) { + val incognitoMode = if (savedStateHandle.get(ReaderIntent.EXTRA_INCOGNITO) == true) { MutableStateFlow(true) } else { interactor.observeIncognitoMode(manga) @@ -240,7 +241,7 @@ class ReaderViewModel @Inject constructor( fun saveCurrentState(state: ReaderState? = null) { if (state != null) { readingState.value = state - savedStateHandle[ReaderActivity.EXTRA_STATE] = state + savedStateHandle[ReaderIntent.EXTRA_STATE] = state } if (incognitoMode.value) { return diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt index c9444532a..474ec79ba 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt @@ -1,7 +1,5 @@ package org.koitharu.kotatsu.reader.ui.colorfilter -import android.content.Context -import android.content.Intent import android.content.res.Resources import android.graphics.Bitmap import android.os.Bundle @@ -23,8 +21,6 @@ import com.google.android.material.slider.LabelFormatter import com.google.android.material.slider.Slider import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga -import org.koitharu.kotatsu.core.model.parcelable.ParcelableMangaPage import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.util.ext.decodeRegion import org.koitharu.kotatsu.core.util.ext.enqueueWith @@ -35,7 +31,6 @@ import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.core.util.ext.setChecked import org.koitharu.kotatsu.core.util.ext.setValueRounded import org.koitharu.kotatsu.databinding.ActivityColorFilterBinding -import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaPage import org.koitharu.kotatsu.parsers.util.format import org.koitharu.kotatsu.reader.domain.ReaderColorFilter @@ -131,8 +126,8 @@ class ColorFilterConfigActivity : private fun onColorFilterChanged(readerColorFilter: ReaderColorFilter?) { viewBinding.sliderBrightness.setValueRounded(readerColorFilter?.brightness ?: 0f) viewBinding.sliderContrast.setValueRounded(readerColorFilter?.contrast ?: 0f) - viewBinding.switchInvert.setChecked(readerColorFilter?.isInverted ?: false, false) - viewBinding.switchGrayscale.setChecked(readerColorFilter?.isGrayscale ?: false, false) + viewBinding.switchInvert.setChecked(readerColorFilter?.isInverted == true, false) + viewBinding.switchGrayscale.setChecked(readerColorFilter?.isGrayscale == true, false) viewBinding.imageViewAfter.colorFilter = readerColorFilter?.toColorFilter() } @@ -168,15 +163,4 @@ class ColorFilterConfigActivity : return pattern.format(percent) } } - - companion object { - - const val EXTRA_PAGES = "pages" - const val EXTRA_MANGA = "manga_id" - - fun newIntent(context: Context, manga: Manga, page: MangaPage) = - Intent(context, ColorFilterConfigActivity::class.java) - .putExtra(EXTRA_MANGA, ParcelableManga(manga)) - .putExtra(EXTRA_PAGES, ParcelableMangaPage(page)) - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigViewModel.kt index 91cc435a6..dec4a3d13 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigViewModel.kt @@ -6,6 +6,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga import org.koitharu.kotatsu.core.model.parcelable.ParcelableMangaPage +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.parser.MangaDataRepository import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.BaseViewModel @@ -13,7 +14,6 @@ import org.koitharu.kotatsu.core.util.ext.MutableEventFlow import org.koitharu.kotatsu.core.util.ext.call import org.koitharu.kotatsu.core.util.ext.require import org.koitharu.kotatsu.reader.domain.ReaderColorFilter -import org.koitharu.kotatsu.reader.ui.colorfilter.ColorFilterConfigActivity.Companion.EXTRA_MANGA import javax.inject.Inject @HiltViewModel @@ -23,12 +23,12 @@ class ColorFilterConfigViewModel @Inject constructor( private val mangaDataRepository: MangaDataRepository, ) : BaseViewModel() { - private val manga = savedStateHandle.require(EXTRA_MANGA).manga + private val manga = savedStateHandle.require(AppRouter.KEY_MANGA).manga private var initialColorFilter: ReaderColorFilter? = null val colorFilter = MutableStateFlow(null) val onDismiss = MutableEventFlow() - val preview = savedStateHandle.require(ColorFilterConfigActivity.EXTRA_PAGES).page + val preview = savedStateHandle.require(AppRouter.KEY_PAGES).page val isChanged: Boolean get() = colorFilter.value != initialColorFilter diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/config/ReaderConfigSheet.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/config/ReaderConfigSheet.kt index 37da96afe..e7bf450d0 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/config/ReaderConfigSheet.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/config/ReaderConfigSheet.kt @@ -7,7 +7,6 @@ import android.view.ViewGroup import android.widget.CompoundButton import androidx.core.view.isGone import androidx.core.view.isVisible -import androidx.fragment.app.FragmentManager import androidx.fragment.app.activityViewModels import androidx.lifecycle.lifecycleScope import com.google.android.material.button.MaterialButtonToggleGroup @@ -19,6 +18,8 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.plus import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.AppRouter +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.ReaderMode @@ -26,15 +27,11 @@ import org.koitharu.kotatsu.core.prefs.observeAsStateFlow import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet import org.koitharu.kotatsu.core.util.ext.findParentCallback import org.koitharu.kotatsu.core.util.ext.observe -import org.koitharu.kotatsu.core.util.ext.showDistinct import org.koitharu.kotatsu.core.util.ext.viewLifecycleScope -import org.koitharu.kotatsu.core.util.ext.withArgs import org.koitharu.kotatsu.databinding.SheetReaderConfigBinding import org.koitharu.kotatsu.reader.domain.PageLoader import org.koitharu.kotatsu.reader.ui.ReaderViewModel import org.koitharu.kotatsu.reader.ui.ScreenOrientationHelper -import org.koitharu.kotatsu.reader.ui.colorfilter.ColorFilterConfigActivity -import org.koitharu.kotatsu.settings.SettingsActivity import javax.inject.Inject @AndroidEntryPoint @@ -64,7 +61,7 @@ class ReaderConfigSheet : override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - mode = arguments?.getInt(ARG_MODE) + mode = arguments?.getInt(AppRouter.KEY_READER_MODE) ?.let { ReaderMode.valueOf(it) } ?: ReaderMode.STANDARD imageServerDelegate = ImageServerDelegate( @@ -129,7 +126,7 @@ class ReaderConfigSheet : override fun onClick(v: View) { when (v.id) { R.id.button_settings -> { - startActivity(SettingsActivity.newReaderSettingsIntent(v.context)) + router.openReaderSettings() dismissAllowingStateLoss() } @@ -145,7 +142,7 @@ class ReaderConfigSheet : R.id.button_color_filter -> { val page = viewModel.getCurrentPage() ?: return val manga = viewModel.getMangaOrNull() ?: return - startActivity(ColorFilterConfigActivity.newIntent(v.context, manga, page)) + router.openColorFilterConfig(manga, page) } R.id.button_image_server -> viewLifecycleScope.launch { @@ -243,14 +240,4 @@ class ReaderConfigSheet : fun onSavePageClick() } - - companion object { - - private const val TAG = "ReaderConfigBottomSheet" - private const val ARG_MODE = "mode" - - fun show(fm: FragmentManager, mode: ReaderMode) = ReaderConfigSheet().withArgs(1) { - putInt(ARG_MODE, mode.id) - }.showDistinct(fm, TAG) - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt index d37907fc8..9a5fb85e4 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt @@ -80,7 +80,7 @@ class PageHolderDelegate( fun showErrorDetails(url: String?) { val e = error ?: return - exceptionResolver.showDetails(e, url) + exceptionResolver.showErrorDetails(e, url) } fun onAttachedToWindow() { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/remotelist/ui/RemoteListFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/remotelist/ui/RemoteListFragment.kt index 0caec63bb..66c70f98c 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/remotelist/ui/RemoteListFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/remotelist/ui/RemoteListFragment.kt @@ -12,8 +12,8 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.flow.drop import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.browser.BrowserActivity import org.koitharu.kotatsu.core.model.getTitle +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.list.ListSelectionController import org.koitharu.kotatsu.core.ui.util.MenuInvalidator import org.koitharu.kotatsu.core.util.ext.addMenuProvider @@ -22,12 +22,9 @@ import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.core.util.ext.withArgs import org.koitharu.kotatsu.databinding.FragmentListBinding -import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.filter.ui.FilterCoordinator -import org.koitharu.kotatsu.filter.ui.sheet.FilterSheetFragment import org.koitharu.kotatsu.list.ui.MangaListFragment import org.koitharu.kotatsu.parsers.model.MangaSource -import org.koitharu.kotatsu.settings.SettingsActivity @AndroidEntryPoint class RemoteListFragment : MangaListFragment(), FilterCoordinator.Owner { @@ -42,9 +39,7 @@ class RemoteListFragment : MangaListFragment(), FilterCoordinator.Owner { addMenuProvider(RemoteListMenuProvider()) addMenuProvider(MangaSearchMenuProvider(filterCoordinator, viewModel)) viewModel.isRandomLoading.observe(viewLifecycleOwner, MenuInvalidator(requireActivity())) - viewModel.onOpenManga.observeEvent(viewLifecycleOwner) { - startActivity(DetailsActivity.newIntent(binding.root.context, it)) - } + viewModel.onOpenManga.observeEvent(viewLifecycleOwner) { router.openDetails(it) } filterCoordinator.observe().distinctUntilChangedBy { it.listFilter.isEmpty() } .drop(1) .observe(viewLifecycleOwner) { @@ -66,7 +61,7 @@ class RemoteListFragment : MangaListFragment(), FilterCoordinator.Owner { } override fun onFilterClick(view: View?) { - FilterSheetFragment.show(getChildFragmentManager()) + router.showFilterSheet() } override fun onEmptyActionClick() { @@ -86,13 +81,10 @@ class RemoteListFragment : MangaListFragment(), FilterCoordinator.Owner { Snackbar.make(requireViewBinding().recyclerView, R.string.operation_not_supported, Snackbar.LENGTH_SHORT) .show() } else { - startActivity( - BrowserActivity.newIntent( - requireContext(), - url, - viewModel.source, - viewModel.source.getTitle(requireContext()), - ), + router.openBrowser( + url = url, + source = viewModel.source, + title = viewModel.source.getTitle(requireContext()), ) } } @@ -105,7 +97,7 @@ class RemoteListFragment : MangaListFragment(), FilterCoordinator.Owner { override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) { R.id.action_source_settings -> { - startActivity(SettingsActivity.newSourceSettingsIntent(requireContext(), viewModel.source)) + router.openSourceSettings(viewModel.source) true } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/config/ScrobblerConfigActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/config/ScrobblerConfigActivity.kt index a10a912f4..acf887f92 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/config/ScrobblerConfigActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/config/ScrobblerConfigActivity.kt @@ -1,6 +1,5 @@ package org.koitharu.kotatsu.scrobbling.common.ui.config -import android.content.Context import android.content.Intent import android.os.Bundle import android.view.View @@ -15,6 +14,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.util.ext.disposeImageRequest @@ -24,9 +24,7 @@ import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.core.util.ext.showOrHide import org.koitharu.kotatsu.databinding.ActivityScrobblerConfigBinding -import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration -import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerUser import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingInfo import org.koitharu.kotatsu.scrobbling.common.ui.config.adapter.ScrobblingMangaAdapter @@ -84,9 +82,7 @@ class ScrobblerConfigActivity : BaseActivity(), } override fun onItemClick(item: ScrobblingInfo, view: View) { - startActivity( - DetailsActivity.newIntent(this, item.mangaId), - ) + router.openDetails(item.mangaId) } override fun onClick(v: View) { @@ -133,16 +129,9 @@ class ScrobblerConfigActivity : BaseActivity(), } companion object { - - const val EXTRA_SERVICE_ID = "service" - const val HOST_SHIKIMORI_AUTH = "shikimori-auth" const val HOST_ANILIST_AUTH = "anilist-auth" const val HOST_MAL_AUTH = "mal-auth" const val HOST_KITSU_AUTH = "kitsu-auth" - - fun newIntent(context: Context, service: ScrobblerService) = - Intent(context, ScrobblerConfigActivity::class.java) - .putExtra(EXTRA_SERVICE_ID, service.id) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/config/ScrobblerConfigViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/config/ScrobblerConfigViewModel.kt index 1bf51ec23..ca5f614cc 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/config/ScrobblerConfigViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/config/ScrobblerConfigViewModel.kt @@ -15,7 +15,7 @@ import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.plus import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.core.ui.BaseActivity +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.ui.BaseViewModel import org.koitharu.kotatsu.core.util.ext.MutableEventFlow import org.koitharu.kotatsu.core.util.ext.call @@ -100,11 +100,11 @@ class ScrobblerConfigViewModel @Inject constructor( private fun getScrobblerService( savedStateHandle: SavedStateHandle, ): ScrobblerService { - val serviceId = savedStateHandle.get(ScrobblerConfigActivity.EXTRA_SERVICE_ID) ?: 0 + val serviceId = savedStateHandle.get(AppRouter.KEY_ID) ?: 0 if (serviceId != 0) { return ScrobblerService.entries.first { it.id == serviceId } } - val uri = savedStateHandle.require(BaseActivity.EXTRA_DATA) + val uri = savedStateHandle.require(AppRouter.KEY_DATA) return when (uri.host) { ScrobblerConfigActivity.HOST_SHIKIMORI_AUTH -> ScrobblerService.SHIKIMORI ScrobblerConfigActivity.HOST_ANILIST_AUTH -> ScrobblerService.ANILIST diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorSheet.kt b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorSheet.kt index f3334a338..c4b319de2 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorSheet.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorSheet.kt @@ -7,7 +7,6 @@ import android.view.View import android.view.ViewGroup import android.widget.Toast import androidx.appcompat.widget.SearchView -import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels import androidx.recyclerview.widget.AsyncListDiffer import androidx.recyclerview.widget.RecyclerView.NO_ID @@ -17,8 +16,7 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver -import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga -import org.koitharu.kotatsu.core.parser.MangaIntent +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.ui.list.PaginationScrollListener import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet @@ -31,15 +29,12 @@ import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.core.util.ext.setProgressIcon import org.koitharu.kotatsu.core.util.ext.setTabsEnabled import org.koitharu.kotatsu.core.util.ext.viewLifecycleScope -import org.koitharu.kotatsu.core.util.ext.withArgs import org.koitharu.kotatsu.databinding.SheetScrobblingSelectorBinding import org.koitharu.kotatsu.list.ui.adapter.ListStateHolderListener import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.model.LoadingFooter -import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerManga -import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService import org.koitharu.kotatsu.scrobbling.common.ui.selector.adapter.ScrobblerMangaSelectionDecoration import org.koitharu.kotatsu.scrobbling.common.ui.selector.adapter.ScrobblerSelectorAdapter import javax.inject.Inject @@ -229,7 +224,7 @@ class ScrobblingSelectorSheet : private fun initTabs() { val entries = viewModel.availableScrobblers val tabs = requireViewBinding().tabs - val selectedId = arguments?.getInt(ARG_SCROBBLER, -1) ?: -1 + val selectedId = arguments?.getInt(AppRouter.KEY_ID, -1) ?: -1 tabs.removeAllTabs() tabs.clearOnTabSelectedListeners() tabs.addOnTabSelectedListener(this) @@ -244,18 +239,4 @@ class ScrobblingSelectorSheet : } } } - - companion object { - - private const val TAG = "ScrobblingSelectorBottomSheet" - private const val ARG_SCROBBLER = "scrobbler" - - fun show(fm: FragmentManager, manga: Manga, scrobblerService: ScrobblerService?) = - ScrobblingSelectorSheet().withArgs(2) { - putParcelable(MangaIntent.KEY_MANGA, ParcelableManga(manga)) - if (scrobblerService != null) { - putInt(ARG_SCROBBLER, scrobblerService.id) - } - }.show(fm, TAG) - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorViewModel.kt index d6c016482..37d0f4384 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorViewModel.kt @@ -15,7 +15,7 @@ import kotlinx.coroutines.plus import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga -import org.koitharu.kotatsu.core.parser.MangaIntent +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.ui.BaseViewModel import org.koitharu.kotatsu.core.util.ext.MutableEventFlow import org.koitharu.kotatsu.core.util.ext.call @@ -41,7 +41,7 @@ class ScrobblingSelectorViewModel @Inject constructor( private val historyRepository: HistoryRepository, ) : BaseViewModel() { - val manga = savedStateHandle.require(MangaIntent.KEY_MANGA).manga + val manga = savedStateHandle.require(AppRouter.KEY_MANGA).manga val availableScrobblers = scrobblers.filter { it.isEnabled } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/kitsu/ui/KitsuAuthActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/kitsu/ui/KitsuAuthActivity.kt index a63a733c9..d75576b22 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/kitsu/ui/KitsuAuthActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/kitsu/ui/KitsuAuthActivity.kt @@ -1,5 +1,6 @@ package org.koitharu.kotatsu.scrobbling.kitsu.ui +import android.annotation.SuppressLint import android.content.Intent import android.os.Bundle import android.text.Editable @@ -55,6 +56,7 @@ class KitsuAuthActivity : BaseActivity(), View.OnClick && password.length >= 3 } + @SuppressLint("UnsafeImplicitIntentLaunch") private fun continueAuth() { val email = viewBinding.editEmail.text?.toString()?.trim().orEmpty() val password = viewBinding.editPassword.text?.toString()?.trim().orEmpty() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/MangaListActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/MangaListActivity.kt index 989828470..10d0ad6c3 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/MangaListActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/MangaListActivity.kt @@ -1,7 +1,5 @@ package org.koitharu.kotatsu.search.ui -import android.content.Context -import android.content.Intent import android.os.Bundle import android.view.View import android.view.ViewGroup.MarginLayoutParams @@ -19,7 +17,6 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map -import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.LocalMangaSource import org.koitharu.kotatsu.core.model.MangaSource @@ -28,7 +25,8 @@ import org.koitharu.kotatsu.core.model.getTitle import org.koitharu.kotatsu.core.model.isNsfw import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga import org.koitharu.kotatsu.core.model.parcelable.ParcelableMangaListFilter -import org.koitharu.kotatsu.core.parser.MangaIntent +import org.koitharu.kotatsu.core.nav.AppRouter +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.ui.model.titleRes import org.koitharu.kotatsu.core.util.ViewBadge @@ -46,7 +44,6 @@ import org.koitharu.kotatsu.main.ui.owners.AppBarOwner import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaListFilter import org.koitharu.kotatsu.parsers.model.MangaSource -import org.koitharu.kotatsu.parsers.util.isNullOrEmpty import org.koitharu.kotatsu.remotelist.ui.RemoteListFragment import kotlin.math.absoluteValue import com.google.android.material.R as materialR @@ -69,8 +66,8 @@ class MangaListActivity : override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(ActivityMangaListBinding.inflate(layoutInflater)) - val filter = intent.getParcelableExtraCompat(EXTRA_FILTER)?.filter - source = MangaSource(intent.getStringExtra(EXTRA_SOURCE)) + val filter = intent.getParcelableExtraCompat(AppRouter.KEY_FILTER)?.filter + source = MangaSource(intent.getStringExtra(AppRouter.KEY_SOURCE)) supportActionBar?.setDisplayHomeAsUpEnabled(true) if (viewBinding.containerFilterHeader != null) { viewBinding.appbar.addOnOffsetChangedListener(this) @@ -104,13 +101,13 @@ class MangaListActivity : override fun onClick(v: View) { when (v.id) { - R.id.button_order -> FilterSheetFragment.show(supportFragmentManager) + R.id.button_order -> router.showFilterSheet() } } fun showPreview(manga: Manga): Boolean = setSideFragment( PreviewFragment::class.java, - bundleOf(MangaIntent.KEY_MANGA to ParcelableManga(manga)), + bundleOf(AppRouter.KEY_MANGA to ParcelableManga(manga)), ) fun hidePreview() = setSideFragment(FilterSheetFragment::class.java, null) @@ -193,21 +190,4 @@ class MangaListActivity : filterOwner.filterCoordinator.set(filter) } } - - companion object { - - private const val EXTRA_FILTER = "filter" - private const val EXTRA_SOURCE = "source" - private const val ACTION_MANGA_EXPLORE = "${BuildConfig.APPLICATION_ID}.action.EXPLORE_MANGA" - - fun newIntent(context: Context, source: MangaSource, filter: MangaListFilter?): Intent = - Intent(context, MangaListActivity::class.java) - .setAction(ACTION_MANGA_EXPLORE) - .putExtra(EXTRA_SOURCE, source.name) - .apply { - if (!filter.isNullOrEmpty()) { - putExtra(EXTRA_FILTER, ParcelableMangaListFilter(filter)) - } - } - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchActivity.kt index a6fcda873..13b6e4460 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchActivity.kt @@ -1,7 +1,5 @@ package org.koitharu.kotatsu.search.ui.multi -import android.content.Context -import android.content.Intent import android.os.Bundle import android.view.Menu import android.view.MenuInflater @@ -15,6 +13,7 @@ import coil3.ImageLoader import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.ui.list.ListSelectionController @@ -25,9 +24,6 @@ import org.koitharu.kotatsu.core.util.ext.invalidateNestedItemDecorations import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.databinding.ActivitySearchBinding -import org.koitharu.kotatsu.details.ui.DetailsActivity -import org.koitharu.kotatsu.download.ui.dialog.DownloadDialogFragment -import org.koitharu.kotatsu.favourites.ui.categories.select.FavoriteDialog import org.koitharu.kotatsu.list.domain.ListFilterOption import org.koitharu.kotatsu.list.ui.MangaSelectionDecoration import org.koitharu.kotatsu.list.ui.adapter.MangaListListener @@ -35,10 +31,7 @@ import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration import org.koitharu.kotatsu.list.ui.model.ListHeader import org.koitharu.kotatsu.list.ui.size.DynamicItemSizeResolver import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaListFilter import org.koitharu.kotatsu.parsers.model.MangaTag -import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder -import org.koitharu.kotatsu.search.ui.MangaListActivity import org.koitharu.kotatsu.search.ui.multi.adapter.SearchAdapter import javax.inject.Inject @@ -62,14 +55,8 @@ class SearchActivity : setContentView(ActivitySearchBinding.inflate(layoutInflater)) title = viewModel.query - val itemCLickListener = OnListItemClickListener { item, view -> - startActivity( - MangaListActivity.newIntent( - view.context, - item.source, - MangaListFilter(query = viewModel.query), - ), - ) + val itemClickListener = OnListItemClickListener { item, view -> + router.openSearch(item.source, viewModel.query) } val sizeResolver = DynamicItemSizeResolver(resources, settings, adjustWidth = true) val selectionDecoration = MangaSelectionDecoration(this) @@ -83,7 +70,7 @@ class SearchActivity : lifecycleOwner = this, coil = coil, listener = this, - itemClickListener = itemCLickListener, + itemClickListener = itemClickListener, sizeResolver = sizeResolver, selectionDecoration = selectionDecoration, ) @@ -98,8 +85,6 @@ class SearchActivity : viewModel.list.observe(this, adapter) viewModel.onError.observeEvent(this, SnackbarErrorObserver(viewBinding.recyclerView, null)) - - DownloadDialogFragment.registerCallback(this, viewBinding.recyclerView) } override fun onWindowInsetsChanged(insets: Insets) { @@ -114,8 +99,7 @@ class SearchActivity : override fun onItemClick(item: Manga, view: View) { if (!selectionController.onItemClick(item.id)) { - val intent = DetailsActivity.newIntent(this, item) - startActivity(intent) + router.openDetails(item) } } @@ -129,15 +113,13 @@ class SearchActivity : override fun onReadClick(manga: Manga, view: View) { if (!selectionController.onItemClick(manga.id)) { - val intent = IntentBuilder(this).manga(manga).build() - startActivity(intent) + router.openReader(manga) } } override fun onTagClick(manga: Manga, tag: MangaTag, view: View) { if (!selectionController.onItemClick(manga.id)) { - val intent = MangaListActivity.newIntent(this, manga.source, MangaListFilter(tags = setOf(tag))) - startActivity(intent) + router.openList(tag) } } @@ -179,13 +161,13 @@ class SearchActivity : } R.id.action_favourite -> { - FavoriteDialog.show(supportFragmentManager, collectSelectedItems()) + router.showFavoriteDialog(collectSelectedItems()) mode?.finish() true } R.id.action_save -> { - DownloadDialogFragment.show(supportFragmentManager, collectSelectedItems()) + router.showDownloadDialog(collectSelectedItems(), viewBinding.recyclerView) mode?.finish() true } @@ -197,13 +179,4 @@ class SearchActivity : private fun collectSelectedItems(): Set { return viewModel.getItems(selectionController.peekCheckedIds()) } - - companion object { - - const val EXTRA_QUERY = "query" - - fun newIntent(context: Context, query: String) = - Intent(context, SearchActivity::class.java) - .putExtra(EXTRA_QUERY, query) - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchViewModel.kt index c4cb96024..ac5cbef66 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchViewModel.kt @@ -25,6 +25,7 @@ import kotlinx.coroutines.sync.withPermit import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.LocalMangaSource import org.koitharu.kotatsu.core.model.UnknownMangaSource +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.prefs.ListMode import org.koitharu.kotatsu.core.ui.BaseViewModel @@ -58,7 +59,7 @@ class SearchViewModel @Inject constructor( private val favouritesRepository: FavouritesRepository, ) : BaseViewModel() { - val query = savedStateHandle.get(SearchActivity.EXTRA_QUERY).orEmpty() + val query = savedStateHandle.get(AppRouter.KEY_QUERY).orEmpty() private val retryCounter = MutableStateFlow(0) private val listData = retryCounter.flatMapLatest { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/SearchSuggestionFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/SearchSuggestionFragment.kt index 49b7d467c..084d20671 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/SearchSuggestionFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/SearchSuggestionFragment.kt @@ -71,16 +71,4 @@ class SearchSuggestionFragment : super.onResume() viewModel.onResume() } - - companion object { - - @Deprecated( - "", - ReplaceWith( - "SearchSuggestionFragment()", - "org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionFragment", - ), - ) - fun newInstance() = SearchSuggestionFragment() - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/DownloadsSettingsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/DownloadsSettingsFragment.kt index eece51d8f..1df4e1d4f 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/DownloadsSettingsFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/DownloadsSettingsFragment.kt @@ -15,6 +15,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.DownloadFormat import org.koitharu.kotatsu.core.prefs.TriStateOption @@ -27,8 +28,6 @@ import org.koitharu.kotatsu.core.util.ext.viewLifecycleScope import org.koitharu.kotatsu.download.ui.worker.DownloadWorker import org.koitharu.kotatsu.local.data.LocalStorageManager import org.koitharu.kotatsu.parsers.util.names -import org.koitharu.kotatsu.settings.storage.MangaDirectorySelectDialog -import org.koitharu.kotatsu.settings.storage.directories.MangaDirectoriesActivity import org.koitharu.kotatsu.settings.utils.DozeHelper import javax.inject.Inject @@ -98,12 +97,12 @@ class DownloadsSettingsFragment : override fun onPreferenceTreeClick(preference: Preference): Boolean { return when (preference.key) { AppSettings.KEY_LOCAL_STORAGE -> { - MangaDirectorySelectDialog.show(childFragmentManager) + router.showDirectorySelectDialog() true } AppSettings.KEY_LOCAL_MANGA_DIRS -> { - startActivity(MangaDirectoriesActivity.newIntent(preference.context)) + router.openDirectoriesSettings() true } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/ReaderSettingsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/ReaderSettingsFragment.kt index 052a1e4c1..c71ca7ab8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/ReaderSettingsFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/ReaderSettingsFragment.kt @@ -1,6 +1,5 @@ package org.koitharu.kotatsu.settings -import android.content.Intent import android.content.SharedPreferences import android.content.pm.ActivityInfo import android.os.Bundle @@ -11,6 +10,7 @@ import androidx.preference.Preference import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.ZoomMode +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.ReaderAnimation import org.koitharu.kotatsu.core.prefs.ReaderBackground @@ -18,7 +18,6 @@ import org.koitharu.kotatsu.core.prefs.ReaderMode import org.koitharu.kotatsu.core.ui.BasePreferenceFragment import org.koitharu.kotatsu.core.util.ext.setDefaultValueCompat import org.koitharu.kotatsu.parsers.util.names -import org.koitharu.kotatsu.settings.reader.ReaderTapGridConfigActivity import org.koitharu.kotatsu.settings.utils.MultiSummaryProvider import org.koitharu.kotatsu.settings.utils.PercentSummaryProvider import org.koitharu.kotatsu.settings.utils.SliderPreference @@ -80,7 +79,7 @@ class ReaderSettingsFragment : override fun onPreferenceTreeClick(preference: Preference): Boolean { return when (preference.key) { AppSettings.KEY_READER_TAP_ACTIONS -> { - startActivity(Intent(preference.context, ReaderTapGridConfigActivity::class.java)) + router.openReaderTapGridSettings() true } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/ServicesSettingsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/ServicesSettingsFragment.kt index af1f7a465..556cb481a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/ServicesSettingsFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/ServicesSettingsFragment.kt @@ -1,7 +1,6 @@ package org.koitharu.kotatsu.settings import android.accounts.AccountManager -import android.content.Intent import android.content.SharedPreferences import android.os.Bundle import android.view.View @@ -12,6 +11,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.BasePreferenceFragment import org.koitharu.kotatsu.core.ui.dialog.buildAlertDialog @@ -20,11 +20,8 @@ import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.core.util.ext.viewLifecycleScope import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService import org.koitharu.kotatsu.scrobbling.common.ui.ScrobblerAuthHelper -import org.koitharu.kotatsu.scrobbling.common.ui.config.ScrobblerConfigActivity import org.koitharu.kotatsu.settings.utils.SplitSwitchPreference -import org.koitharu.kotatsu.stats.ui.StatsActivity import org.koitharu.kotatsu.sync.domain.SyncController -import org.koitharu.kotatsu.sync.ui.SyncSettingsIntent import javax.inject.Inject @AndroidEntryPoint @@ -41,7 +38,7 @@ class ServicesSettingsFragment : BasePreferenceFragment(R.string.services), addPreferencesFromResource(R.xml.pref_services) findPreference(AppSettings.KEY_STATS_ENABLED)?.let { it.onContainerClickListener = Preference.OnPreferenceClickListener { - it.context.startActivity(Intent(it.context, StatsActivity::class.java)) + router.openStatistic() true } } @@ -105,7 +102,9 @@ class ServicesSettingsFragment : BasePreferenceFragment(R.string.services), if (account == null) { am.addAccount(accountType, accountType, null, null, requireActivity(), null, null) } else { - startActivitySafe(SyncSettingsIntent(account)) + if (!router.openSystemSyncSettings(account)) { + Snackbar.make(listView, R.string.operation_not_supported, Snackbar.LENGTH_SHORT).show() + } } true } @@ -146,7 +145,7 @@ class ServicesSettingsFragment : BasePreferenceFragment(R.string.services), if (!scrobblerAuthHelper.isAuthorized(scrobblerService)) { confirmScrobblerAuth(scrobblerService) } else { - startActivity(ScrobblerConfigActivity.newIntent(context ?: return, scrobblerService)) + router.openScrobblerSettings(scrobblerService) } } 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 c554de7b1..8ed5dd06f 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt @@ -1,10 +1,7 @@ package org.koitharu.kotatsu.settings -import android.content.Context import android.content.Intent -import android.net.Uri import android.os.Bundle -import android.provider.Settings import android.view.ViewGroup.MarginLayoutParams import androidx.activity.viewModels import androidx.core.graphics.Insets @@ -19,24 +16,22 @@ import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import com.google.android.material.appbar.AppBarLayout import dagger.hilt.android.AndroidEntryPoint -import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.MangaSource -import org.koitharu.kotatsu.core.model.MangaSourceInfo -import org.koitharu.kotatsu.core.parser.external.ExternalMangaSource +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.core.util.ext.textAndVisible import org.koitharu.kotatsu.databinding.ActivitySettingsBinding import org.koitharu.kotatsu.main.ui.owners.AppBarOwner -import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.settings.about.AboutSettingsFragment import org.koitharu.kotatsu.settings.search.SettingsItem import org.koitharu.kotatsu.settings.search.SettingsSearchFragment import org.koitharu.kotatsu.settings.search.SettingsSearchMenuProvider import org.koitharu.kotatsu.settings.search.SettingsSearchViewModel import org.koitharu.kotatsu.settings.sources.SourceSettingsFragment +import org.koitharu.kotatsu.settings.sources.SourceSettingsFragment.Companion.EXTRA_SOURCE import org.koitharu.kotatsu.settings.sources.SourcesSettingsFragment import org.koitharu.kotatsu.settings.sources.manage.SourcesManageFragment import org.koitharu.kotatsu.settings.tracker.TrackerSettingsFragment @@ -145,18 +140,18 @@ class SettingsActivity : private fun openDefaultFragment() { val fragment = when (intent?.action) { - ACTION_READER -> ReaderSettingsFragment() - ACTION_SUGGESTIONS -> SuggestionsSettingsFragment() - ACTION_HISTORY -> UserDataSettingsFragment() - ACTION_TRACKER -> TrackerSettingsFragment() - ACTION_SOURCES -> SourcesSettingsFragment() - ACTION_PROXY -> ProxySettingsFragment() - ACTION_MANAGE_DOWNLOADS -> DownloadsSettingsFragment() - ACTION_SOURCE -> SourceSettingsFragment.newInstance( + AppRouter.ACTION_READER -> ReaderSettingsFragment() + AppRouter.ACTION_SUGGESTIONS -> SuggestionsSettingsFragment() + AppRouter.ACTION_HISTORY -> UserDataSettingsFragment() + AppRouter.ACTION_TRACKER -> TrackerSettingsFragment() + AppRouter.ACTION_SOURCES -> SourcesSettingsFragment() + AppRouter.ACTION_PROXY -> ProxySettingsFragment() + AppRouter.ACTION_MANAGE_DOWNLOADS -> DownloadsSettingsFragment() + AppRouter.ACTION_SOURCE -> SourceSettingsFragment.newInstance( MangaSource(intent.getStringExtra(EXTRA_SOURCE)), ) - ACTION_MANAGE_SOURCES -> SourcesManageFragment() + AppRouter.ACTION_MANAGE_SOURCES -> SourcesManageFragment() Intent.ACTION_VIEW -> { when (intent.data?.host) { HOST_ABOUT -> AboutSettingsFragment() @@ -181,62 +176,8 @@ class SettingsActivity : companion object { - private const val ACTION_READER = "${BuildConfig.APPLICATION_ID}.action.MANAGE_READER_SETTINGS" - private const val ACTION_SUGGESTIONS = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SUGGESTIONS" - private const val ACTION_TRACKER = "${BuildConfig.APPLICATION_ID}.action.MANAGE_TRACKER" - private const val ACTION_HISTORY = "${BuildConfig.APPLICATION_ID}.action.MANAGE_HISTORY" - private const val ACTION_SOURCE = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SOURCE_SETTINGS" - private const val ACTION_SOURCES = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SOURCES" - private const val ACTION_MANAGE_SOURCES = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SOURCES_LIST" - private const val ACTION_MANAGE_DOWNLOADS = "${BuildConfig.APPLICATION_ID}.action.MANAGE_DOWNLOADS" - private const val ACTION_PROXY = "${BuildConfig.APPLICATION_ID}.action.MANAGE_PROXY" - private const val EXTRA_SOURCE = "source" private const val HOST_ABOUT = "about" private const val HOST_SYNC_SETTINGS = "sync-settings" const val ARG_PREF_KEY = "pref_key" - - fun newIntent(context: Context) = Intent(context, SettingsActivity::class.java) - - fun newReaderSettingsIntent(context: Context) = - Intent(context, SettingsActivity::class.java) - .setAction(ACTION_READER) - - fun newSuggestionsSettingsIntent(context: Context) = - Intent(context, SettingsActivity::class.java) - .setAction(ACTION_SUGGESTIONS) - - fun newTrackerSettingsIntent(context: Context) = - Intent(context, SettingsActivity::class.java) - .setAction(ACTION_TRACKER) - - fun newProxySettingsIntent(context: Context) = - Intent(context, SettingsActivity::class.java) - .setAction(ACTION_PROXY) - - fun newHistorySettingsIntent(context: Context) = - Intent(context, SettingsActivity::class.java) - .setAction(ACTION_HISTORY) - - fun newSourcesSettingsIntent(context: Context) = - Intent(context, SettingsActivity::class.java) - .setAction(ACTION_SOURCES) - - fun newManageSourcesIntent(context: Context) = - Intent(context, SettingsActivity::class.java) - .setAction(ACTION_MANAGE_SOURCES) - - fun newDownloadsSettingsIntent(context: Context) = - Intent(context, SettingsActivity::class.java) - .setAction(ACTION_MANAGE_DOWNLOADS) - - fun newSourceSettingsIntent(context: Context, source: MangaSource): Intent = when (source) { - is MangaSourceInfo -> newSourceSettingsIntent(context, source.mangaSource) - is ExternalMangaSource -> Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) - .setData(Uri.fromParts("package", source.packageName, null)) - - else -> Intent(context, SettingsActivity::class.java) - .setAction(ACTION_SOURCE) - .putExtra(EXTRA_SOURCE, source.name) - } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/about/AboutSettingsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/about/AboutSettingsFragment.kt index 5419fed7a..f3c877b02 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/about/AboutSettingsFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/about/AboutSettingsFragment.kt @@ -4,7 +4,6 @@ import android.content.Intent import android.os.Bundle import android.view.View import androidx.annotation.StringRes -import androidx.core.net.toUri import androidx.fragment.app.viewModels import androidx.preference.Preference import androidx.preference.SwitchPreferenceCompat @@ -15,6 +14,7 @@ import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.github.AppVersion import org.koitharu.kotatsu.core.github.VersionId import org.koitharu.kotatsu.core.github.isStable +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.BasePreferenceFragment import org.koitharu.kotatsu.core.util.ext.observe @@ -87,15 +87,13 @@ class AboutSettingsFragment : BasePreferenceFragment(R.string.about) { } } - private fun openLink(@StringRes url: Int, title: CharSequence?): Boolean { - val intent = Intent(Intent.ACTION_VIEW) - intent.data = getString(url).toUri() - return startActivitySafe( - if (title != null) { - Intent.createChooser(intent, title) - } else { - intent - }, - ) + private fun openLink( + @StringRes url: Int, + title: CharSequence? + ): Boolean = if (router.openExternalBrowser(getString(url), title)) { + true + } else { + Snackbar.make(listView, R.string.operation_not_supported, Snackbar.LENGTH_SHORT).show() + false } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/about/AppUpdateActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/about/AppUpdateActivity.kt index 01bdc2b4a..d28971610 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/about/AppUpdateActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/about/AppUpdateActivity.kt @@ -15,8 +15,8 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.core.content.ContextCompat import androidx.core.graphics.Insets -import androidx.core.net.toUri import androidx.core.text.buildSpannedString +import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint import io.noties.markwon.Markwon import kotlinx.coroutines.Dispatchers @@ -24,6 +24,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.withContext import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.github.AppVersion +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.util.FileSize import org.koitharu.kotatsu.core.util.ext.getDisplayMessage @@ -137,8 +138,9 @@ class AppUpdateActivity : BaseActivity(), View.OnClick private fun openInBrowser() { val latestVersion = viewModel.nextVersion.value ?: return - val intent = Intent(Intent.ACTION_VIEW, latestVersion.url.toUri()) - startActivity(Intent.createChooser(intent, getString(R.string.open_in_browser))) + if (!router.openExternalBrowser(latestVersion.url, getString(R.string.open_in_browser))) { + Snackbar.make(viewBinding.scrollView, R.string.operation_not_supported, Snackbar.LENGTH_SHORT).show() + } } private fun onProgressChanged(value: Pair) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/backup/BackupDialogFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/backup/BackupDialogFragment.kt index b40157ab0..1bae292a9 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/backup/BackupDialogFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/backup/BackupDialogFragment.kt @@ -7,7 +7,6 @@ import android.view.ViewGroup import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.core.view.isVisible -import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint @@ -103,13 +102,4 @@ class BackupDialogFragment : AlertDialogFragment() { onError(e) } } - - companion object { - - private const val TAG = "BackupDialogFragment" - - fun show(fm: FragmentManager) { - BackupDialogFragment().show(fm, TAG) - } - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/backup/PeriodicalBackupSettingsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/backup/PeriodicalBackupSettingsFragment.kt index 7adbd748f..6b5cd7b7e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/backup/PeriodicalBackupSettingsFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/backup/PeriodicalBackupSettingsFragment.kt @@ -10,10 +10,12 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.fragment.app.viewModels import androidx.preference.EditTextPreference import androidx.preference.Preference +import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.backup.TelegramBackupUploader import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.BasePreferenceFragment import org.koitharu.kotatsu.core.util.ext.observe @@ -51,16 +53,20 @@ class PeriodicalBackupSettingsFragment : BasePreferenceFragment(R.string.periodi } override fun onPreferenceTreeClick(preference: Preference): Boolean { - return when (preference.key) { + val result = when (preference.key) { AppSettings.KEY_BACKUP_PERIODICAL_OUTPUT -> outputSelectCall.tryLaunch(null) - AppSettings.KEY_BACKUP_TG_OPEN -> telegramBackupUploader.openBotInApp(preference.context) + AppSettings.KEY_BACKUP_TG_OPEN -> telegramBackupUploader.openBotInApp(router) AppSettings.KEY_BACKUP_TG_TEST -> { viewModel.checkTelegram() true } - else -> super.onPreferenceTreeClick(preference) + else -> return super.onPreferenceTreeClick(preference) } + if (!result) { + Snackbar.make(listView, R.string.operation_not_supported, Snackbar.LENGTH_SHORT).show() + } + return true } override fun onActivityResult(result: Uri?) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/backup/RestoreDialogFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/backup/RestoreDialogFragment.kt index 11e16e4e2..330eb9e05 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/backup/RestoreDialogFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/backup/RestoreDialogFragment.kt @@ -1,28 +1,25 @@ 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.core.view.isGone import androidx.core.view.isVisible -import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.combine import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.backup.CompositeResult +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.AlertDialogFragment import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.util.ext.getDisplayMessage import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.core.util.ext.textAndVisible -import org.koitharu.kotatsu.core.util.ext.withArgs import org.koitharu.kotatsu.databinding.DialogRestoreBinding -import org.koitharu.kotatsu.main.ui.welcome.WelcomeSheet import java.text.DateFormat import java.text.SimpleDateFormat import java.util.Date @@ -143,21 +140,8 @@ class RestoreDialogFragment : AlertDialogFragment(), OnLis builder.setPositiveButton(android.R.string.ok, null) .show() if (!result.isEmpty && !result.isAllFailed) { - WelcomeSheet.dismiss(parentFragmentManager) + router.closeWelcomeSheet() } dismiss() } - - - companion object { - - const val ARG_FILE = "file" - private const val TAG = "RestoreDialogFragment" - - fun show(fm: FragmentManager, uri: Uri) { - RestoreDialogFragment().withArgs(1) { - putString(ARG_FILE, uri.toString()) - }.show(fm, TAG) - } - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/backup/RestoreViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/backup/RestoreViewModel.kt index 6aa77ded3..67ac872ae 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/backup/RestoreViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/backup/RestoreViewModel.kt @@ -11,6 +11,7 @@ import org.koitharu.kotatsu.core.backup.BackupEntry import org.koitharu.kotatsu.core.backup.BackupRepository import org.koitharu.kotatsu.core.backup.BackupZipInput import org.koitharu.kotatsu.core.backup.CompositeResult +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.ui.BaseViewModel import org.koitharu.kotatsu.core.util.ext.MutableEventFlow import org.koitharu.kotatsu.core.util.ext.call @@ -32,7 +33,7 @@ class RestoreViewModel @Inject constructor( ) : BaseViewModel() { private val backupInput = suspendLazy { - val uri = savedStateHandle.get(RestoreDialogFragment.ARG_FILE) + val uri = savedStateHandle.get(AppRouter.KEY_FILE) ?.toUriOrNull() ?: throw FileNotFoundException() val contentResolver = context.contentResolver runInterruptible(Dispatchers.IO) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourceSettingsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourceSettingsFragment.kt index 88d62e1b5..5bd148025 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourceSettingsFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourceSettingsFragment.kt @@ -7,9 +7,9 @@ import androidx.preference.Preference import androidx.preference.SwitchPreferenceCompat import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.browser.BrowserActivity import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver import org.koitharu.kotatsu.core.model.getTitle +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.parser.EmptyMangaRepository import org.koitharu.kotatsu.core.parser.ParserMangaRepository import org.koitharu.kotatsu.core.prefs.AppSettings @@ -20,7 +20,6 @@ import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.core.util.ext.withArgs import org.koitharu.kotatsu.parsers.model.MangaSource -import org.koitharu.kotatsu.settings.sources.auth.SourceAuthActivity import java.io.File @AndroidEntryPoint @@ -87,18 +86,15 @@ class SourceSettingsFragment : BasePreferenceFragment(0), Preference.OnPreferenc override fun onPreferenceTreeClick(preference: Preference): Boolean { return when (preference.key) { KEY_AUTH -> { - startActivity(SourceAuthActivity.newIntent(preference.context, viewModel.source)) + router.openSourceAuth(viewModel.source) true } AppSettings.KEY_OPEN_BROWSER -> { - startActivity( - BrowserActivity.newIntent( - context = preference.context, - url = viewModel.browserUrl.value ?: return false, - source = viewModel.source, - title = viewModel.source.getTitle(preference.context), - ), + router.openBrowser( + url = viewModel.browserUrl.value ?: return false, + source = viewModel.source, + title = viewModel.source.getTitle(preference.context), ) true } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesSettingsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesSettingsFragment.kt index 49ed190bc..1e493ce81 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesSettingsFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesSettingsFragment.kt @@ -1,6 +1,5 @@ package org.koitharu.kotatsu.settings.sources -import android.content.Intent import android.os.Bundle import android.view.View import androidx.fragment.app.viewModels @@ -9,13 +8,13 @@ import androidx.preference.Preference import androidx.preference.TwoStatePreference import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.BasePreferenceFragment import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.setDefaultValueCompat import org.koitharu.kotatsu.explore.data.SourcesSortOrder import org.koitharu.kotatsu.parsers.util.names -import org.koitharu.kotatsu.settings.sources.catalog.SourcesCatalogActivity @AndroidEntryPoint class SourcesSettingsFragment : BasePreferenceFragment(R.string.remote_sources) { @@ -60,7 +59,7 @@ class SourcesSettingsFragment : BasePreferenceFragment(R.string.remote_sources) override fun onPreferenceTreeClick(preference: Preference): Boolean = when (preference.key) { AppSettings.KEY_SOURCES_CATALOG -> { - startActivity(Intent(preference.context, SourcesCatalogActivity::class.java)) + router.openSourcesCatalog() true } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt index ea285a8a4..a135c5ca6 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt @@ -1,7 +1,6 @@ package org.koitharu.kotatsu.settings.sources.auth import android.annotation.SuppressLint -import android.app.Activity import android.content.Context import android.content.Intent import android.os.Bundle @@ -19,6 +18,7 @@ import org.koitharu.kotatsu.browser.BrowserClient import org.koitharu.kotatsu.browser.ProgressChromeClient import org.koitharu.kotatsu.browser.WebViewBackPressedCallback import org.koitharu.kotatsu.core.model.MangaSource +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.network.CommonHeaders import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.ParserMangaRepository @@ -28,6 +28,7 @@ import org.koitharu.kotatsu.databinding.ActivityBrowserBinding import org.koitharu.kotatsu.parsers.MangaParserAuthProvider import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.settings.sources.SourceSettingsFragment.Companion.EXTRA_SOURCE import javax.inject.Inject import com.google.android.material.R as materialR @@ -90,7 +91,7 @@ class SourceAuthActivity : BaseActivity(), BrowserCallba override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { android.R.id.home -> { viewBinding.webView.stopLoading() - setResult(Activity.RESULT_CANCELED) + setResult(RESULT_CANCELED) finishAfterTransition() true } @@ -112,7 +113,7 @@ class SourceAuthActivity : BaseActivity(), BrowserCallba viewBinding.progressBar.isVisible = isLoading if (!isLoading && authProvider.isAuthorized) { Toast.makeText(this, R.string.auth_complete, Toast.LENGTH_SHORT).show() - setResult(Activity.RESULT_OK) + setResult(RESULT_OK) finishAfterTransition() } } @@ -133,7 +134,7 @@ class SourceAuthActivity : BaseActivity(), BrowserCallba class Contract : ActivityResultContract() { override fun createIntent(context: Context, input: MangaSource): Intent { - return newIntent(context, input) + return AppRouter.sourceAuthIntent(context, input) } override fun parseResult(resultCode: Int, intent: Intent?): Boolean { @@ -142,13 +143,6 @@ class SourceAuthActivity : BaseActivity(), BrowserCallba } companion object { - - private const val EXTRA_SOURCE = "source" const val TAG = "SourceAuthActivity" - - fun newIntent(context: Context, source: MangaSource): Intent { - return Intent(context, SourceAuthActivity::class.java) - .putExtra(EXTRA_SOURCE, source.name) - } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogActivity.kt index bb4b71200..1fa7bda4a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogActivity.kt @@ -16,6 +16,7 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.combine import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.titleResId +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.ui.util.FadingAppbarMediator @@ -31,7 +32,6 @@ import org.koitharu.kotatsu.databinding.ActivitySourcesCatalogBinding import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration import org.koitharu.kotatsu.main.ui.owners.AppBarOwner import org.koitharu.kotatsu.parsers.model.ContentType -import org.koitharu.kotatsu.search.ui.MangaListActivity import javax.inject.Inject @AndroidEntryPoint @@ -89,7 +89,7 @@ class SourcesCatalogActivity : BaseActivity(), } override fun onItemClick(item: SourceCatalogItem.Source, view: View) { - startActivity(MangaListActivity.newIntent(this, item.source, null)) + router.openList(item.source, null) } override fun onItemLongClick(item: SourceCatalogItem.Source, view: View): Boolean { 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 07bcc8921..3bc0d3cf5 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 @@ -1,6 +1,5 @@ package org.koitharu.kotatsu.settings.sources.manage -import android.content.Intent import android.os.Bundle import android.view.LayoutInflater import android.view.Menu @@ -18,6 +17,7 @@ import coil3.ImageLoader import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.os.AppShortcutManager import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.BaseFragment @@ -34,7 +34,6 @@ import org.koitharu.kotatsu.settings.SettingsActivity import org.koitharu.kotatsu.settings.sources.SourceSettingsFragment import org.koitharu.kotatsu.settings.sources.adapter.SourceConfigAdapter import org.koitharu.kotatsu.settings.sources.adapter.SourceConfigListener -import org.koitharu.kotatsu.settings.sources.catalog.SourcesCatalogActivity import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem import javax.inject.Inject @@ -152,7 +151,7 @@ class SourcesManageFragment : override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) { R.id.action_catalog -> { - startActivity(Intent(context, SourcesCatalogActivity::class.java)) + router.openSourcesCatalog() true } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/storage/MangaDirectorySelectDialog.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/storage/MangaDirectorySelectDialog.kt index f9294c3e0..ecf5b0e78 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/storage/MangaDirectorySelectDialog.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/storage/MangaDirectorySelectDialog.kt @@ -8,7 +8,6 @@ import android.view.View import android.view.ViewGroup import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts -import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter @@ -19,7 +18,6 @@ import org.koitharu.kotatsu.core.ui.AlertDialogFragment import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent -import org.koitharu.kotatsu.core.util.ext.showDistinct import org.koitharu.kotatsu.core.util.ext.tryLaunch import org.koitharu.kotatsu.databinding.DialogDirectorySelectBinding @@ -80,12 +78,4 @@ class MangaDirectorySelectDialog : AlertDialogFragment() bottom = insets.bottom, ) } - - companion object { - - fun newIntent(context: Context) = Intent(context, MangaDirectoriesActivity::class.java) - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/tracker/TrackerSettingsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/tracker/TrackerSettingsFragment.kt index cb0ccd202..08105be06 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/tracker/TrackerSettingsFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/tracker/TrackerSettingsFragment.kt @@ -1,5 +1,6 @@ package org.koitharu.kotatsu.settings.tracker +import android.content.ActivityNotFoundException import android.content.Intent import android.content.SharedPreferences import android.net.Uri @@ -14,15 +15,16 @@ import androidx.fragment.app.viewModels import androidx.preference.ListPreference import androidx.preference.MultiSelectListPreference import androidx.preference.Preference +import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.TrackerDownloadStrategy import org.koitharu.kotatsu.core.ui.BasePreferenceFragment import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.setDefaultValueCompat import org.koitharu.kotatsu.parsers.util.names -import org.koitharu.kotatsu.settings.tracker.categories.TrackerCategoriesConfigSheet import org.koitharu.kotatsu.settings.utils.DozeHelper import org.koitharu.kotatsu.settings.utils.MultiSummaryProvider import org.koitharu.kotatsu.tracker.ui.debug.TrackerDebugActivity @@ -108,7 +110,7 @@ class TrackerSettingsFragment : } AppSettings.KEY_TRACK_CATEGORIES -> { - TrackerCategoriesConfigSheet.show(childFragmentManager) + router.showTrackerCategoriesConfigSheet() true } @@ -147,4 +149,12 @@ class TrackerSettingsFragment : getString(R.string.enabled_d_of_d, count[0], count[1]) } } + + private fun startActivitySafe(intent: Intent): Boolean = try { + startActivity(intent) + true + } catch (_: ActivityNotFoundException) { + Snackbar.make(listView, R.string.operation_not_supported, Snackbar.LENGTH_SHORT).show() + false + } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/tracker/categories/TrackerCategoriesConfigSheet.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/tracker/categories/TrackerCategoriesConfigSheet.kt index 3e0a07e34..d32a578cb 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/tracker/categories/TrackerCategoriesConfigSheet.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/tracker/categories/TrackerCategoriesConfigSheet.kt @@ -4,7 +4,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R @@ -37,11 +36,4 @@ class TrackerCategoriesConfigSheet : override fun onItemClick(item: FavouriteCategory, view: View) { viewModel.toggleItem(item) } - - companion object { - - private const val TAG = "TrackerCategoriesConfigSheet" - - fun show(fm: FragmentManager) = TrackerCategoriesConfigSheet().show(fm, TAG) - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/userdata/UserDataSettingsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/userdata/UserDataSettingsFragment.kt index e85a1beee..b3188c3a4 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/userdata/UserDataSettingsFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/userdata/UserDataSettingsFragment.kt @@ -8,21 +8,19 @@ import android.view.View import androidx.activity.result.ActivityResultCallback import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatDelegate -import androidx.collection.ArraySet import androidx.core.view.postDelayed import androidx.fragment.app.viewModels import androidx.preference.ListPreference import androidx.preference.MultiSelectListPreference import androidx.preference.Preference import androidx.preference.TwoStatePreference -import androidx.preference.forEach -import androidx.preference.get import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.StateFlow import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.os.AppShortcutManager import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.ScreenshotsPolicy @@ -38,8 +36,6 @@ import org.koitharu.kotatsu.core.util.ext.tryLaunch import org.koitharu.kotatsu.local.data.CacheDir import org.koitharu.kotatsu.parsers.util.mapToSet import org.koitharu.kotatsu.parsers.util.names -import org.koitharu.kotatsu.settings.backup.BackupDialogFragment -import org.koitharu.kotatsu.settings.backup.RestoreDialogFragment import org.koitharu.kotatsu.settings.protect.ProtectSetupActivity import org.koitharu.kotatsu.settings.utils.MultiSummaryProvider import javax.inject.Inject @@ -163,7 +159,7 @@ class UserDataSettingsFragment : BasePreferenceFragment(R.string.data_and_privac } AppSettings.KEY_BACKUP -> { - BackupDialogFragment.show(childFragmentManager) + router.showBackupCreateDialog() true } @@ -215,7 +211,7 @@ class UserDataSettingsFragment : BasePreferenceFragment(R.string.data_and_privac override fun onActivityResult(result: Uri?) { if (result != null) { - RestoreDialogFragment.show(childFragmentManager, result) + router.showBackupRestoreDialog(result) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/stats/ui/StatsActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/stats/ui/StatsActivity.kt index 141e9e487..7da0dfa9b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/stats/ui/StatsActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/stats/ui/StatsActivity.kt @@ -18,6 +18,7 @@ import com.google.android.material.chip.ChipDrawable import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.FavouriteCategory +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.ui.BaseListAdapter import org.koitharu.kotatsu.core.ui.dialog.buildAlertDialog @@ -36,7 +37,6 @@ import org.koitharu.kotatsu.list.ui.adapter.ListItemType import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.stats.domain.StatsPeriod import org.koitharu.kotatsu.stats.domain.StatsRecord -import org.koitharu.kotatsu.stats.ui.sheet.MangaStatsSheet import org.koitharu.kotatsu.stats.ui.views.PieChartView import javax.inject.Inject @@ -103,7 +103,7 @@ class StatsActivity : BaseActivity(), } override fun onItemClick(item: Manga, view: View) { - MangaStatsSheet.show(supportFragmentManager, item) + router.showStatisticSheet(item) } override fun onSegmentClick(view: PieChartView, segment: PieChartView.Segment) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/stats/ui/sheet/MangaStatsSheet.kt b/app/src/main/kotlin/org/koitharu/kotatsu/stats/ui/sheet/MangaStatsSheet.kt index 7c12c195c..ae00e4486 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/stats/ui/sheet/MangaStatsSheet.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/stats/ui/sheet/MangaStatsSheet.kt @@ -5,20 +5,15 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.collection.IntList -import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet import org.koitharu.kotatsu.core.util.KotatsuColors import org.koitharu.kotatsu.core.util.ext.observe -import org.koitharu.kotatsu.core.util.ext.showDistinct import org.koitharu.kotatsu.core.util.ext.textAndVisible -import org.koitharu.kotatsu.core.util.ext.withArgs import org.koitharu.kotatsu.databinding.SheetStatsMangaBinding -import org.koitharu.kotatsu.details.ui.DetailsActivity -import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.util.format import org.koitharu.kotatsu.stats.ui.views.BarChartView @@ -46,7 +41,7 @@ class MangaStatsSheet : BaseAdaptiveSheet(), View.OnClic } override fun onClick(v: View) { - startActivity(DetailsActivity.newIntent(v.context, viewModel.manga)) + router.openDetails(viewModel.manga) } private fun onStatsChanged(stats: IntList) { @@ -66,17 +61,4 @@ class MangaStatsSheet : BaseAdaptiveSheet(), View.OnClic } chartView.setData(bars) } - - companion object { - - const val ARG_MANGA = "manga" - - private const val TAG = "MangaStatsSheet" - - fun show(fm: FragmentManager, manga: Manga) { - MangaStatsSheet().withArgs(1) { - putParcelable(ARG_MANGA, ParcelableManga(manga)) - }.showDistinct(fm, TAG) - } - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/stats/ui/sheet/MangaStatsViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/stats/ui/sheet/MangaStatsViewModel.kt index 9fc1f782b..7d0133608 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/stats/ui/sheet/MangaStatsViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/stats/ui/sheet/MangaStatsViewModel.kt @@ -8,6 +8,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.ui.BaseViewModel import org.koitharu.kotatsu.core.ui.model.DateTimeAgo import org.koitharu.kotatsu.core.util.ext.calculateTimeAgo @@ -23,7 +24,7 @@ class MangaStatsViewModel @Inject constructor( private val repository: StatsRepository, ) : BaseViewModel() { - val manga = savedStateHandle.require(MangaStatsSheet.ARG_MANGA).manga + val manga = savedStateHandle.require(AppRouter.KEY_MANGA).manga val stats = MutableStateFlow(emptyIntList()) val startDate = MutableStateFlow(null) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsActivity.kt index 207091960..890ebfa87 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsActivity.kt @@ -1,7 +1,5 @@ package org.koitharu.kotatsu.suggestions.ui -import android.content.Context -import android.content.Intent import android.os.Bundle import androidx.core.graphics.Insets import androidx.core.view.updatePadding @@ -40,9 +38,4 @@ class SuggestionsActivity : right = insets.right, ) } - - companion object { - - fun newIntent(context: Context) = Intent(context, SuggestionsActivity::class.java) - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsFragment.kt index 92e192e1d..e29e969b8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsFragment.kt @@ -8,11 +8,11 @@ import androidx.core.view.MenuProvider import androidx.fragment.app.viewModels import com.google.android.material.snackbar.Snackbar import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.list.ListSelectionController import org.koitharu.kotatsu.core.util.ext.addMenuProvider import org.koitharu.kotatsu.databinding.FragmentListBinding import org.koitharu.kotatsu.list.ui.MangaListFragment -import org.koitharu.kotatsu.settings.SettingsActivity class SuggestionsFragment : MangaListFragment() { @@ -59,7 +59,7 @@ class SuggestionsFragment : MangaListFragment() { } R.id.action_settings_suggestions -> { - startActivity(SettingsActivity.newSuggestionsSettingsIntent(requireContext())) + router.openSuggestionsSettings() true } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsWorker.kt b/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsWorker.kt index d7bfd3b67..8e05eabc2 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsWorker.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsWorker.kt @@ -49,6 +49,8 @@ import org.koitharu.kotatsu.browser.cloudflare.CaptchaNotifier import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException import org.koitharu.kotatsu.core.model.distinctById import org.koitharu.kotatsu.core.model.isNsfw +import org.koitharu.kotatsu.core.nav.AppRouter +import org.koitharu.kotatsu.core.nav.ReaderIntent import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.util.ext.almostEquals @@ -64,7 +66,6 @@ import org.koitharu.kotatsu.core.util.ext.sizeOrZero import org.koitharu.kotatsu.core.util.ext.takeMostFrequent import org.koitharu.kotatsu.core.util.ext.toBitmapOrNull import org.koitharu.kotatsu.core.util.ext.trySetForeground -import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.explore.data.MangaSourcesRepository import org.koitharu.kotatsu.favourites.domain.FavouritesRepository import org.koitharu.kotatsu.history.data.HistoryRepository @@ -74,8 +75,6 @@ import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.util.runCatchingCancellable -import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder -import org.koitharu.kotatsu.settings.SettingsActivity import org.koitharu.kotatsu.settings.work.PeriodicWorkScheduler import org.koitharu.kotatsu.suggestions.domain.MangaSuggestion import org.koitharu.kotatsu.suggestions.domain.SuggestionRepository @@ -130,7 +129,7 @@ class SuggestionsWorker @AssistedInject constructor( PendingIntentCompat.getActivity( applicationContext, 0, - SettingsActivity.newSuggestionsSettingsIntent(applicationContext), + AppRouter.suggestionsSettingsIntent(applicationContext), 0, false, ), @@ -326,7 +325,7 @@ class SuggestionsWorker @AssistedInject constructor( style.setBigContentTitle(title) setStyle(style) } - val intent = DetailsActivity.newIntent(applicationContext, manga) + val intent = AppRouter.detailsIntent(applicationContext, manga) setContentIntent( PendingIntentCompat.getActivity( applicationContext, @@ -348,7 +347,7 @@ class SuggestionsWorker @AssistedInject constructor( PendingIntentCompat.getActivity( applicationContext, id + 2, - IntentBuilder(applicationContext).manga(manga).build(), + ReaderIntent.Builder(applicationContext).manga(manga).build().intent, 0, false, ), @@ -360,7 +359,7 @@ class SuggestionsWorker @AssistedInject constructor( PendingIntentCompat.getActivity( applicationContext, 0, - SuggestionsActivity.newIntent(applicationContext), + AppRouter.suggestionsIntent(applicationContext), 0, false, ), diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/sync/ui/SyncSettingsIntent.kt b/app/src/main/kotlin/org/koitharu/kotatsu/sync/ui/SyncSettingsIntent.kt deleted file mode 100644 index 2775d8072..000000000 --- a/app/src/main/kotlin/org/koitharu/kotatsu/sync/ui/SyncSettingsIntent.kt +++ /dev/null @@ -1,18 +0,0 @@ -package org.koitharu.kotatsu.sync.ui - -import android.accounts.Account -import android.content.Intent -import android.os.Bundle - -private const val ACCOUNT_KEY = "account" -private const val ACTION_ACCOUNT_SYNC_SETTINGS = "android.settings.ACCOUNT_SYNC_SETTINGS" -private const val EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args" - -@Suppress("FunctionName") -fun SyncSettingsIntent(account: Account): Intent { - val args = Bundle(1) - args.putParcelable(ACCOUNT_KEY, account) - val intent = Intent(ACTION_ACCOUNT_SYNC_SETTINGS) - intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args) - return intent -} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/debug/TrackerDebugActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/debug/TrackerDebugActivity.kt index 18d02dd3e..2d1dff299 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/debug/TrackerDebugActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/debug/TrackerDebugActivity.kt @@ -7,12 +7,12 @@ import androidx.core.graphics.Insets import androidx.core.view.updatePadding import coil3.ImageLoader import dagger.hilt.android.AndroidEntryPoint +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.ui.BaseListAdapter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.databinding.ActivityTrackerDebugBinding -import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.list.ui.adapter.ListItemType import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration import javax.inject.Inject @@ -53,6 +53,6 @@ class TrackerDebugActivity : BaseActivity(), OnList } override fun onItemClick(item: TrackDebugItem, view: View) { - startActivity(DetailsActivity.newIntent(this, item.manga)) + router.openDetails(item.manga) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/FeedFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/FeedFragment.kt index 6fb310599..838b3a9c8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/FeedFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/FeedFragment.kt @@ -16,6 +16,7 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.drop import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver +import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.ui.BaseFragment import org.koitharu.kotatsu.core.ui.list.PaginationScrollListener import org.koitharu.kotatsu.core.ui.list.RecyclerScrollKeeper @@ -25,7 +26,6 @@ import org.koitharu.kotatsu.core.util.ext.addMenuProvider import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.databinding.FragmentListBinding -import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.list.domain.ListFilterOption import org.koitharu.kotatsu.list.ui.adapter.MangaListListener import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration @@ -35,7 +35,6 @@ import org.koitharu.kotatsu.main.ui.owners.BottomNavOwner import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.tracker.ui.feed.adapter.FeedAdapter -import org.koitharu.kotatsu.tracker.ui.updates.UpdatesActivity import javax.inject.Inject @AndroidEntryPoint @@ -105,8 +104,7 @@ class FeedFragment : override fun onSecondaryButtonClick(tipView: TipView) = Unit override fun onListHeaderClick(item: ListHeader, view: View) { - val context = view.context - context.startActivity(UpdatesActivity.newIntent(context)) + router.openMangaUpdates() } private fun onFeedCleared() { @@ -128,7 +126,7 @@ class FeedFragment : } override fun onItemClick(item: Manga, view: View) { - startActivity(DetailsActivity.newIntent(context ?: return, item)) + router.openDetails(item) } override fun onReadClick(manga: Manga, view: View) = Unit diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/updates/UpdatesActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/updates/UpdatesActivity.kt index 047b0d7ca..238c6becb 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/updates/UpdatesActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/updates/UpdatesActivity.kt @@ -1,7 +1,5 @@ package org.koitharu.kotatsu.tracker.ui.updates -import android.content.Context -import android.content.Intent import android.os.Bundle import androidx.core.graphics.Insets import androidx.core.view.updatePadding @@ -41,9 +39,4 @@ class UpdatesActivity : right = insets.right, ) } - - companion object { - - fun newIntent(context: Context) = Intent(context, UpdatesActivity::class.java) - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/work/TrackWorker.kt b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/work/TrackWorker.kt index 8f2e55dc8..323bb1f4b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/work/TrackWorker.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/work/TrackWorker.kt @@ -46,6 +46,7 @@ import org.koitharu.kotatsu.browser.cloudflare.CaptchaNotifier import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException import org.koitharu.kotatsu.core.model.ids +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.TrackerDownloadStrategy import org.koitharu.kotatsu.core.prefs.TriStateOption @@ -59,7 +60,6 @@ import org.koitharu.kotatsu.download.ui.worker.DownloadWorker import org.koitharu.kotatsu.local.data.LocalMangaRepository import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import org.koitharu.kotatsu.parsers.util.toIntUp -import org.koitharu.kotatsu.settings.SettingsActivity import org.koitharu.kotatsu.settings.work.PeriodicWorkScheduler import org.koitharu.kotatsu.tracker.domain.CheckNewChaptersUseCase import org.koitharu.kotatsu.tracker.domain.GetTracksUseCase @@ -209,7 +209,7 @@ class TrackWorker @AssistedInject constructor( PendingIntentCompat.getActivity( applicationContext, 0, - SettingsActivity.newTrackerSettingsIntent(applicationContext), + AppRouter.trackerSettingsIntent(applicationContext), 0, false, ), diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/work/TrackerNotificationHelper.kt b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/work/TrackerNotificationHelper.kt index dc18149a5..daab52ec1 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/work/TrackerNotificationHelper.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/work/TrackerNotificationHelper.kt @@ -16,14 +16,13 @@ import coil3.ImageLoader import coil3.request.ImageRequest import dagger.hilt.android.qualifiers.ApplicationContext import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.util.ext.checkNotificationPermission import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.toBitmapOrNull -import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaChapter -import org.koitharu.kotatsu.tracker.ui.updates.UpdatesActivity import javax.inject.Inject class TrackerNotificationHelper @Inject constructor( @@ -81,7 +80,7 @@ class TrackerNotificationHelper @Inject constructor( style.setSummaryText(manga.title) style.setBigContentTitle(summary) setStyle(style) - val intent = DetailsActivity.newIntent(applicationContext, manga) + val intent = AppRouter.detailsIntent(applicationContext, manga) setContentIntent( PendingIntentCompat.getActivity( applicationContext, @@ -126,7 +125,7 @@ class TrackerNotificationHelper @Inject constructor( setNumber(newChaptersCount) setGroup(GROUP_NEW_CHAPTERS) setGroupSummary(true) - val intent = UpdatesActivity.newIntent(applicationContext) + val intent = AppRouter.mangaUpdatesIntent(applicationContext) setContentIntent( PendingIntentCompat.getActivity( applicationContext, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/widget/recent/RecentListFactory.kt b/app/src/main/kotlin/org/koitharu/kotatsu/widget/recent/RecentListFactory.kt index a4e047999..4359d1e9d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/widget/recent/RecentListFactory.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/widget/recent/RecentListFactory.kt @@ -14,7 +14,7 @@ import coil3.transform.RoundedCornersTransformation import dagger.Lazy import kotlinx.coroutines.runBlocking import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.core.parser.MangaIntent +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.util.ext.getDrawableOrThrow import org.koitharu.kotatsu.core.util.ext.mangaExtra @@ -74,7 +74,7 @@ class RecentListFactory( views.setImageViewResource(R.id.imageView_cover, R.drawable.ic_placeholder) } val intent = Intent() - intent.putExtra(MangaIntent.KEY_ID, item.id) + intent.putExtra(AppRouter.KEY_ID, item.id) views.setOnClickFillInIntent(R.id.imageView_cover, intent) return views } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/widget/recent/RecentWidgetProvider.kt b/app/src/main/kotlin/org/koitharu/kotatsu/widget/recent/RecentWidgetProvider.kt index c94e449b9..380eb1cca 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/widget/recent/RecentWidgetProvider.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/widget/recent/RecentWidgetProvider.kt @@ -9,6 +9,7 @@ import android.net.Uri import android.widget.RemoteViews import androidx.core.app.PendingIntentCompat import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.ReaderIntent import org.koitharu.kotatsu.core.prefs.AppWidgetConfig import org.koitharu.kotatsu.core.ui.BaseAppWidgetProvider import org.koitharu.kotatsu.reader.ui.ReaderActivity @@ -36,7 +37,7 @@ class RecentWidgetProvider : BaseAppWidgetProvider() { adapter.data = Uri.parse(adapter.toUri(Intent.URI_INTENT_SCHEME)) views.setRemoteAdapter(R.id.stackView, adapter) val intent = Intent(context, ReaderActivity::class.java) - intent.action = ReaderActivity.ACTION_MANGA_READ + intent.action = ReaderIntent.ACTION_MANGA_READ views.setPendingIntentTemplate( R.id.stackView, PendingIntentCompat.getActivity( diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/widget/shelf/ShelfListFactory.kt b/app/src/main/kotlin/org/koitharu/kotatsu/widget/shelf/ShelfListFactory.kt index 26d4cee95..a6747ffe1 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/widget/shelf/ShelfListFactory.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/widget/shelf/ShelfListFactory.kt @@ -14,7 +14,7 @@ import coil3.transform.RoundedCornersTransformation import dagger.Lazy import kotlinx.coroutines.runBlocking import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.core.parser.MangaIntent +import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppWidgetConfig import org.koitharu.kotatsu.core.ui.image.TrimTransformation @@ -85,7 +85,7 @@ class ShelfListFactory( views.setImageViewResource(R.id.imageView_cover, R.drawable.ic_placeholder) } val intent = Intent() - intent.putExtra(MangaIntent.KEY_ID, item.id) + intent.putExtra(AppRouter.KEY_ID, item.id) views.setOnClickFillInIntent(R.id.rootLayout, intent) return views } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/widget/shelf/ShelfWidgetProvider.kt b/app/src/main/kotlin/org/koitharu/kotatsu/widget/shelf/ShelfWidgetProvider.kt index 5246ecd3f..f56e11e0d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/widget/shelf/ShelfWidgetProvider.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/widget/shelf/ShelfWidgetProvider.kt @@ -9,6 +9,7 @@ import android.net.Uri import android.widget.RemoteViews import androidx.core.app.PendingIntentCompat import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.nav.ReaderIntent import org.koitharu.kotatsu.core.prefs.AppWidgetConfig import org.koitharu.kotatsu.core.ui.BaseAppWidgetProvider import org.koitharu.kotatsu.reader.ui.ReaderActivity @@ -36,7 +37,7 @@ class ShelfWidgetProvider : BaseAppWidgetProvider() { adapter.data = Uri.parse(adapter.toUri(Intent.URI_INTENT_SCHEME)) views.setRemoteAdapter(R.id.gridView, adapter) val intent = Intent(context, ReaderActivity::class.java) - intent.action = ReaderActivity.ACTION_MANGA_READ + intent.action = ReaderIntent.ACTION_MANGA_READ views.setPendingIntentTemplate( R.id.gridView, PendingIntentCompat.getActivity(