WebViewScreenContent.kt 7.2 KB

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