WebViewScreenContent.kt 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. package eu.kanade.presentation.webview
  2. import android.content.pm.ApplicationInfo
  3. import android.graphics.Bitmap
  4. import android.webkit.WebResourceRequest
  5. import android.webkit.WebView
  6. import androidx.compose.foundation.layout.Box
  7. import androidx.compose.foundation.layout.fillMaxSize
  8. import androidx.compose.foundation.layout.fillMaxWidth
  9. import androidx.compose.foundation.layout.padding
  10. import androidx.compose.material.icons.Icons
  11. import androidx.compose.material.icons.outlined.ArrowBack
  12. import androidx.compose.material.icons.outlined.ArrowForward
  13. import androidx.compose.material.icons.outlined.Close
  14. import androidx.compose.material3.LinearProgressIndicator
  15. import androidx.compose.runtime.Composable
  16. import androidx.compose.runtime.remember
  17. import androidx.compose.ui.Alignment
  18. import androidx.compose.ui.Modifier
  19. import androidx.compose.ui.res.stringResource
  20. import com.google.accompanist.web.AccompanistWebViewClient
  21. import com.google.accompanist.web.LoadingState
  22. import com.google.accompanist.web.WebView
  23. import com.google.accompanist.web.rememberWebViewNavigator
  24. import com.google.accompanist.web.rememberWebViewState
  25. import eu.kanade.presentation.components.AppBar
  26. import eu.kanade.presentation.components.AppBarActions
  27. import eu.kanade.tachiyomi.BuildConfig
  28. import eu.kanade.tachiyomi.R
  29. import eu.kanade.tachiyomi.util.system.setDefaultSettings
  30. import tachiyomi.presentation.core.components.material.Scaffold
  31. @Composable
  32. fun WebViewScreenContent(
  33. onNavigateUp: () -> Unit,
  34. initialTitle: String?,
  35. url: String,
  36. headers: Map<String, String> = emptyMap(),
  37. onUrlChange: (String) -> Unit = {},
  38. onShare: (String) -> Unit,
  39. onOpenInBrowser: (String) -> Unit,
  40. onClearCookies: (String) -> Unit,
  41. ) {
  42. val state = rememberWebViewState(url = url, additionalHttpHeaders = headers)
  43. val navigator = rememberWebViewNavigator()
  44. Scaffold(
  45. topBar = {
  46. Box {
  47. AppBar(
  48. title = state.pageTitle ?: initialTitle,
  49. subtitle = state.lastLoadedUrl,
  50. navigateUp = onNavigateUp,
  51. navigationIcon = Icons.Outlined.Close,
  52. actions = {
  53. AppBarActions(
  54. listOf(
  55. AppBar.Action(
  56. title = stringResource(R.string.action_webview_back),
  57. icon = Icons.Outlined.ArrowBack,
  58. onClick = {
  59. if (navigator.canGoBack) {
  60. navigator.navigateBack()
  61. }
  62. },
  63. enabled = navigator.canGoBack,
  64. ),
  65. AppBar.Action(
  66. title = stringResource(R.string.action_webview_forward),
  67. icon = Icons.Outlined.ArrowForward,
  68. onClick = {
  69. if (navigator.canGoForward) {
  70. navigator.navigateForward()
  71. }
  72. },
  73. enabled = navigator.canGoForward,
  74. ),
  75. AppBar.OverflowAction(
  76. title = stringResource(R.string.action_webview_refresh),
  77. onClick = { navigator.reload() },
  78. ),
  79. AppBar.OverflowAction(
  80. title = stringResource(R.string.action_share),
  81. onClick = { onShare(state.lastLoadedUrl!!) },
  82. ),
  83. AppBar.OverflowAction(
  84. title = stringResource(R.string.action_open_in_browser),
  85. onClick = { onOpenInBrowser(state.lastLoadedUrl!!) },
  86. ),
  87. AppBar.OverflowAction(
  88. title = stringResource(R.string.pref_clear_cookies),
  89. onClick = { onClearCookies(state.lastLoadedUrl!!) },
  90. ),
  91. ),
  92. )
  93. },
  94. )
  95. when (val loadingState = state.loadingState) {
  96. is LoadingState.Initializing -> LinearProgressIndicator(
  97. modifier = Modifier
  98. .fillMaxWidth()
  99. .align(Alignment.BottomCenter),
  100. )
  101. is LoadingState.Loading -> LinearProgressIndicator(
  102. progress = (loadingState as? LoadingState.Loading)?.progress ?: 1f,
  103. modifier = Modifier
  104. .fillMaxWidth()
  105. .align(Alignment.BottomCenter),
  106. )
  107. else -> {}
  108. }
  109. }
  110. },
  111. ) { contentPadding ->
  112. val webClient = remember {
  113. object : AccompanistWebViewClient() {
  114. override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
  115. super.onPageStarted(view, url, favicon)
  116. url?.let { onUrlChange(it) }
  117. }
  118. override fun shouldOverrideUrlLoading(
  119. view: WebView?,
  120. request: WebResourceRequest?,
  121. ): Boolean {
  122. request?.let {
  123. view?.loadUrl(it.url.toString(), headers)
  124. }
  125. return super.shouldOverrideUrlLoading(view, request)
  126. }
  127. }
  128. }
  129. WebView(
  130. state = state,
  131. modifier = Modifier
  132. .padding(contentPadding)
  133. .fillMaxSize(),
  134. navigator = navigator,
  135. onCreated = { webView ->
  136. webView.setDefaultSettings()
  137. // Debug mode (chrome://inspect/#devices)
  138. if (BuildConfig.DEBUG &&
  139. 0 != webView.context.applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE
  140. ) {
  141. WebView.setWebContentsDebuggingEnabled(true)
  142. }
  143. headers["user-agent"]?.let {
  144. webView.settings.userAgentString = it
  145. }
  146. },
  147. client = webClient,
  148. )
  149. }
  150. }