mirror of
https://github.com/aniyomiorg/aniyomi.git
synced 2024-11-27 16:26:29 +03:00
Add Assistant content URLs
This is surfaced in recents on Pixel devices for example. Docs: https://developer.android.com/guide/app-actions/assistant-sharing Co-authored-by: Jays2Kings <Jays2Kings@users.noreply.github.com>
This commit is contained in:
parent
ca500da4d8
commit
3749cee28f
7 changed files with 95 additions and 16 deletions
|
@ -18,6 +18,10 @@ interface Tab : cafe.adriel.voyager.navigator.tab.Tab {
|
||||||
suspend fun onReselect(navigator: Navigator) {}
|
suspend fun onReselect(navigator: Navigator) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface AssistContentScreen {
|
||||||
|
fun onProvideAssistUrl(): String?
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DefaultNavigatorScreenTransition(navigator: Navigator) {
|
fun DefaultNavigatorScreenTransition(navigator: Navigator) {
|
||||||
val slideDistance = rememberSlideDistance()
|
val slideDistance = rememberSlideDistance()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package eu.kanade.presentation.webview
|
package eu.kanade.presentation.webview
|
||||||
|
|
||||||
import android.content.pm.ApplicationInfo
|
import android.content.pm.ApplicationInfo
|
||||||
|
import android.graphics.Bitmap
|
||||||
import android.webkit.WebResourceRequest
|
import android.webkit.WebResourceRequest
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
@ -35,6 +36,7 @@ fun WebViewScreen(
|
||||||
initialTitle: String?,
|
initialTitle: String?,
|
||||||
url: String,
|
url: String,
|
||||||
headers: Map<String, String> = emptyMap(),
|
headers: Map<String, String> = emptyMap(),
|
||||||
|
onUrlChange: (String) -> Unit = {},
|
||||||
onShare: (String) -> Unit,
|
onShare: (String) -> Unit,
|
||||||
onOpenInBrowser: (String) -> Unit,
|
onOpenInBrowser: (String) -> Unit,
|
||||||
onClearCookies: (String) -> Unit,
|
onClearCookies: (String) -> Unit,
|
||||||
|
@ -112,6 +114,11 @@ fun WebViewScreen(
|
||||||
) { contentPadding ->
|
) { contentPadding ->
|
||||||
val webClient = remember {
|
val webClient = remember {
|
||||||
object : AccompanistWebViewClient() {
|
object : AccompanistWebViewClient() {
|
||||||
|
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
|
||||||
|
super.onPageStarted(view, url, favicon)
|
||||||
|
url?.let { onUrlChange(it) }
|
||||||
|
}
|
||||||
|
|
||||||
override fun shouldOverrideUrlLoading(
|
override fun shouldOverrideUrlLoading(
|
||||||
view: WebView?,
|
view: WebView?,
|
||||||
request: WebResourceRequest?,
|
request: WebResourceRequest?,
|
||||||
|
|
|
@ -47,6 +47,7 @@ import eu.kanade.presentation.components.ChangeCategoryDialog
|
||||||
import eu.kanade.presentation.components.Divider
|
import eu.kanade.presentation.components.Divider
|
||||||
import eu.kanade.presentation.components.DuplicateMangaDialog
|
import eu.kanade.presentation.components.DuplicateMangaDialog
|
||||||
import eu.kanade.presentation.components.Scaffold
|
import eu.kanade.presentation.components.Scaffold
|
||||||
|
import eu.kanade.presentation.util.AssistContentScreen
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.source.LocalSource
|
import eu.kanade.tachiyomi.source.LocalSource
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
|
@ -62,10 +63,14 @@ import kotlinx.coroutines.flow.receiveAsFlow
|
||||||
data class BrowseSourceScreen(
|
data class BrowseSourceScreen(
|
||||||
private val sourceId: Long,
|
private val sourceId: Long,
|
||||||
private val query: String? = null,
|
private val query: String? = null,
|
||||||
) : Screen {
|
) : Screen, AssistContentScreen {
|
||||||
|
|
||||||
|
private var assistUrl: String? = null
|
||||||
|
|
||||||
override val key = uniqueScreenKey
|
override val key = uniqueScreenKey
|
||||||
|
|
||||||
|
override fun onProvideAssistUrl() = assistUrl
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun Content() {
|
override fun Content() {
|
||||||
val navigator = LocalNavigator.currentOrThrow
|
val navigator = LocalNavigator.currentOrThrow
|
||||||
|
@ -74,7 +79,7 @@ data class BrowseSourceScreen(
|
||||||
val haptic = LocalHapticFeedback.current
|
val haptic = LocalHapticFeedback.current
|
||||||
val uriHandler = LocalUriHandler.current
|
val uriHandler = LocalUriHandler.current
|
||||||
|
|
||||||
val screenModel = rememberScreenModel { BrowseSourceScreenModel(sourceId = sourceId, searchQuery = query) }
|
val screenModel = rememberScreenModel { BrowseSourceScreenModel(sourceId, query) }
|
||||||
val state by screenModel.state.collectAsState()
|
val state by screenModel.state.collectAsState()
|
||||||
|
|
||||||
val snackbarHostState = remember { SnackbarHostState() }
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
|
@ -87,6 +92,10 @@ data class BrowseSourceScreen(
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(screenModel.source) {
|
||||||
|
assistUrl = (screenModel.source as? HttpSource)?.baseUrl
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
Column(modifier = Modifier.background(MaterialTheme.colorScheme.surface)) {
|
Column(modifier = Modifier.background(MaterialTheme.colorScheme.surface)) {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.main
|
||||||
|
|
||||||
import android.animation.ValueAnimator
|
import android.animation.ValueAnimator
|
||||||
import android.app.SearchManager
|
import android.app.SearchManager
|
||||||
|
import android.app.assist.AssistContent
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
@ -32,6 +33,7 @@ import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.animation.doOnEnd
|
import androidx.core.animation.doOnEnd
|
||||||
|
import androidx.core.net.toUri
|
||||||
import androidx.core.splashscreen.SplashScreen
|
import androidx.core.splashscreen.SplashScreen
|
||||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
|
@ -49,6 +51,7 @@ import eu.kanade.domain.library.service.LibraryPreferences
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
import eu.kanade.domain.ui.UiPreferences
|
import eu.kanade.domain.ui.UiPreferences
|
||||||
import eu.kanade.presentation.components.AppStateBanners
|
import eu.kanade.presentation.components.AppStateBanners
|
||||||
|
import eu.kanade.presentation.util.AssistContentScreen
|
||||||
import eu.kanade.presentation.util.DefaultNavigatorScreenTransition
|
import eu.kanade.presentation.util.DefaultNavigatorScreenTransition
|
||||||
import eu.kanade.presentation.util.collectAsState
|
import eu.kanade.presentation.util.collectAsState
|
||||||
import eu.kanade.tachiyomi.BuildConfig
|
import eu.kanade.tachiyomi.BuildConfig
|
||||||
|
@ -261,6 +264,15 @@ class MainActivity : BaseActivity() {
|
||||||
setSplashScreenExitAnimation(splashScreen)
|
setSplashScreenExitAnimation(splashScreen)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onProvideAssistContent(outContent: AssistContent) {
|
||||||
|
super.onProvideAssistContent(outContent)
|
||||||
|
when (val screen = navigator.lastItem) {
|
||||||
|
is AssistContentScreen -> {
|
||||||
|
screen.onProvideAssistUrl()?.let { outContent.webUri = it.toUri() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun showSettingsSheet(category: Category? = null) {
|
private fun showSettingsSheet(category: Category? = null) {
|
||||||
if (category != null) {
|
if (category != null) {
|
||||||
settingsSheet?.show(category)
|
settingsSheet?.show(category)
|
||||||
|
|
|
@ -2,11 +2,11 @@ package eu.kanade.tachiyomi.ui.manga
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.foundation.layout.systemBarsPadding
|
import androidx.compose.foundation.layout.systemBarsPadding
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
@ -15,6 +15,7 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||||
|
import androidx.core.net.toUri
|
||||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||||
import cafe.adriel.voyager.core.screen.Screen
|
import cafe.adriel.voyager.core.screen.Screen
|
||||||
import cafe.adriel.voyager.core.screen.uniqueScreenKey
|
import cafe.adriel.voyager.core.screen.uniqueScreenKey
|
||||||
|
@ -34,6 +35,7 @@ import eu.kanade.presentation.manga.MangaScreen
|
||||||
import eu.kanade.presentation.manga.components.DeleteChaptersDialog
|
import eu.kanade.presentation.manga.components.DeleteChaptersDialog
|
||||||
import eu.kanade.presentation.manga.components.DownloadCustomAmountDialog
|
import eu.kanade.presentation.manga.components.DownloadCustomAmountDialog
|
||||||
import eu.kanade.presentation.manga.components.MangaCoverDialog
|
import eu.kanade.presentation.manga.components.MangaCoverDialog
|
||||||
|
import eu.kanade.presentation.util.AssistContentScreen
|
||||||
import eu.kanade.presentation.util.isTabletUi
|
import eu.kanade.presentation.util.isTabletUi
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
|
@ -47,18 +49,25 @@ import eu.kanade.tachiyomi.ui.home.HomeScreen
|
||||||
import eu.kanade.tachiyomi.ui.manga.track.TrackInfoDialogHomeScreen
|
import eu.kanade.tachiyomi.ui.manga.track.TrackInfoDialogHomeScreen
|
||||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||||
|
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||||
|
import eu.kanade.tachiyomi.util.system.logcat
|
||||||
import eu.kanade.tachiyomi.util.system.toShareIntent
|
import eu.kanade.tachiyomi.util.system.toShareIntent
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import logcat.LogPriority
|
||||||
|
|
||||||
class MangaScreen(
|
class MangaScreen(
|
||||||
private val mangaId: Long,
|
private val mangaId: Long,
|
||||||
val fromSource: Boolean = false,
|
val fromSource: Boolean = false,
|
||||||
) : Screen {
|
) : Screen, AssistContentScreen {
|
||||||
|
|
||||||
|
private var assistUrl: String? = null
|
||||||
|
|
||||||
override val key = uniqueScreenKey
|
override val key = uniqueScreenKey
|
||||||
|
|
||||||
|
override fun onProvideAssistUrl() = assistUrl
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun Content() {
|
override fun Content() {
|
||||||
val navigator = LocalNavigator.currentOrThrow
|
val navigator = LocalNavigator.currentOrThrow
|
||||||
|
@ -77,6 +86,18 @@ class MangaScreen(
|
||||||
val successState = state as MangaScreenState.Success
|
val successState = state as MangaScreenState.Success
|
||||||
val isHttpSource = remember { successState.source is HttpSource }
|
val isHttpSource = remember { successState.source is HttpSource }
|
||||||
|
|
||||||
|
LaunchedEffect(successState.manga, screenModel.source) {
|
||||||
|
if (isHttpSource) {
|
||||||
|
try {
|
||||||
|
withIOContext {
|
||||||
|
assistUrl = getMangaUrl(screenModel.manga, screenModel.source)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logcat(LogPriority.ERROR, e) { "Failed to get manga URL" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MangaScreen(
|
MangaScreen(
|
||||||
state = successState,
|
state = successState,
|
||||||
snackbarHostState = screenModel.snackbarHostState,
|
snackbarHostState = screenModel.snackbarHostState,
|
||||||
|
@ -208,27 +229,35 @@ class MangaScreen(
|
||||||
context.startActivity(ReaderActivity.newIntent(context, chapter.mangaId, chapter.id))
|
context.startActivity(ReaderActivity.newIntent(context, chapter.mangaId, chapter.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openMangaInWebView(context: Context, manga_: Manga?, source_: Source?) {
|
private fun getMangaUrl(manga_: Manga?, source_: Source?): String? {
|
||||||
val manga = manga_ ?: return
|
val manga = manga_ ?: return null
|
||||||
val source = source_ as? HttpSource ?: return
|
val source = source_ as? HttpSource ?: return null
|
||||||
|
|
||||||
val url = try {
|
return try {
|
||||||
source.getMangaUrl(manga.toSManga())
|
source.getMangaUrl(manga.toSManga())
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
return
|
null
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val intent = WebViewActivity.newIntent(context, url, source.id, manga.title)
|
private fun openMangaInWebView(context: Context, manga_: Manga?, source_: Source?) {
|
||||||
context.startActivity(intent)
|
getMangaUrl(manga_, source_)?.let { url ->
|
||||||
|
val intent = WebViewActivity.newIntent(context, url, source_?.id, manga_?.title)
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shareManga(context: Context, manga_: Manga?, source_: Source?) {
|
private fun shareManga(context: Context, manga_: Manga?, source_: Source?) {
|
||||||
val manga = manga_ ?: return
|
|
||||||
val source = source_ as? HttpSource ?: return
|
|
||||||
try {
|
try {
|
||||||
val uri = Uri.parse(source.getMangaUrl(manga.toSManga()))
|
getMangaUrl(manga_, source_)?.let { url ->
|
||||||
val intent = uri.toShareIntent(context, type = "text/plain")
|
val intent = url.toUri().toShareIntent(context, type = "text/plain")
|
||||||
context.startActivity(Intent.createChooser(intent, context.getString(R.string.action_share)))
|
context.startActivity(
|
||||||
|
Intent.createChooser(
|
||||||
|
intent,
|
||||||
|
context.getString(R.string.action_share),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
context.toast(e.message)
|
context.toast(e.message)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.reader
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.annotation.TargetApi
|
import android.annotation.TargetApi
|
||||||
import android.app.ProgressDialog
|
import android.app.ProgressDialog
|
||||||
|
import android.app.assist.AssistContent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
|
@ -29,6 +30,7 @@ import android.widget.FrameLayout
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.core.graphics.ColorUtils
|
import androidx.core.graphics.ColorUtils
|
||||||
|
import androidx.core.net.toUri
|
||||||
import androidx.core.transition.doOnEnd
|
import androidx.core.transition.doOnEnd
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
|
@ -296,6 +298,13 @@ class ReaderActivity : BaseActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onProvideAssistContent(outContent: AssistContent) {
|
||||||
|
super.onProvideAssistContent(outContent)
|
||||||
|
viewModel.getChapterUrl()?.let { url ->
|
||||||
|
outContent.webUri = url.toUri()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the options menu of the toolbar is being created. It adds our custom menu.
|
* Called when the options menu of the toolbar is being created. It adds our custom menu.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package eu.kanade.tachiyomi.ui.webview
|
package eu.kanade.tachiyomi.ui.webview
|
||||||
|
|
||||||
|
import android.app.assist.AssistContent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
@ -25,6 +26,8 @@ class WebViewActivity : BaseActivity() {
|
||||||
private val sourceManager: SourceManager by injectLazy()
|
private val sourceManager: SourceManager by injectLazy()
|
||||||
private val network: NetworkHelper by injectLazy()
|
private val network: NetworkHelper by injectLazy()
|
||||||
|
|
||||||
|
private var assistUrl: String? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
registerSecureActivity(this)
|
registerSecureActivity(this)
|
||||||
}
|
}
|
||||||
|
@ -52,6 +55,7 @@ class WebViewActivity : BaseActivity() {
|
||||||
initialTitle = intent.extras?.getString(TITLE_KEY),
|
initialTitle = intent.extras?.getString(TITLE_KEY),
|
||||||
url = url,
|
url = url,
|
||||||
headers = headers,
|
headers = headers,
|
||||||
|
onUrlChange = { assistUrl = it },
|
||||||
onShare = this::shareWebpage,
|
onShare = this::shareWebpage,
|
||||||
onOpenInBrowser = this::openInBrowser,
|
onOpenInBrowser = this::openInBrowser,
|
||||||
onClearCookies = this::clearCookies,
|
onClearCookies = this::clearCookies,
|
||||||
|
@ -59,6 +63,11 @@ class WebViewActivity : BaseActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onProvideAssistContent(outContent: AssistContent) {
|
||||||
|
super.onProvideAssistContent(outContent)
|
||||||
|
assistUrl?.let { outContent.webUri = it.toUri() }
|
||||||
|
}
|
||||||
|
|
||||||
override fun finish() {
|
override fun finish() {
|
||||||
super.finish()
|
super.finish()
|
||||||
overridePendingTransition(R.anim.shared_axis_x_pop_enter, R.anim.shared_axis_x_pop_exit)
|
overridePendingTransition(R.anim.shared_axis_x_pop_enter, R.anim.shared_axis_x_pop_exit)
|
||||||
|
|
Loading…
Reference in a new issue