Add ResolvableSource interface for potentially opening entries directly based on some URI via a share intent
Implemented as an intermediate step in the existing Global Search share intent workflow. If any source manages to resolve the URI (e.g., a URL, a slug, etc.), the resolved SManga entry is directly opened. If nothing gets resolved, continue to a Global Search.
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
package eu.kanade.tachiyomi.ui.deeplink
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
|
||||
class DeepLinkActivity : Activity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
intent.apply {
|
||||
flags = flags or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
setClass(applicationContext, MainActivity::class.java)
|
||||
}
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package eu.kanade.tachiyomi.ui.deeplink
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.presentation.components.AppBar
|
||||
import eu.kanade.presentation.util.Screen
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchScreen
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaScreen
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
import tachiyomi.presentation.core.screens.LoadingScreen
|
||||
|
||||
class DeepLinkScreen(
|
||||
val query: String = "",
|
||||
) : Screen() {
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
|
||||
val screenModel = rememberScreenModel {
|
||||
DeepLinkScreenModel(query = query)
|
||||
}
|
||||
val state by screenModel.state.collectAsState()
|
||||
Scaffold(
|
||||
topBar = { scrollBehavior ->
|
||||
AppBar(
|
||||
title = stringResource(R.string.action_search_hint),
|
||||
navigateUp = navigator::pop,
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
},
|
||||
) { contentPadding ->
|
||||
when (state) {
|
||||
is DeepLinkScreenModel.State.Loading -> {
|
||||
LoadingScreen(Modifier.padding(contentPadding))
|
||||
}
|
||||
is DeepLinkScreenModel.State.NoResults -> {
|
||||
navigator.replace(GlobalSearchScreen(query))
|
||||
}
|
||||
is DeepLinkScreenModel.State.Result -> {
|
||||
navigator.replace(
|
||||
MangaScreen(
|
||||
(state as DeepLinkScreenModel.State.Result).manga.id,
|
||||
true,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package eu.kanade.tachiyomi.ui.deeplink
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import cafe.adriel.voyager.core.model.StateScreenModel
|
||||
import cafe.adriel.voyager.core.model.coroutineScope
|
||||
import eu.kanade.domain.manga.model.toDomainManga
|
||||
import eu.kanade.tachiyomi.source.online.ResolvableSource
|
||||
import kotlinx.coroutines.flow.update
|
||||
import tachiyomi.core.util.lang.launchIO
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.source.service.SourceManager
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class DeepLinkScreenModel(
|
||||
query: String = "",
|
||||
private val sourceManager: SourceManager = Injekt.get(),
|
||||
) : StateScreenModel<DeepLinkScreenModel.State>(State.Loading) {
|
||||
|
||||
init {
|
||||
coroutineScope.launchIO {
|
||||
val manga = sourceManager.getCatalogueSources()
|
||||
.filterIsInstance<ResolvableSource>()
|
||||
.filter { it.canResolveUri(query) }
|
||||
.firstNotNullOfOrNull { it.getManga(query)?.toDomainManga(it.id) }
|
||||
|
||||
mutableState.update {
|
||||
if (manga == null) {
|
||||
State.NoResults
|
||||
} else {
|
||||
State.Result(manga)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface State {
|
||||
@Immutable
|
||||
data object Loading : State
|
||||
|
||||
@Immutable
|
||||
data object NoResults : State
|
||||
|
||||
@Immutable
|
||||
data class Result(val manga: Manga) : State
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user