Migrate to MVVM

This commit is contained in:
Koitharu
2020-11-17 21:47:22 +02:00
parent eaac271143
commit 7d24286c55
273 changed files with 1926 additions and 2115 deletions

View File

@@ -0,0 +1,94 @@
package org.koitharu.kotatsu.browser
import android.annotation.SuppressLint
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.core.view.isVisible
import kotlinx.android.synthetic.main.activity_browser.*
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseActivity
@SuppressLint("SetJavaScriptEnabled")
class BrowserActivity : BaseActivity(), BrowserCallback {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_browser)
supportActionBar?.run {
setDisplayHomeAsUpEnabled(true)
setHomeAsUpIndicator(R.drawable.ic_cross)
}
with(webView.settings) {
javaScriptEnabled = true
}
webView.webViewClient = BrowserClient(this)
val url = intent?.dataString
if (url.isNullOrEmpty()) {
finishAfterTransition()
} else {
webView.loadUrl(url)
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.opt_browser, menu)
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
android.R.id.home -> {
webView.stopLoading()
finishAfterTransition()
true
}
R.id.action_browser -> {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(webView.url)
try {
startActivity(Intent.createChooser(intent, item.title))
} catch (_: ActivityNotFoundException) {
}
true
}
else -> super.onOptionsItemSelected(item)
}
override fun onBackPressed() {
if (webView.canGoBack()) {
webView.goBack()
} else {
super.onBackPressed()
}
}
override fun onPause() {
webView.onPause()
super.onPause()
}
override fun onResume() {
super.onResume()
webView.onResume()
}
override fun onLoadingStateChanged(isLoading: Boolean) {
progressBar.isVisible = isLoading
}
override fun onTitleChanged(title: CharSequence, subtitle: CharSequence?) {
this.title = title
supportActionBar?.subtitle = subtitle
}
companion object {
@JvmStatic
fun newIntent(context: Context, url: String) = Intent(context, BrowserActivity::class.java)
.setData(Uri.parse(url))
}
}

View File

@@ -0,0 +1,8 @@
package org.koitharu.kotatsu.browser
interface BrowserCallback {
fun onLoadingStateChanged(isLoading: Boolean)
fun onTitleChanged(title: CharSequence, subtitle: CharSequence?)
}

View File

@@ -0,0 +1,60 @@
package org.koitharu.kotatsu.browser
import android.graphics.Bitmap
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
import android.webkit.WebViewClient
import okhttp3.OkHttpClient
import okhttp3.Request
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import org.koitharu.kotatsu.utils.ext.safe
class BrowserClient(private val callback: BrowserCallback) : WebViewClient(), KoinComponent {
private val okHttp by inject<OkHttpClient>()
override fun onPageFinished(webView: WebView, url: String) {
super.onPageFinished(webView, url)
callback.onLoadingStateChanged(isLoading = false)
}
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
callback.onLoadingStateChanged(isLoading = true)
}
override fun onPageCommitVisible(view: WebView, url: String?) {
super.onPageCommitVisible(view, url)
callback.onTitleChanged(view.title.orEmpty(), url)
}
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?) = false
override fun shouldOverrideUrlLoading(view: WebView, url: String) = false
override fun shouldInterceptRequest(view: WebView?, url: String?): WebResourceResponse? {
return url?.let(::doRequest)
}
override fun shouldInterceptRequest(
view: WebView?,
request: WebResourceRequest?
): WebResourceResponse? {
return request?.url?.toString()?.let(::doRequest)
}
private fun doRequest(url: String): WebResourceResponse? = safe {
val request = Request.Builder()
.url(url)
.build()
val response = okHttp.newCall(request).execute()
val ct = response.body?.contentType()
WebResourceResponse(
"${ct?.type}/${ct?.subtype}",
ct?.charset()?.name() ?: "utf-8",
response.body?.byteStream()
)
}
}

View File

@@ -0,0 +1,8 @@
package org.koitharu.kotatsu.browser.cloudflare
interface CloudFlareCallback {
fun onPageLoaded()
fun onCheckPassed()
}

View File

@@ -0,0 +1,57 @@
package org.koitharu.kotatsu.browser.cloudflare
import android.graphics.Bitmap
import android.webkit.CookieManager
import android.webkit.WebView
import android.webkit.WebViewClient
import okhttp3.Cookie
import okhttp3.CookieJar
import okhttp3.HttpUrl.Companion.toHttpUrl
import org.koin.core.component.KoinComponent
import org.koin.core.component.get
class CloudFlareClient(
private val callback: CloudFlareCallback,
private val targetUrl: String
) : WebViewClient(), KoinComponent {
private val cookieJar = get<CookieJar>()
private val cookieManager = CookieManager.getInstance()
init {
cookieManager.removeAllCookies(null)
}
override fun onPageStarted(view: WebView, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
checkClearance()
}
override fun onPageCommitVisible(view: WebView?, url: String?) {
super.onPageCommitVisible(view, url)
callback.onPageLoaded()
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
callback.onPageLoaded()
}
private fun checkClearance() {
val httpUrl = targetUrl.toHttpUrl()
val cookies = cookieManager.getCookie(targetUrl).split(';').mapNotNull {
Cookie.parse(httpUrl, it)
}
if (cookies.none { it.name == CF_CLEARANCE }) {
return
}
cookieJar.saveFromResponse(httpUrl, cookies)
callback.onCheckPassed()
}
private companion object {
const val CF_UID = "__cfduid"
const val CF_CLEARANCE = "cf_clearance"
}
}

View File

@@ -0,0 +1,78 @@
package org.koitharu.kotatsu.browser.cloudflare
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.View
import android.webkit.CookieManager
import android.webkit.WebSettings
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isInvisible
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import kotlinx.android.synthetic.main.fragment_cloudflare.*
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.AlertDialogFragment
import org.koitharu.kotatsu.core.network.UserAgentInterceptor
import org.koitharu.kotatsu.utils.ext.stringArgument
import org.koitharu.kotatsu.utils.ext.withArgs
class CloudFlareDialog : AlertDialogFragment(R.layout.fragment_cloudflare), CloudFlareCallback {
private val url by stringArgument(ARG_URL)
@SuppressLint("SetJavaScriptEnabled")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
with(webView.settings) {
javaScriptEnabled = true
cacheMode = WebSettings.LOAD_DEFAULT
domStorageEnabled = true
databaseEnabled = true
userAgentString = UserAgentInterceptor.userAgent
}
webView.webViewClient = CloudFlareClient(this, url.orEmpty())
CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true)
if (url.isNullOrEmpty()) {
dismissAllowingStateLoss()
} else {
webView.loadUrl(url.orEmpty())
}
}
override fun onDestroyView() {
webView.stopLoading()
super.onDestroyView()
}
override fun onBuildDialog(builder: AlertDialog.Builder) {
builder.setNegativeButton(android.R.string.cancel, null)
}
override fun onResume() {
super.onResume()
webView.onResume()
}
override fun onPause() {
webView.onPause()
super.onPause()
}
override fun onPageLoaded() {
progressBar?.isInvisible = true
}
override fun onCheckPassed() {
((parentFragment ?: activity) as? SwipeRefreshLayout.OnRefreshListener)?.onRefresh()
dismiss()
}
companion object {
const val TAG = "CloudFlareDialog"
private const val ARG_URL = "url"
fun newInstance(url: String) = CloudFlareDialog().withArgs(1) {
putString(ARG_URL, url)
}
}
}