Эх сурвалжийг харах

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 <[email protected]>
arkon 2 жил өмнө
parent
commit
3749cee28f

+ 4 - 0
app/src/main/java/eu/kanade/presentation/util/Navigator.kt

@@ -18,6 +18,10 @@ interface Tab : cafe.adriel.voyager.navigator.tab.Tab {
     suspend fun onReselect(navigator: Navigator) {}
 }
 
+interface AssistContentScreen {
+    fun onProvideAssistUrl(): String?
+}
+
 @Composable
 fun DefaultNavigatorScreenTransition(navigator: Navigator) {
     val slideDistance = rememberSlideDistance()

+ 7 - 0
app/src/main/java/eu/kanade/presentation/webview/WebViewScreen.kt

@@ -1,6 +1,7 @@
 package eu.kanade.presentation.webview
 
 import android.content.pm.ApplicationInfo
+import android.graphics.Bitmap
 import android.webkit.WebResourceRequest
 import android.webkit.WebView
 import androidx.compose.foundation.layout.Box
@@ -35,6 +36,7 @@ fun WebViewScreen(
     initialTitle: String?,
     url: String,
     headers: Map<String, String> = emptyMap(),
+    onUrlChange: (String) -> Unit = {},
     onShare: (String) -> Unit,
     onOpenInBrowser: (String) -> Unit,
     onClearCookies: (String) -> Unit,
@@ -112,6 +114,11 @@ fun WebViewScreen(
     ) { contentPadding ->
         val webClient = remember {
             object : AccompanistWebViewClient() {
+                override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
+                    super.onPageStarted(view, url, favicon)
+                    url?.let { onUrlChange(it) }
+                }
+
                 override fun shouldOverrideUrlLoading(
                     view: WebView?,
                     request: WebResourceRequest?,

+ 11 - 2
app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt

@@ -47,6 +47,7 @@ import eu.kanade.presentation.components.ChangeCategoryDialog
 import eu.kanade.presentation.components.Divider
 import eu.kanade.presentation.components.DuplicateMangaDialog
 import eu.kanade.presentation.components.Scaffold
+import eu.kanade.presentation.util.AssistContentScreen
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.source.LocalSource
 import eu.kanade.tachiyomi.source.online.HttpSource
@@ -62,10 +63,14 @@ import kotlinx.coroutines.flow.receiveAsFlow
 data class BrowseSourceScreen(
     private val sourceId: Long,
     private val query: String? = null,
-) : Screen {
+) : Screen, AssistContentScreen {
+
+    private var assistUrl: String? = null
 
     override val key = uniqueScreenKey
 
+    override fun onProvideAssistUrl() = assistUrl
+
     @Composable
     override fun Content() {
         val navigator = LocalNavigator.currentOrThrow
@@ -74,7 +79,7 @@ data class BrowseSourceScreen(
         val haptic = LocalHapticFeedback.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 snackbarHostState = remember { SnackbarHostState() }
@@ -87,6 +92,10 @@ data class BrowseSourceScreen(
             context.startActivity(intent)
         }
 
+        LaunchedEffect(screenModel.source) {
+            assistUrl = (screenModel.source as? HttpSource)?.baseUrl
+        }
+
         Scaffold(
             topBar = {
                 Column(modifier = Modifier.background(MaterialTheme.colorScheme.surface)) {

+ 12 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt

@@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.main
 
 import android.animation.ValueAnimator
 import android.app.SearchManager
+import android.app.assist.AssistContent
 import android.content.Intent
 import android.graphics.Color
 import android.os.Build
@@ -32,6 +33,7 @@ import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import androidx.core.animation.doOnEnd
+import androidx.core.net.toUri
 import androidx.core.splashscreen.SplashScreen
 import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
 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.ui.UiPreferences
 import eu.kanade.presentation.components.AppStateBanners
+import eu.kanade.presentation.util.AssistContentScreen
 import eu.kanade.presentation.util.DefaultNavigatorScreenTransition
 import eu.kanade.presentation.util.collectAsState
 import eu.kanade.tachiyomi.BuildConfig
@@ -261,6 +264,15 @@ class MainActivity : BaseActivity() {
         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) {
         if (category != null) {
             settingsSheet?.show(category)

+ 43 - 14
app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt

@@ -2,11 +2,11 @@ package eu.kanade.tachiyomi.ui.manga
 
 import android.content.Context
 import android.content.Intent
-import android.net.Uri
 import androidx.activity.compose.rememberLauncherForActivityResult
 import androidx.activity.result.contract.ActivityResultContracts
 import androidx.compose.foundation.layout.systemBarsPadding
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
@@ -15,6 +15,7 @@ import androidx.compose.ui.Modifier
 import androidx.compose.ui.hapticfeedback.HapticFeedbackType
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.platform.LocalHapticFeedback
+import androidx.core.net.toUri
 import cafe.adriel.voyager.core.model.rememberScreenModel
 import cafe.adriel.voyager.core.screen.Screen
 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.DownloadCustomAmountDialog
 import eu.kanade.presentation.manga.components.MangaCoverDialog
+import eu.kanade.presentation.util.AssistContentScreen
 import eu.kanade.presentation.util.isTabletUi
 import eu.kanade.tachiyomi.R
 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.reader.ReaderActivity
 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.logcat
 import eu.kanade.tachiyomi.util.system.toShareIntent
 import eu.kanade.tachiyomi.util.system.toast
 import kotlinx.coroutines.launch
+import logcat.LogPriority
 
 class MangaScreen(
     private val mangaId: Long,
     val fromSource: Boolean = false,
-) : Screen {
+) : Screen, AssistContentScreen {
+
+    private var assistUrl: String? = null
 
     override val key = uniqueScreenKey
 
+    override fun onProvideAssistUrl() = assistUrl
+
     @Composable
     override fun Content() {
         val navigator = LocalNavigator.currentOrThrow
@@ -77,6 +86,18 @@ class MangaScreen(
         val successState = state as MangaScreenState.Success
         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(
             state = successState,
             snackbarHostState = screenModel.snackbarHostState,
@@ -208,27 +229,35 @@ class MangaScreen(
         context.startActivity(ReaderActivity.newIntent(context, chapter.mangaId, chapter.id))
     }
 
-    private fun openMangaInWebView(context: Context, manga_: Manga?, source_: Source?) {
-        val manga = manga_ ?: return
-        val source = source_ as? HttpSource ?: return
+    private fun getMangaUrl(manga_: Manga?, source_: Source?): String? {
+        val manga = manga_ ?: return null
+        val source = source_ as? HttpSource ?: return null
 
-        val url = try {
+        return try {
             source.getMangaUrl(manga.toSManga())
         } catch (e: Exception) {
-            return
+            null
         }
+    }
 
-        val intent = WebViewActivity.newIntent(context, url, source.id, manga.title)
-        context.startActivity(intent)
+    private fun openMangaInWebView(context: Context, manga_: Manga?, source_: Source?) {
+        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?) {
-        val manga = manga_ ?: return
-        val source = source_ as? HttpSource ?: return
         try {
-            val uri = Uri.parse(source.getMangaUrl(manga.toSManga()))
-            val intent = uri.toShareIntent(context, type = "text/plain")
-            context.startActivity(Intent.createChooser(intent, context.getString(R.string.action_share)))
+            getMangaUrl(manga_, source_)?.let { url ->
+                val intent = url.toUri().toShareIntent(context, type = "text/plain")
+                context.startActivity(
+                    Intent.createChooser(
+                        intent,
+                        context.getString(R.string.action_share),
+                    ),
+                )
+            }
         } catch (e: Exception) {
             context.toast(e.message)
         }

+ 9 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt

@@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.reader
 import android.annotation.SuppressLint
 import android.annotation.TargetApi
 import android.app.ProgressDialog
+import android.app.assist.AssistContent
 import android.content.Context
 import android.content.Intent
 import android.content.res.ColorStateList
@@ -29,6 +30,7 @@ import android.widget.FrameLayout
 import android.widget.Toast
 import androidx.activity.viewModels
 import androidx.core.graphics.ColorUtils
+import androidx.core.net.toUri
 import androidx.core.transition.doOnEnd
 import androidx.core.view.WindowCompat
 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.
      */

+ 9 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewActivity.kt

@@ -1,5 +1,6 @@
 package eu.kanade.tachiyomi.ui.webview
 
+import android.app.assist.AssistContent
 import android.content.Context
 import android.content.Intent
 import android.os.Bundle
@@ -25,6 +26,8 @@ class WebViewActivity : BaseActivity() {
     private val sourceManager: SourceManager by injectLazy()
     private val network: NetworkHelper by injectLazy()
 
+    private var assistUrl: String? = null
+
     init {
         registerSecureActivity(this)
     }
@@ -52,6 +55,7 @@ class WebViewActivity : BaseActivity() {
                 initialTitle = intent.extras?.getString(TITLE_KEY),
                 url = url,
                 headers = headers,
+                onUrlChange = { assistUrl = it },
                 onShare = this::shareWebpage,
                 onOpenInBrowser = this::openInBrowser,
                 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() {
         super.finish()
         overridePendingTransition(R.anim.shared_axis_x_pop_enter, R.anim.shared_axis_x_pop_exit)