Apply proxy settings to WebView
This commit is contained in:
@@ -0,0 +1,82 @@
|
|||||||
|
package org.koitharu.kotatsu.browser
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import androidx.core.view.WindowInsetsCompat
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.core.view.updatePadding
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import org.koitharu.kotatsu.core.network.proxy.ProxyProvider
|
||||||
|
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.consumeAll
|
||||||
|
import org.koitharu.kotatsu.databinding.ActivityBrowserBinding
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
abstract class BaseBrowserActivity : BaseActivity<ActivityBrowserBinding>(), BrowserCallback {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var proxyProvider: ProxyProvider
|
||||||
|
|
||||||
|
private lateinit var onBackPressedCallback: WebViewBackPressedCallback
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
if (!setContentViewWebViewSafe { ActivityBrowserBinding.inflate(layoutInflater) }) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
viewBinding.webView.webChromeClient = ProgressChromeClient(viewBinding.progressBar)
|
||||||
|
onBackPressedCallback = WebViewBackPressedCallback(viewBinding.webView)
|
||||||
|
onBackPressedDispatcher.addCallback(onBackPressedCallback)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onApplyWindowInsets(
|
||||||
|
v: View,
|
||||||
|
insets: WindowInsetsCompat
|
||||||
|
): WindowInsetsCompat {
|
||||||
|
val type = WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.ime()
|
||||||
|
val barsInsets = insets.getInsets(type)
|
||||||
|
viewBinding.webView.updatePadding(
|
||||||
|
left = barsInsets.left,
|
||||||
|
right = barsInsets.right,
|
||||||
|
bottom = barsInsets.bottom,
|
||||||
|
)
|
||||||
|
viewBinding.appbar.updatePadding(
|
||||||
|
left = barsInsets.left,
|
||||||
|
right = barsInsets.right,
|
||||||
|
top = barsInsets.top,
|
||||||
|
)
|
||||||
|
return insets.consumeAll(type)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
viewBinding.webView.onPause()
|
||||||
|
super.onPause()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
viewBinding.webView.onResume()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
if (hasViewBinding()) {
|
||||||
|
viewBinding.webView.stopLoading()
|
||||||
|
viewBinding.webView.destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLoadingStateChanged(isLoading: Boolean) {
|
||||||
|
viewBinding.progressBar.isVisible = isLoading
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTitleChanged(title: CharSequence, subtitle: CharSequence?) {
|
||||||
|
this.title = title
|
||||||
|
supportActionBar?.subtitle = subtitle
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onHistoryChanged() {
|
||||||
|
onBackPressedCallback.onHistoryChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,12 +3,10 @@ package org.koitharu.kotatsu.browser
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.core.view.WindowInsetsCompat
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.core.view.updatePadding
|
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.core.model.MangaSource
|
import org.koitharu.kotatsu.core.model.MangaSource
|
||||||
import org.koitharu.kotatsu.core.nav.AppRouter
|
import org.koitharu.kotatsu.core.nav.AppRouter
|
||||||
@@ -16,26 +14,20 @@ import org.koitharu.kotatsu.core.nav.router
|
|||||||
import org.koitharu.kotatsu.core.network.CommonHeaders
|
import org.koitharu.kotatsu.core.network.CommonHeaders
|
||||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||||
import org.koitharu.kotatsu.core.parser.ParserMangaRepository
|
import org.koitharu.kotatsu.core.parser.ParserMangaRepository
|
||||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.configureForParser
|
import org.koitharu.kotatsu.core.util.ext.configureForParser
|
||||||
import org.koitharu.kotatsu.core.util.ext.consumeAll
|
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
|
||||||
import org.koitharu.kotatsu.databinding.ActivityBrowserBinding
|
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import com.google.android.material.R as materialR
|
import com.google.android.material.R as materialR
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class BrowserActivity : BaseActivity<ActivityBrowserBinding>(), BrowserCallback {
|
class BrowserActivity : BaseBrowserActivity() {
|
||||||
|
|
||||||
private lateinit var onBackPressedCallback: WebViewBackPressedCallback
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var mangaRepositoryFactory: MangaRepository.Factory
|
lateinit var mangaRepositoryFactory: MangaRepository.Factory
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
if (!setContentViewWebViewSafe { ActivityBrowserBinding.inflate(layoutInflater) }) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
supportActionBar?.run {
|
supportActionBar?.run {
|
||||||
setDisplayHomeAsUpEnabled(true)
|
setDisplayHomeAsUpEnabled(true)
|
||||||
setHomeAsUpIndicator(materialR.drawable.abc_ic_clear_material)
|
setHomeAsUpIndicator(materialR.drawable.abc_ic_clear_material)
|
||||||
@@ -44,42 +36,27 @@ class BrowserActivity : BaseActivity<ActivityBrowserBinding>(), BrowserCallback
|
|||||||
val repository = mangaRepositoryFactory.create(mangaSource) as? ParserMangaRepository
|
val repository = mangaRepositoryFactory.create(mangaSource) as? ParserMangaRepository
|
||||||
val userAgent = repository?.getRequestHeaders()?.get(CommonHeaders.USER_AGENT)
|
val userAgent = repository?.getRequestHeaders()?.get(CommonHeaders.USER_AGENT)
|
||||||
viewBinding.webView.configureForParser(userAgent)
|
viewBinding.webView.configureForParser(userAgent)
|
||||||
viewBinding.webView.webViewClient = BrowserClient(this)
|
viewBinding.webView.webViewClient = BrowserClient(proxyProvider, this)
|
||||||
viewBinding.webView.webChromeClient = ProgressChromeClient(viewBinding.progressBar)
|
lifecycleScope.launch {
|
||||||
onBackPressedCallback = WebViewBackPressedCallback(viewBinding.webView)
|
try {
|
||||||
onBackPressedDispatcher.addCallback(onBackPressedCallback)
|
proxyProvider.applyWebViewConfig()
|
||||||
if (savedInstanceState != null) {
|
} catch (e: Exception) {
|
||||||
return
|
e.printStackTraceDebug()
|
||||||
|
Snackbar.make(viewBinding.webView, e.getDisplayMessage(resources), Snackbar.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
val url = intent?.dataString
|
||||||
|
if (url.isNullOrEmpty()) {
|
||||||
|
finishAfterTransition()
|
||||||
|
} else {
|
||||||
|
onTitleChanged(
|
||||||
|
intent?.getStringExtra(AppRouter.KEY_TITLE) ?: getString(R.string.loading_),
|
||||||
|
url,
|
||||||
|
)
|
||||||
|
viewBinding.webView.loadUrl(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val url = intent?.dataString
|
|
||||||
if (url.isNullOrEmpty()) {
|
|
||||||
finishAfterTransition()
|
|
||||||
} else {
|
|
||||||
onTitleChanged(
|
|
||||||
intent?.getStringExtra(AppRouter.KEY_TITLE) ?: getString(R.string.loading_),
|
|
||||||
url,
|
|
||||||
)
|
|
||||||
viewBinding.webView.loadUrl(url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onApplyWindowInsets(
|
|
||||||
v: View,
|
|
||||||
insets: WindowInsetsCompat
|
|
||||||
): WindowInsetsCompat {
|
|
||||||
val type = WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.ime()
|
|
||||||
val barsInsets = insets.getInsets(type)
|
|
||||||
viewBinding.webView.updatePadding(
|
|
||||||
left = barsInsets.left,
|
|
||||||
right = barsInsets.right,
|
|
||||||
bottom = barsInsets.bottom,
|
|
||||||
)
|
|
||||||
viewBinding.appbar.updatePadding(
|
|
||||||
left = barsInsets.left,
|
|
||||||
right = barsInsets.right,
|
|
||||||
top = barsInsets.top,
|
|
||||||
)
|
|
||||||
return insets.consumeAll(type)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
@@ -104,35 +81,4 @@ class BrowserActivity : BaseActivity<ActivityBrowserBinding>(), BrowserCallback
|
|||||||
|
|
||||||
else -> super.onOptionsItemSelected(item)
|
else -> super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
|
||||||
viewBinding.webView.onPause()
|
|
||||||
super.onPause()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
viewBinding.webView.onResume()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
if (hasViewBinding()) {
|
|
||||||
viewBinding.webView.stopLoading()
|
|
||||||
viewBinding.webView.destroy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLoadingStateChanged(isLoading: Boolean) {
|
|
||||||
viewBinding.progressBar.isVisible = isLoading
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onTitleChanged(title: CharSequence, subtitle: CharSequence?) {
|
|
||||||
this.title = title
|
|
||||||
supportActionBar?.subtitle = subtitle
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onHistoryChanged() {
|
|
||||||
onBackPressedCallback.onHistoryChanged()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,13 @@ package org.koitharu.kotatsu.browser
|
|||||||
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
import android.webkit.WebViewClient
|
import androidx.webkit.WebViewClientCompat
|
||||||
|
import org.koitharu.kotatsu.core.network.proxy.ProxyProvider
|
||||||
|
|
||||||
open class BrowserClient(private val callback: BrowserCallback) : WebViewClient() {
|
open class BrowserClient(
|
||||||
|
private val proxyProvider: ProxyProvider,
|
||||||
|
private val callback: BrowserCallback
|
||||||
|
) : WebViewClientCompat() {
|
||||||
|
|
||||||
override fun onPageFinished(webView: WebView, url: String) {
|
override fun onPageFinished(webView: WebView, url: String) {
|
||||||
super.onPageFinished(webView, url)
|
super.onPageFinished(webView, url)
|
||||||
@@ -16,7 +20,7 @@ open class BrowserClient(private val callback: BrowserCallback) : WebViewClient(
|
|||||||
callback.onLoadingStateChanged(isLoading = true)
|
callback.onLoadingStateChanged(isLoading = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPageCommitVisible(view: WebView, url: String?) {
|
override fun onPageCommitVisible(view: WebView, url: String) {
|
||||||
super.onPageCommitVisible(view, url)
|
super.onPageCommitVisible(view, url)
|
||||||
callback.onTitleChanged(view.title.orEmpty(), url)
|
callback.onTitleChanged(view.title.orEmpty(), url)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,10 @@ import android.content.Intent
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
|
||||||
import androidx.activity.result.contract.ActivityResultContract
|
import androidx.activity.result.contract.ActivityResultContract
|
||||||
import androidx.core.view.WindowInsetsCompat
|
|
||||||
import androidx.core.view.isInvisible
|
import androidx.core.view.isInvisible
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.core.view.updatePadding
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -20,21 +17,19 @@ import kotlinx.coroutines.yield
|
|||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.browser.WebViewBackPressedCallback
|
import org.koitharu.kotatsu.browser.BaseBrowserActivity
|
||||||
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
|
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
|
||||||
import org.koitharu.kotatsu.core.model.MangaSource
|
import org.koitharu.kotatsu.core.model.MangaSource
|
||||||
import org.koitharu.kotatsu.core.nav.AppRouter
|
import org.koitharu.kotatsu.core.nav.AppRouter
|
||||||
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
|
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
|
||||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.configureForParser
|
import org.koitharu.kotatsu.core.util.ext.configureForParser
|
||||||
import org.koitharu.kotatsu.core.util.ext.consumeAll
|
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
|
||||||
import org.koitharu.kotatsu.databinding.ActivityBrowserBinding
|
|
||||||
import org.koitharu.kotatsu.parsers.network.CloudFlareHelper
|
import org.koitharu.kotatsu.parsers.network.CloudFlareHelper
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import com.google.android.material.R as materialR
|
import com.google.android.material.R as materialR
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class CloudFlareActivity : BaseActivity<ActivityBrowserBinding>(), CloudFlareCallback {
|
class CloudFlareActivity : BaseBrowserActivity(), CloudFlareCallback {
|
||||||
|
|
||||||
private var pendingResult = RESULT_CANCELED
|
private var pendingResult = RESULT_CANCELED
|
||||||
|
|
||||||
@@ -42,13 +37,9 @@ class CloudFlareActivity : BaseActivity<ActivityBrowserBinding>(), CloudFlareCal
|
|||||||
lateinit var cookieJar: MutableCookieJar
|
lateinit var cookieJar: MutableCookieJar
|
||||||
|
|
||||||
private lateinit var cfClient: CloudFlareClient
|
private lateinit var cfClient: CloudFlareClient
|
||||||
private var onBackPressedCallback: WebViewBackPressedCallback? = null
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
if (!setContentViewWebViewSafe { ActivityBrowserBinding.inflate(layoutInflater) }) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
supportActionBar?.run {
|
supportActionBar?.run {
|
||||||
setDisplayHomeAsUpEnabled(true)
|
setDisplayHomeAsUpEnabled(true)
|
||||||
setHomeAsUpIndicator(materialR.drawable.abc_ic_clear_material)
|
setHomeAsUpIndicator(materialR.drawable.abc_ic_clear_material)
|
||||||
@@ -58,45 +49,20 @@ class CloudFlareActivity : BaseActivity<ActivityBrowserBinding>(), CloudFlareCal
|
|||||||
finishAfterTransition()
|
finishAfterTransition()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cfClient = CloudFlareClient(cookieJar, this, url)
|
cfClient = CloudFlareClient(proxyProvider, cookieJar, this, url)
|
||||||
viewBinding.webView.configureForParser(intent?.getStringExtra(AppRouter.KEY_USER_AGENT))
|
viewBinding.webView.configureForParser(intent?.getStringExtra(AppRouter.KEY_USER_AGENT))
|
||||||
viewBinding.webView.webViewClient = cfClient
|
viewBinding.webView.webViewClient = cfClient
|
||||||
onBackPressedCallback = WebViewBackPressedCallback(viewBinding.webView).also {
|
lifecycleScope.launch {
|
||||||
onBackPressedDispatcher.addCallback(it)
|
try {
|
||||||
|
proxyProvider.applyWebViewConfig()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Snackbar.make(viewBinding.webView, e.getDisplayMessage(resources), Snackbar.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
onTitleChanged(getString(R.string.loading_), url)
|
||||||
|
viewBinding.webView.loadUrl(url)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (savedInstanceState == null) {
|
|
||||||
onTitleChanged(getString(R.string.loading_), url)
|
|
||||||
viewBinding.webView.loadUrl(url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onApplyWindowInsets(
|
|
||||||
v: View,
|
|
||||||
insets: WindowInsetsCompat
|
|
||||||
): WindowInsetsCompat {
|
|
||||||
val type = WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.ime()
|
|
||||||
val barsInsets = insets.getInsets(type)
|
|
||||||
viewBinding.webView.updatePadding(
|
|
||||||
left = barsInsets.left,
|
|
||||||
right = barsInsets.right,
|
|
||||||
bottom = barsInsets.bottom,
|
|
||||||
)
|
|
||||||
viewBinding.appbar.updatePadding(
|
|
||||||
left = barsInsets.left,
|
|
||||||
right = barsInsets.right,
|
|
||||||
top = barsInsets.top,
|
|
||||||
)
|
|
||||||
return insets.consumeAll(type)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
runCatching {
|
|
||||||
viewBinding.webView
|
|
||||||
}.onSuccess {
|
|
||||||
it.stopLoading()
|
|
||||||
it.destroy()
|
|
||||||
}
|
|
||||||
super.onDestroy()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||||
@@ -119,21 +85,13 @@ class CloudFlareActivity : BaseActivity<ActivityBrowserBinding>(), CloudFlareCal
|
|||||||
else -> super.onOptionsItemSelected(item)
|
else -> super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
viewBinding.webView.onResume()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPause() {
|
|
||||||
viewBinding.webView.onPause()
|
|
||||||
super.onPause()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun finish() {
|
override fun finish() {
|
||||||
setResult(pendingResult)
|
setResult(pendingResult)
|
||||||
super.finish()
|
super.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onLoadingStateChanged(isLoading: Boolean) = Unit
|
||||||
|
|
||||||
override fun onPageLoaded() {
|
override fun onPageLoaded() {
|
||||||
viewBinding.progressBar.isInvisible = true
|
viewBinding.progressBar.isInvisible = true
|
||||||
}
|
}
|
||||||
@@ -151,14 +109,6 @@ class CloudFlareActivity : BaseActivity<ActivityBrowserBinding>(), CloudFlareCal
|
|||||||
finishAfterTransition()
|
finishAfterTransition()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLoadingStateChanged(isLoading: Boolean) {
|
|
||||||
viewBinding.progressBar.isVisible = isLoading
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onHistoryChanged() {
|
|
||||||
onBackPressedCallback?.onHistoryChanged()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onTitleChanged(title: CharSequence, subtitle: CharSequence?) {
|
override fun onTitleChanged(title: CharSequence, subtitle: CharSequence?) {
|
||||||
setTitle(title)
|
setTitle(title)
|
||||||
supportActionBar?.subtitle =
|
supportActionBar?.subtitle =
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import org.koitharu.kotatsu.browser.BrowserCallback
|
|||||||
|
|
||||||
interface CloudFlareCallback : BrowserCallback {
|
interface CloudFlareCallback : BrowserCallback {
|
||||||
|
|
||||||
override fun onLoadingStateChanged(isLoading: Boolean) = Unit
|
|
||||||
|
|
||||||
override fun onTitleChanged(title: CharSequence, subtitle: CharSequence?) = Unit
|
override fun onTitleChanged(title: CharSequence, subtitle: CharSequence?) = Unit
|
||||||
|
|
||||||
fun onPageLoaded()
|
fun onPageLoaded()
|
||||||
|
|||||||
@@ -4,15 +4,17 @@ import android.graphics.Bitmap
|
|||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
import org.koitharu.kotatsu.browser.BrowserClient
|
import org.koitharu.kotatsu.browser.BrowserClient
|
||||||
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
|
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
|
||||||
|
import org.koitharu.kotatsu.core.network.proxy.ProxyProvider
|
||||||
import org.koitharu.kotatsu.parsers.network.CloudFlareHelper
|
import org.koitharu.kotatsu.parsers.network.CloudFlareHelper
|
||||||
|
|
||||||
private const val LOOP_COUNTER = 3
|
private const val LOOP_COUNTER = 3
|
||||||
|
|
||||||
class CloudFlareClient(
|
class CloudFlareClient(
|
||||||
|
proxyProvider: ProxyProvider,
|
||||||
private val cookieJar: MutableCookieJar,
|
private val cookieJar: MutableCookieJar,
|
||||||
private val callback: CloudFlareCallback,
|
private val callback: CloudFlareCallback,
|
||||||
private val targetUrl: String,
|
private val targetUrl: String,
|
||||||
) : BrowserClient(callback) {
|
) : BrowserClient(proxyProvider, callback) {
|
||||||
|
|
||||||
private val oldClearance = getClearance()
|
private val oldClearance = getClearance()
|
||||||
private var counter = 0
|
private var counter = 0
|
||||||
@@ -22,7 +24,7 @@ class CloudFlareClient(
|
|||||||
checkClearance()
|
checkClearance()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPageCommitVisible(view: WebView, url: String?) {
|
override fun onPageCommitVisible(view: WebView, url: String) {
|
||||||
super.onPageCommitVisible(view, url)
|
super.onPageCommitVisible(view, url)
|
||||||
callback.onPageLoaded()
|
callback.onPageLoaded()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
package org.koitharu.kotatsu.core.network
|
|
||||||
|
|
||||||
import okio.IOException
|
|
||||||
import org.koitharu.kotatsu.core.exceptions.ProxyConfigException
|
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
|
|
||||||
import java.net.InetSocketAddress
|
|
||||||
import java.net.Proxy
|
|
||||||
import java.net.ProxySelector
|
|
||||||
import java.net.SocketAddress
|
|
||||||
import java.net.URI
|
|
||||||
|
|
||||||
class AppProxySelector(
|
|
||||||
private val settings: AppSettings,
|
|
||||||
) : ProxySelector() {
|
|
||||||
|
|
||||||
init {
|
|
||||||
setDefault(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var cachedProxy: Proxy? = null
|
|
||||||
|
|
||||||
override fun select(uri: URI?): List<Proxy> {
|
|
||||||
return listOf(getProxy())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun connectFailed(uri: URI?, sa: SocketAddress?, ioe: IOException?) {
|
|
||||||
ioe?.printStackTraceDebug()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getProxy(): Proxy {
|
|
||||||
val type = settings.proxyType
|
|
||||||
val address = settings.proxyAddress
|
|
||||||
val port = settings.proxyPort
|
|
||||||
if (type == Proxy.Type.DIRECT) {
|
|
||||||
return Proxy.NO_PROXY
|
|
||||||
}
|
|
||||||
if (address.isNullOrEmpty() || port < 0 || port > 0xFFFF) {
|
|
||||||
throw ProxyConfigException()
|
|
||||||
}
|
|
||||||
cachedProxy?.let {
|
|
||||||
val addr = it.address() as? InetSocketAddress
|
|
||||||
if (addr != null && it.type() == type && addr.port == port && addr.hostString == address) {
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val proxy = Proxy(type, InetSocketAddress(address, port))
|
|
||||||
cachedProxy = proxy
|
|
||||||
return proxy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -16,6 +16,7 @@ import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
|
|||||||
import org.koitharu.kotatsu.core.network.cookies.PreferencesCookieJar
|
import org.koitharu.kotatsu.core.network.cookies.PreferencesCookieJar
|
||||||
import org.koitharu.kotatsu.core.network.imageproxy.ImageProxyInterceptor
|
import org.koitharu.kotatsu.core.network.imageproxy.ImageProxyInterceptor
|
||||||
import org.koitharu.kotatsu.core.network.imageproxy.RealImageProxyInterceptor
|
import org.koitharu.kotatsu.core.network.imageproxy.RealImageProxyInterceptor
|
||||||
|
import org.koitharu.kotatsu.core.network.proxy.ProxyProvider
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.core.util.ext.assertNotInMainThread
|
import org.koitharu.kotatsu.core.util.ext.assertNotInMainThread
|
||||||
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
|
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
|
||||||
@@ -62,14 +63,15 @@ interface NetworkModule {
|
|||||||
cache: Cache,
|
cache: Cache,
|
||||||
cookieJar: CookieJar,
|
cookieJar: CookieJar,
|
||||||
settings: AppSettings,
|
settings: AppSettings,
|
||||||
|
proxyProvider: ProxyProvider,
|
||||||
): OkHttpClient = OkHttpClient.Builder().apply {
|
): OkHttpClient = OkHttpClient.Builder().apply {
|
||||||
assertNotInMainThread()
|
assertNotInMainThread()
|
||||||
connectTimeout(20, TimeUnit.SECONDS)
|
connectTimeout(20, TimeUnit.SECONDS)
|
||||||
readTimeout(60, TimeUnit.SECONDS)
|
readTimeout(60, TimeUnit.SECONDS)
|
||||||
writeTimeout(20, TimeUnit.SECONDS)
|
writeTimeout(20, TimeUnit.SECONDS)
|
||||||
cookieJar(cookieJar)
|
cookieJar(cookieJar)
|
||||||
proxySelector(AppProxySelector(settings))
|
proxySelector(proxyProvider.selector)
|
||||||
proxyAuthenticator(ProxyAuthenticator(settings))
|
proxyAuthenticator(proxyProvider.authenticator)
|
||||||
dns(DoHManager(cache, settings))
|
dns(DoHManager(cache, settings))
|
||||||
if (settings.isSSLBypassEnabled) {
|
if (settings.isSSLBypassEnabled) {
|
||||||
disableCertificateVerification()
|
disableCertificateVerification()
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
package org.koitharu.kotatsu.core.network
|
|
||||||
|
|
||||||
import okhttp3.Authenticator
|
|
||||||
import okhttp3.Credentials
|
|
||||||
import okhttp3.Request
|
|
||||||
import okhttp3.Response
|
|
||||||
import okhttp3.Route
|
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
|
||||||
import java.net.PasswordAuthentication
|
|
||||||
import java.net.Proxy
|
|
||||||
|
|
||||||
class ProxyAuthenticator(
|
|
||||||
private val settings: AppSettings,
|
|
||||||
) : Authenticator, java.net.Authenticator() {
|
|
||||||
|
|
||||||
init {
|
|
||||||
setDefault(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun authenticate(route: Route?, response: Response): Request? {
|
|
||||||
if (!isProxyEnabled()) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
if (response.request.header(CommonHeaders.PROXY_AUTHORIZATION) != null) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
val login = settings.proxyLogin ?: return null
|
|
||||||
val password = settings.proxyPassword ?: return null
|
|
||||||
val credential = Credentials.basic(login, password)
|
|
||||||
return response.request.newBuilder()
|
|
||||||
.header(CommonHeaders.PROXY_AUTHORIZATION, credential)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getPasswordAuthentication(): PasswordAuthentication? {
|
|
||||||
if (!isProxyEnabled()) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
val login = settings.proxyLogin ?: return null
|
|
||||||
val password = settings.proxyPassword ?: return null
|
|
||||||
return PasswordAuthentication(login, password.toCharArray())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isProxyEnabled() = settings.proxyType != Proxy.Type.DIRECT
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
package org.koitharu.kotatsu.core.network.proxy
|
||||||
|
|
||||||
|
import androidx.webkit.ProxyConfig
|
||||||
|
import androidx.webkit.ProxyController
|
||||||
|
import androidx.webkit.WebViewFeature
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.asExecutor
|
||||||
|
import okhttp3.Authenticator
|
||||||
|
import okhttp3.Credentials
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import okhttp3.Route
|
||||||
|
import org.koitharu.kotatsu.core.exceptions.ProxyConfigException
|
||||||
|
import org.koitharu.kotatsu.core.network.CommonHeaders
|
||||||
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
|
||||||
|
import java.net.InetSocketAddress
|
||||||
|
import java.net.PasswordAuthentication
|
||||||
|
import java.net.Proxy
|
||||||
|
import java.net.ProxySelector
|
||||||
|
import java.net.SocketAddress
|
||||||
|
import java.net.URI
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.suspendCoroutine
|
||||||
|
import java.net.Authenticator as JavaAuthenticator
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class ProxyProvider @Inject constructor(
|
||||||
|
private val settings: AppSettings,
|
||||||
|
) {
|
||||||
|
|
||||||
|
private var cachedProxy: Proxy? = null
|
||||||
|
|
||||||
|
val selector = object : ProxySelector() {
|
||||||
|
override fun select(uri: URI?): List<Proxy> {
|
||||||
|
return listOf(getProxy())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun connectFailed(uri: URI?, sa: SocketAddress?, ioe: okio.IOException?) {
|
||||||
|
ioe?.printStackTraceDebug()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val authenticator = ProxyAuthenticator()
|
||||||
|
|
||||||
|
init {
|
||||||
|
ProxySelector.setDefault(selector)
|
||||||
|
JavaAuthenticator.setDefault(authenticator)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun applyWebViewConfig() {
|
||||||
|
val isProxyEnabled = isProxyEnabled()
|
||||||
|
if (!WebViewFeature.isFeatureSupported(WebViewFeature.PROXY_OVERRIDE)) {
|
||||||
|
if (isProxyEnabled) {
|
||||||
|
throw IllegalArgumentException("Proxy for WebView is not supported") // TODO localize
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val controller = ProxyController.getInstance()
|
||||||
|
if (settings.proxyType == Proxy.Type.DIRECT) {
|
||||||
|
suspendCoroutine { cont ->
|
||||||
|
controller.clearProxyOverride(
|
||||||
|
(cont.context[CoroutineDispatcher] ?: Dispatchers.Main).asExecutor(),
|
||||||
|
) {
|
||||||
|
cont.resume(Unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val url = buildString {
|
||||||
|
when (settings.proxyType) {
|
||||||
|
Proxy.Type.DIRECT -> Unit
|
||||||
|
Proxy.Type.HTTP -> append("http")
|
||||||
|
Proxy.Type.SOCKS -> append("socks")
|
||||||
|
}
|
||||||
|
append("://")
|
||||||
|
append(settings.proxyAddress)
|
||||||
|
append(':')
|
||||||
|
append(settings.proxyPort)
|
||||||
|
}
|
||||||
|
if (settings.proxyType == Proxy.Type.SOCKS) {
|
||||||
|
System.setProperty("java.net.socks.username", settings.proxyLogin);
|
||||||
|
System.setProperty("java.net.socks.password", settings.proxyPassword);
|
||||||
|
}
|
||||||
|
val proxyConfig = ProxyConfig.Builder()
|
||||||
|
.addProxyRule(url)
|
||||||
|
.build()
|
||||||
|
suspendCoroutine { cont ->
|
||||||
|
controller.setProxyOverride(
|
||||||
|
proxyConfig,
|
||||||
|
(cont.context[CoroutineDispatcher] ?: Dispatchers.Main).asExecutor(),
|
||||||
|
) {
|
||||||
|
cont.resume(Unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isProxyEnabled() = settings.proxyType != Proxy.Type.DIRECT
|
||||||
|
|
||||||
|
private fun getProxy(): Proxy {
|
||||||
|
val type = settings.proxyType
|
||||||
|
val address = settings.proxyAddress
|
||||||
|
val port = settings.proxyPort
|
||||||
|
if (type == Proxy.Type.DIRECT) {
|
||||||
|
return Proxy.NO_PROXY
|
||||||
|
}
|
||||||
|
if (address.isNullOrEmpty() || port < 0 || port > 0xFFFF) {
|
||||||
|
throw ProxyConfigException()
|
||||||
|
}
|
||||||
|
cachedProxy?.let {
|
||||||
|
val addr = it.address() as? InetSocketAddress
|
||||||
|
if (addr != null && it.type() == type && addr.port == port && addr.hostString == address) {
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val proxy = Proxy(type, InetSocketAddress(address, port))
|
||||||
|
cachedProxy = proxy
|
||||||
|
return proxy
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class ProxyAuthenticator : Authenticator, JavaAuthenticator() {
|
||||||
|
|
||||||
|
override fun authenticate(route: Route?, response: Response): Request? {
|
||||||
|
if (!isProxyEnabled()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (response.request.header(CommonHeaders.PROXY_AUTHORIZATION) != null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
val login = settings.proxyLogin ?: return null
|
||||||
|
val password = settings.proxyPassword ?: return null
|
||||||
|
val credential = Credentials.basic(login, password)
|
||||||
|
return response.request.newBuilder()
|
||||||
|
.header(CommonHeaders.PROXY_AUTHORIZATION, credential)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun getPasswordAuthentication(): PasswordAuthentication? {
|
||||||
|
if (!isProxyEnabled()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
val login = settings.proxyLogin ?: return null
|
||||||
|
val password = settings.proxyPassword ?: return null
|
||||||
|
return PasswordAuthentication(login, password.toCharArray())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,6 +43,7 @@ class ProxySettingsFragment : BasePreferenceFragment(R.string.proxy),
|
|||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
addPreferencesFromResource(R.xml.pref_proxy)
|
addPreferencesFromResource(R.xml.pref_proxy)
|
||||||
|
@Suppress("UsePropertyAccessSyntax")
|
||||||
findPreference<EditTextPreference>(AppSettings.KEY_PROXY_ADDRESS)?.setOnBindEditTextListener(
|
findPreference<EditTextPreference>(AppSettings.KEY_PROXY_ADDRESS)?.setOnBindEditTextListener(
|
||||||
EditTextBindListener(
|
EditTextBindListener(
|
||||||
inputType = EditorInfo.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_URI,
|
inputType = EditorInfo.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_URI,
|
||||||
@@ -50,6 +51,7 @@ class ProxySettingsFragment : BasePreferenceFragment(R.string.proxy),
|
|||||||
validator = DomainValidator(),
|
validator = DomainValidator(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@Suppress("UsePropertyAccessSyntax")
|
||||||
findPreference<EditTextPreference>(AppSettings.KEY_PROXY_PORT)?.setOnBindEditTextListener(
|
findPreference<EditTextPreference>(AppSettings.KEY_PROXY_PORT)?.setOnBindEditTextListener(
|
||||||
EditTextBindListener(
|
EditTextBindListener(
|
||||||
inputType = EditorInfo.TYPE_CLASS_NUMBER,
|
inputType = EditorInfo.TYPE_CLASS_NUMBER,
|
||||||
@@ -58,6 +60,7 @@ class ProxySettingsFragment : BasePreferenceFragment(R.string.proxy),
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
findPreference<EditTextPreference>(AppSettings.KEY_PROXY_PASSWORD)?.let { pref ->
|
findPreference<EditTextPreference>(AppSettings.KEY_PROXY_PASSWORD)?.let { pref ->
|
||||||
|
@Suppress("UsePropertyAccessSyntax")
|
||||||
pref.setOnBindEditTextListener(
|
pref.setOnBindEditTextListener(
|
||||||
EditTextBindListener(
|
EditTextBindListener(
|
||||||
inputType = EditorInfo.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_PASSWORD,
|
inputType = EditorInfo.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_PASSWORD,
|
||||||
|
|||||||
@@ -1,30 +1,26 @@
|
|||||||
package org.koitharu.kotatsu.settings.sources.auth
|
package org.koitharu.kotatsu.settings.sources.auth
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.result.contract.ActivityResultContract
|
import androidx.activity.result.contract.ActivityResultContract
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.core.view.isVisible
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import androidx.core.view.updatePadding
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.browser.BaseBrowserActivity
|
||||||
import org.koitharu.kotatsu.browser.BrowserCallback
|
import org.koitharu.kotatsu.browser.BrowserCallback
|
||||||
import org.koitharu.kotatsu.browser.BrowserClient
|
import org.koitharu.kotatsu.browser.BrowserClient
|
||||||
import org.koitharu.kotatsu.browser.ProgressChromeClient
|
|
||||||
import org.koitharu.kotatsu.browser.WebViewBackPressedCallback
|
|
||||||
import org.koitharu.kotatsu.core.model.MangaSource
|
import org.koitharu.kotatsu.core.model.MangaSource
|
||||||
import org.koitharu.kotatsu.core.nav.AppRouter
|
import org.koitharu.kotatsu.core.nav.AppRouter
|
||||||
import org.koitharu.kotatsu.core.network.CommonHeaders
|
import org.koitharu.kotatsu.core.network.CommonHeaders
|
||||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||||
import org.koitharu.kotatsu.core.parser.ParserMangaRepository
|
import org.koitharu.kotatsu.core.parser.ParserMangaRepository
|
||||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.configureForParser
|
import org.koitharu.kotatsu.core.util.ext.configureForParser
|
||||||
import org.koitharu.kotatsu.core.util.ext.consumeAll
|
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
|
||||||
import org.koitharu.kotatsu.databinding.ActivityBrowserBinding
|
import org.koitharu.kotatsu.databinding.ActivityBrowserBinding
|
||||||
import org.koitharu.kotatsu.parsers.MangaParserAuthProvider
|
import org.koitharu.kotatsu.parsers.MangaParserAuthProvider
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaParserSource
|
import org.koitharu.kotatsu.parsers.model.MangaParserSource
|
||||||
@@ -34,15 +30,13 @@ import javax.inject.Inject
|
|||||||
import com.google.android.material.R as materialR
|
import com.google.android.material.R as materialR
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class SourceAuthActivity : BaseActivity<ActivityBrowserBinding>(), BrowserCallback {
|
class SourceAuthActivity : BaseBrowserActivity(), BrowserCallback {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var mangaRepositoryFactory: MangaRepository.Factory
|
lateinit var mangaRepositoryFactory: MangaRepository.Factory
|
||||||
|
|
||||||
private lateinit var onBackPressedCallback: WebViewBackPressedCallback
|
|
||||||
private lateinit var authProvider: MangaParserAuthProvider
|
private lateinit var authProvider: MangaParserAuthProvider
|
||||||
|
|
||||||
@SuppressLint("SetJavaScriptEnabled")
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
if (!setContentViewWebViewSafe { ActivityBrowserBinding.inflate(layoutInflater) }) {
|
if (!setContentViewWebViewSafe { ActivityBrowserBinding.inflate(layoutInflater) }) {
|
||||||
@@ -68,43 +62,22 @@ class SourceAuthActivity : BaseActivity<ActivityBrowserBinding>(), BrowserCallba
|
|||||||
setHomeAsUpIndicator(materialR.drawable.abc_ic_clear_material)
|
setHomeAsUpIndicator(materialR.drawable.abc_ic_clear_material)
|
||||||
}
|
}
|
||||||
viewBinding.webView.configureForParser(repository.getRequestHeaders()[CommonHeaders.USER_AGENT])
|
viewBinding.webView.configureForParser(repository.getRequestHeaders()[CommonHeaders.USER_AGENT])
|
||||||
viewBinding.webView.webViewClient = BrowserClient(this)
|
viewBinding.webView.webViewClient = BrowserClient(proxyProvider, this)
|
||||||
viewBinding.webView.webChromeClient = ProgressChromeClient(viewBinding.progressBar)
|
lifecycleScope.launch {
|
||||||
onBackPressedCallback = WebViewBackPressedCallback(viewBinding.webView)
|
try {
|
||||||
onBackPressedDispatcher.addCallback(onBackPressedCallback)
|
proxyProvider.applyWebViewConfig()
|
||||||
if (savedInstanceState != null) {
|
} catch (e: Exception) {
|
||||||
return
|
Snackbar.make(viewBinding.webView, e.getDisplayMessage(resources), Snackbar.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
val url = authProvider.authUrl
|
||||||
|
onTitleChanged(
|
||||||
|
source.title,
|
||||||
|
getString(R.string.loading_),
|
||||||
|
)
|
||||||
|
viewBinding.webView.loadUrl(url)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val url = authProvider.authUrl
|
|
||||||
onTitleChanged(
|
|
||||||
source.title,
|
|
||||||
getString(R.string.loading_),
|
|
||||||
)
|
|
||||||
viewBinding.webView.loadUrl(url)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
viewBinding.webView.destroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onApplyWindowInsets(
|
|
||||||
v: View,
|
|
||||||
insets: WindowInsetsCompat
|
|
||||||
): WindowInsetsCompat {
|
|
||||||
val type = WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.ime()
|
|
||||||
val barsInsets = insets.getInsets(type)
|
|
||||||
viewBinding.webView.updatePadding(
|
|
||||||
left = barsInsets.left,
|
|
||||||
right = barsInsets.right,
|
|
||||||
bottom = barsInsets.bottom,
|
|
||||||
)
|
|
||||||
viewBinding.appbar.updatePadding(
|
|
||||||
left = barsInsets.left,
|
|
||||||
right = barsInsets.right,
|
|
||||||
top = barsInsets.top,
|
|
||||||
)
|
|
||||||
return insets.consumeAll(type)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
|
||||||
@@ -118,18 +91,8 @@ class SourceAuthActivity : BaseActivity<ActivityBrowserBinding>(), BrowserCallba
|
|||||||
else -> super.onOptionsItemSelected(item)
|
else -> super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
|
||||||
viewBinding.webView.onPause()
|
|
||||||
super.onPause()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
viewBinding.webView.onResume()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLoadingStateChanged(isLoading: Boolean) {
|
override fun onLoadingStateChanged(isLoading: Boolean) {
|
||||||
viewBinding.progressBar.isVisible = isLoading
|
super.onLoadingStateChanged(isLoading)
|
||||||
if (!isLoading && authProvider.isAuthorized) {
|
if (!isLoading && authProvider.isAuthorized) {
|
||||||
Toast.makeText(this, R.string.auth_complete, Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, R.string.auth_complete, Toast.LENGTH_SHORT).show()
|
||||||
setResult(RESULT_OK)
|
setResult(RESULT_OK)
|
||||||
@@ -137,15 +100,6 @@ class SourceAuthActivity : BaseActivity<ActivityBrowserBinding>(), BrowserCallba
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTitleChanged(title: CharSequence, subtitle: CharSequence?) {
|
|
||||||
this.title = title
|
|
||||||
supportActionBar?.subtitle = subtitle
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onHistoryChanged() {
|
|
||||||
onBackPressedCallback.onHistoryChanged()
|
|
||||||
}
|
|
||||||
|
|
||||||
class Contract : ActivityResultContract<MangaSource, Boolean>() {
|
class Contract : ActivityResultContract<MangaSource, Boolean>() {
|
||||||
override fun createIntent(context: Context, input: MangaSource): Intent {
|
override fun createIntent(context: Context, input: MangaSource): Intent {
|
||||||
return AppRouter.sourceAuthIntent(context, input)
|
return AppRouter.sourceAuthIntent(context, input)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import org.koitharu.kotatsu.core.db.entity.TagEntity
|
|||||||
import org.koitharu.kotatsu.favourites.data.FavouriteCategoryEntity
|
import org.koitharu.kotatsu.favourites.data.FavouriteCategoryEntity
|
||||||
import org.koitharu.kotatsu.favourites.data.FavouriteEntity
|
import org.koitharu.kotatsu.favourites.data.FavouriteEntity
|
||||||
import org.koitharu.kotatsu.history.data.HistoryEntity
|
import org.koitharu.kotatsu.history.data.HistoryEntity
|
||||||
|
import org.koitharu.kotatsu.parsers.model.ContentRating
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaParserSource
|
import org.koitharu.kotatsu.parsers.model.MangaParserSource
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaState
|
import org.koitharu.kotatsu.parsers.model.MangaState
|
||||||
import org.koitharu.kotatsu.parsers.model.SortOrder
|
import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||||
@@ -33,15 +34,16 @@ class JsonSerializerTest {
|
|||||||
val entity = MangaEntity(
|
val entity = MangaEntity(
|
||||||
id = 231,
|
id = 231,
|
||||||
title = "Lorem Ipsum",
|
title = "Lorem Ipsum",
|
||||||
altTitle = "Lorem Ispum 2",
|
altTitles = "Lorem Ispum 2",
|
||||||
url = "erw",
|
url = "erw",
|
||||||
publicUrl = "hthth",
|
publicUrl = "hthth",
|
||||||
rating = 0.78f,
|
rating = 0.78f,
|
||||||
isNsfw = true,
|
isNsfw = true,
|
||||||
|
contentRating = ContentRating.ADULT.name,
|
||||||
coverUrl = "5345",
|
coverUrl = "5345",
|
||||||
largeCoverUrl = null,
|
largeCoverUrl = null,
|
||||||
state = MangaState.FINISHED.name,
|
state = MangaState.FINISHED.name,
|
||||||
author = "RERE",
|
authors = "RERE",
|
||||||
source = MangaParserSource.DUMMY.name,
|
source = MangaParserSource.DUMMY.name,
|
||||||
)
|
)
|
||||||
val json = JsonSerializer(entity).toJson()
|
val json = JsonSerializer(entity).toJson()
|
||||||
|
|||||||
Reference in New Issue
Block a user