Refactor navigation
This commit is contained in:
@@ -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<ActivityAlternativesBinding>(),
|
||||
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<ActivityAlternativesBinding>(),
|
||||
|
||||
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<ActivityAlternativesBinding>(),
|
||||
}
|
||||
}.show()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun newIntent(context: Context, manga: Manga) = Intent(context, AlternativesActivity::class.java)
|
||||
.putExtra(MangaIntent.KEY_MANGA, ParcelableManga(manga))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ParcelableManga>(MangaIntent.KEY_MANGA).manga
|
||||
val manga = savedStateHandle.require<ParcelableManga>(AppRouter.KEY_MANGA).manga
|
||||
|
||||
val onMigrated = MutableEventFlow<Manga>()
|
||||
val content = MutableStateFlow<List<ListModel>>(listOf(LoadingState))
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ActivityBrowserBinding>(), 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<ActivityBrowserBinding>(), 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<ActivityBrowserBinding>(), 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<ActivityBrowserBinding>(), 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<ActivityBrowserBinding>(), 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<ActivityBrowserBinding>(), 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<ActivityBrowserBinding>(), CloudFlareCal
|
||||
|
||||
class Contract : ActivityResultContract<CloudFlareProtectedException, Boolean>() {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Throwable>(EXTRA_ERROR) ?: return
|
||||
val e = intent?.getSerializableExtraCompat<Throwable>(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()
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
602
app/src/main/kotlin/org/koitharu/kotatsu/core/nav/AppRouter.kt
Normal file
602
app/src/main/kotlin/org/koitharu/kotatsu/core/nav/AppRouter.kt
Normal file
@@ -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<Manga>, 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<Manga>) {
|
||||
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<ChaptersPagesSheet>()) as? ChaptersPagesSheet
|
||||
return sheet?.dialog?.isShowing == true
|
||||
}
|
||||
|
||||
fun closeWelcomeSheet(): Boolean {
|
||||
val fm = fragment?.parentFragmentManager ?: activity?.supportFragmentManager ?: return false
|
||||
val sheet = fm.findFragmentByTag(fragmentTag<WelcomeSheet>()) 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<out Activity>) {
|
||||
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<Fragment>())
|
||||
}.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<out Fragment>.fragmentTag() = name // TODO
|
||||
|
||||
private inline fun <reified F : Fragment> fragmentTag() = F::class.java.fragmentTag()
|
||||
}
|
||||
}
|
||||
@@ -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<ParcelableManga>(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)
|
||||
}
|
||||
}
|
||||
39
app/src/main/kotlin/org/koitharu/kotatsu/core/nav/NavUtil.kt
Normal file
39
app/src/main/kotlin/org/koitharu/kotatsu/core/nav/NavUtil.kt
Normal file
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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<B : ViewBinding> :
|
||||
override fun isNsfwContent(): Flow<Boolean> = 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<B : ViewBinding> :
|
||||
}
|
||||
|
||||
protected fun hasViewBinding() = ::viewBinding.isInitialized
|
||||
|
||||
companion object {
|
||||
|
||||
const val EXTRA_DATA = "data"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Preference>(key)
|
||||
if (pref == null) {
|
||||
|
||||
@@ -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<DialogErrorDetailsBinding>() {
|
||||
@@ -27,7 +27,7 @@ class ErrorDetailsDialog : AlertDialogFragment<DialogErrorDetailsBinding>() {
|
||||
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<DialogErrorDetailsBinding>() {
|
||||
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<DialogErrorDetailsBinding>() {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <I> ActivityResultLauncher<I>.resolve(context: Context, input: I): ResolveInfo? {
|
||||
val pm = context.packageManager
|
||||
val intent = contract.createIntent(context, input)
|
||||
return pm.resolveActivity(intent, 0)
|
||||
}
|
||||
|
||||
@CheckResult
|
||||
fun <I> ActivityResultLauncher<I>.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) {
|
||||
|
||||
@@ -110,3 +110,5 @@ fun <T : Parcelable> Parcelable.Creator<T>.unmarshall(bytes: ByteArray): T {
|
||||
parcel.recycle()
|
||||
}
|
||||
}
|
||||
|
||||
inline fun buildBundle(capacity: Int, block: Bundle.() -> Unit): Bundle = Bundle(capacity).apply(block)
|
||||
|
||||
@@ -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 : Fragment> 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 <T> Fragment.findParentCallback(cls: Class<T>): T? {
|
||||
val parent = parentFragment
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package org.koitharu.kotatsu.core.util.iterator
|
||||
|
||||
import org.koitharu.kotatsu.R
|
||||
|
||||
class MappingIterator<T, R>(
|
||||
private val upstream: Iterator<T>,
|
||||
private val mapper: (T) -> R,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<SheetChaptersPagesBinding>(), 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<SheetChaptersPagesBinding>(), 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<FragmentMangaBookmarksBinding>(),
|
||||
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) {
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<ParcelableManga>(MangaIntent.KEY_MANGA).manga
|
||||
private val seed = savedStateHandle.require<ParcelableManga>(AppRouter.KEY_MANGA).manga
|
||||
private val repository = mangaRepositoryFactory.create(seed.source)
|
||||
private val mangaList = MutableStateFlow<List<Manga>?>(null)
|
||||
private val listError = MutableStateFlow<Throwable?>(null)
|
||||
|
||||
@@ -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<ActivityContainerBinding>(), AppBarOwner {
|
||||
@@ -41,10 +36,4 @@ class RelatedMangaActivity : BaseActivity<ActivityContainerBinding>(), AppBarOwn
|
||||
right = insets.right,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun newIntent(context: Context, seed: Manga) = Intent(context, RelatedMangaActivity::class.java)
|
||||
.putExtra(MangaIntent.KEY_MANGA, ParcelableManga(seed))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ScrobblingInfo, ListModel, ItemScrobblingInfoBinding>(
|
||||
{ layoutInflater, parent -> ItemScrobblingInfoBinding.inflate(layoutInflater, parent, false) },
|
||||
) {
|
||||
binding.root.setOnClickListener {
|
||||
ScrobblingInfoSheet.show(fragmentManager, bindingAdapterPosition)
|
||||
router.showScrobblingInfoSheet(bindingAdapterPosition)
|
||||
}
|
||||
|
||||
bind {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ListModel>() {
|
||||
|
||||
init {
|
||||
delegatesManager.addDelegate(scrobblingInfoAD(lifecycleOwner, coil, fragmentManager))
|
||||
delegatesManager.addDelegate(scrobblingInfoAD(lifecycleOwner, coil, router))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<DialogDownloadBinding>(), 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<DialogDownloadBinding>(), 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<DialogDownloadBinding>(), 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<Manga>) = 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Array<ParcelableManga>>(DownloadDialogFragment.ARG_MANGA).map {
|
||||
val manga = savedStateHandle.require<Array<ParcelableManga>>(AppRouter.KEY_MANGA).map {
|
||||
it.manga
|
||||
}
|
||||
private val mangaDetails = suspendLazy {
|
||||
|
||||
@@ -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<ActivityDownloadsBinding>(),
|
||||
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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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<ActivityContainerBinding>() {
|
||||
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<ActivityContainerBinding>() {
|
||||
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<ActivityContainerBinding>() {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<ListSortOrder>(KEY_SORT_ORDER)?.let {
|
||||
selectedSortOrder = it
|
||||
savedInstanceState.getSerializableCompat<ListSortOrder>(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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Unit>()
|
||||
val category = MutableStateFlow<FavouriteCategory?>(null)
|
||||
|
||||
@@ -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<SheetFavoriteCategoriesBinding>(),
|
||||
OnListItemClickListener<MangaCategoryItem>, DialogInterface.OnClickListener {
|
||||
|
||||
private val viewModel by viewModels<FavoriteSheetViewModel>()
|
||||
private val viewModel by viewModels<FavoriteDialogViewModel>()
|
||||
|
||||
@Inject
|
||||
lateinit var coil: ImageLoader
|
||||
@@ -84,7 +78,7 @@ class FavoriteDialog : AlertDialogFragment<SheetFavoriteCategoriesBinding>(),
|
||||
}
|
||||
|
||||
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<SheetFavoriteCategoriesBinding>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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<Manga>) = FavoriteDialog().withArgs(1) {
|
||||
putParcelableArrayList(
|
||||
KEY_MANGA_LIST,
|
||||
manga.mapTo(ArrayList(manga.size), ::ParcelableManga),
|
||||
)
|
||||
}.showDistinct(fm, TAG)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<List<ParcelableManga>>(FavoriteDialog.KEY_MANGA_LIST).map {
|
||||
val manga = savedStateHandle.require<List<ParcelableManga>>(AppRouter.KEY_MANGA_LIST).map {
|
||||
it.manga
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<FragmentFavouritesContainerBind
|
||||
TabLayoutMediator(
|
||||
binding.tabs,
|
||||
binding.pager,
|
||||
FavouritesTabConfigurationStrategy(pagerAdapter, viewModel),
|
||||
FavouritesTabConfigurationStrategy(pagerAdapter, viewModel, router),
|
||||
).attach()
|
||||
binding.stubEmpty.setOnInflateListener(this)
|
||||
actionModeDelegate.addListener(this)
|
||||
viewModel.categories.observe(viewLifecycleOwner, pagerAdapter)
|
||||
viewModel.isEmpty.observe(viewLifecycleOwner, ::onEmptyStateChanged)
|
||||
addMenuProvider(FavouritesContainerMenuProvider(binding.root.context))
|
||||
addMenuProvider(FavouritesContainerMenuProvider(router))
|
||||
viewModel.onActionDone.observeEvent(viewLifecycleOwner, ReversibleActionObserver(binding.pager))
|
||||
}
|
||||
|
||||
@@ -102,9 +101,7 @@ class FavouritesContainerFragment : BaseFragment<FragmentFavouritesContainerBind
|
||||
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.button_retry -> startActivity(
|
||||
Intent(v.context, FavouriteCategoriesActivity::class.java),
|
||||
)
|
||||
R.id.button_retry -> router.openFavoriteCategories()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<FragmentFilterHeaderBinding>(), ChipsV
|
||||
when (data) {
|
||||
is MangaTag -> filter.toggleTag(data, !chip.isChecked)
|
||||
is String -> Unit
|
||||
null -> TagsCatalogSheet.show(parentFragmentManager, isExcludeTag = false)
|
||||
null -> router.showTagsCatalogSheet(excludeMode = false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<SheetFilterBinding>(),
|
||||
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<SheetFilterBinding>(),
|
||||
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<SheetFilterBinding>(),
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<SheetTagsBinding>(), OnListItemClickL
|
||||
defaultViewModelCreationExtras.withCreationCallback<TagsCatalogViewModel.Factory> { 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<SheetTagsBinding>(), 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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<ActivityImageBinding>(), 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<ActivityImageBinding>(), 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<ActivityImageBinding>(), 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Uri>(BaseActivity.EXTRA_DATA))
|
||||
.data(savedStateHandle.require<Uri>(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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ListConfigSection>(ListConfigBottomSheet.ARG_SECTION)
|
||||
val section = savedStateHandle.require<ListConfigSection>(AppRouter.KEY_LIST_SECTION)
|
||||
|
||||
var listMode: ListMode
|
||||
get() = when (section) {
|
||||
|
||||
@@ -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<FragmentPreviewBinding>(), 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<FragmentPreviewBinding>(), 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()
|
||||
|
||||
@@ -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<ParcelableManga>(MangaIntent.KEY_MANGA).manga,
|
||||
savedStateHandle.require<ParcelableManga>(AppRouter.KEY_MANGA).manga,
|
||||
)
|
||||
|
||||
val footer = combine(
|
||||
|
||||
@@ -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<DialogImportBinding>(), 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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<DialogLocalInfoBinding>(), 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ParcelableManga>(LocalInfoDialog.ARG_MANGA).manga
|
||||
private val manga = savedStateHandle.require<ParcelableManga>(AppRouter.KEY_MANGA).manga
|
||||
|
||||
val isCleaningUp = MutableStateFlow(false)
|
||||
val onCleanedUp = MutableEventFlow<Pair<Int, Long>>()
|
||||
|
||||
@@ -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<ActivityMainBinding>(), 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<ActivityMainBinding>(), AppBarOwner, BottomNav
|
||||
}
|
||||
|
||||
R.id.action_settings -> {
|
||||
startActivity(SettingsActivity.newIntent(this))
|
||||
router.openSettings()
|
||||
true
|
||||
}
|
||||
|
||||
@@ -198,7 +188,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), AppBarOwner, BottomNav
|
||||
}
|
||||
|
||||
R.id.action_app_update -> {
|
||||
startActivity(Intent(this, AppUpdateActivity::class.java))
|
||||
router.openAppUpdate()
|
||||
true
|
||||
}
|
||||
|
||||
@@ -241,7 +231,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), 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<ActivityMainBinding>(), 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<ActivityMainBinding>(), 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<ActivityMainBinding>(), 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<ActivityMainBinding>(), 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) {
|
||||
|
||||
@@ -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<SheetWelcomeBinding>(), ChipsView.OnChipC
|
||||
|
||||
override fun onActivityResult(result: Uri?) {
|
||||
if (result != null) {
|
||||
RestoreDialogFragment.show(parentFragmentManager, result)
|
||||
router.showBackupRestoreDialog(result)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,17 +109,4 @@ class WelcomeSheet : BaseAdaptiveSheet<SheetWelcomeBinding>(), 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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<String>(ReaderActivity.EXTRA_BRANCH)
|
||||
readingState.value = savedStateHandle[ReaderActivity.EXTRA_STATE]
|
||||
selectedBranch.value = savedStateHandle.get<String>(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<Int>()
|
||||
val uiState = MutableStateFlow<ReaderUiState?>(null)
|
||||
|
||||
val incognitoMode = if (savedStateHandle.get<Boolean>(ReaderActivity.EXTRA_INCOGNITO) == true) {
|
||||
val incognitoMode = if (savedStateHandle.get<Boolean>(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
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ParcelableManga>(EXTRA_MANGA).manga
|
||||
private val manga = savedStateHandle.require<ParcelableManga>(AppRouter.KEY_MANGA).manga
|
||||
|
||||
private var initialColorFilter: ReaderColorFilter? = null
|
||||
val colorFilter = MutableStateFlow<ReaderColorFilter?>(null)
|
||||
val onDismiss = MutableEventFlow<Unit>()
|
||||
val preview = savedStateHandle.require<ParcelableMangaPage>(ColorFilterConfigActivity.EXTRA_PAGES).page
|
||||
val preview = savedStateHandle.require<ParcelableMangaPage>(AppRouter.KEY_PAGES).page
|
||||
|
||||
val isChanged: Boolean
|
||||
get() = colorFilter.value != initialColorFilter
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ class PageHolderDelegate(
|
||||
|
||||
fun showErrorDetails(url: String?) {
|
||||
val e = error ?: return
|
||||
exceptionResolver.showDetails(e, url)
|
||||
exceptionResolver.showErrorDetails(e, url)
|
||||
}
|
||||
|
||||
fun onAttachedToWindow() {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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<ActivityScrobblerConfigBinding>(),
|
||||
}
|
||||
|
||||
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<ActivityScrobblerConfigBinding>(),
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Int>(ScrobblerConfigActivity.EXTRA_SERVICE_ID) ?: 0
|
||||
val serviceId = savedStateHandle.get<Int>(AppRouter.KEY_ID) ?: 0
|
||||
if (serviceId != 0) {
|
||||
return ScrobblerService.entries.first { it.id == serviceId }
|
||||
}
|
||||
val uri = savedStateHandle.require<Uri>(BaseActivity.EXTRA_DATA)
|
||||
val uri = savedStateHandle.require<Uri>(AppRouter.KEY_DATA)
|
||||
return when (uri.host) {
|
||||
ScrobblerConfigActivity.HOST_SHIKIMORI_AUTH -> ScrobblerService.SHIKIMORI
|
||||
ScrobblerConfigActivity.HOST_ANILIST_AUTH -> ScrobblerService.ANILIST
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ParcelableManga>(MangaIntent.KEY_MANGA).manga
|
||||
val manga = savedStateHandle.require<ParcelableManga>(AppRouter.KEY_MANGA).manga
|
||||
|
||||
val availableScrobblers = scrobblers.filter { it.isEnabled }
|
||||
|
||||
|
||||
@@ -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<ActivityKitsuAuthBinding>(), 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()
|
||||
|
||||
@@ -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<ParcelableMangaListFilter>(EXTRA_FILTER)?.filter
|
||||
source = MangaSource(intent.getStringExtra(EXTRA_SOURCE))
|
||||
val filter = intent.getParcelableExtraCompat<ParcelableMangaListFilter>(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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<SearchResultsListModel> { item, view ->
|
||||
startActivity(
|
||||
MangaListActivity.newIntent(
|
||||
view.context,
|
||||
item.source,
|
||||
MangaListFilter(query = viewModel.query),
|
||||
),
|
||||
)
|
||||
val itemClickListener = OnListItemClickListener<SearchResultsListModel> { 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<Manga> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String>(SearchActivity.EXTRA_QUERY).orEmpty()
|
||||
val query = savedStateHandle.get<String>(AppRouter.KEY_QUERY).orEmpty()
|
||||
|
||||
private val retryCounter = MutableStateFlow(0)
|
||||
private val listData = retryCounter.flatMapLatest {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user