Add linear progress bar and refactor WebViewCallback

This commit is contained in:
Grigorii 2022-12-27 15:44:42 +04:00
parent 4ebe674620
commit 49037036b4
8 changed files with 107 additions and 82 deletions

View File

@ -2,6 +2,11 @@ package ru.touchin.extensions
import android.content.res.TypedArray import android.content.res.TypedArray
import androidx.annotation.StyleableRes import androidx.annotation.StyleableRes
import androidx.core.content.res.getResourceIdOrThrow
fun TypedArray.getResourceIdOrNull(@StyleableRes index: Int) = runCatching { getResourceIdOrThrow(index) }.getOrNull() private const val DEFAULT_VALUE = -1
fun TypedArray.getResourceIdOrNull(@StyleableRes index: Int) = getResourceId(index, DEFAULT_VALUE)
.takeIf { it != DEFAULT_VALUE }
fun TypedArray.getColorOrNull(@StyleableRes index: Int) = getColor(index, DEFAULT_VALUE)
.takeIf { it != DEFAULT_VALUE }

View File

@ -14,6 +14,7 @@ dependencies {
implementation "com.google.android.material:material" implementation "com.google.android.material:material"
implementation "androidx.constraintlayout:constraintlayout" implementation "androidx.constraintlayout:constraintlayout"
implementation "androidx.core:core-ktx" implementation "androidx.core:core-ktx"
implementation "androidx.swiperefreshlayout:swiperefreshlayout"
constraints { constraints {
implementation("com.google.android.material:material") { implementation("com.google.android.material:material") {
@ -36,6 +37,11 @@ dependencies {
require '1.3.0' require '1.3.0'
} }
} }
implementation("androidx.swiperefreshlayout:swiperefreshlayout") {
version {
require '1.1.0'
}
}
} }
} }

View File

@ -7,35 +7,37 @@ import android.webkit.WebChromeClient
import android.webkit.WebView import android.webkit.WebView
open class BaseChromeWebViewClient( open class BaseChromeWebViewClient(
private val onJsConfirm: (() -> Unit)? = null, private val callback: WebViewCallback
private val onJsAlert: (() -> Unit)? = null,
private val onJsPrompt: ((String?) -> Unit)? = null,
private val onJsError: ((error: ConsoleMessage) -> Unit)? = null
) : WebChromeClient() { ) : WebChromeClient() {
override fun onJsConfirm(view: WebView?, url: String?, message: String?, result: JsResult?): Boolean { override fun onJsConfirm(view: WebView?, url: String?, message: String?, result: JsResult?): Boolean {
onJsConfirm?.invoke() callback.onJsConfirm(message)
result?.confirm() result?.confirm()
return true return true
} }
override fun onJsAlert(view: WebView?, url: String?, message: String?, result: JsResult?): Boolean { override fun onJsAlert(view: WebView?, url: String?, message: String?, result: JsResult?): Boolean {
onJsAlert?.invoke() callback.onJsAlert(message)
result?.confirm() result?.confirm()
return true return true
} }
override fun onConsoleMessage(consoleMessage: ConsoleMessage?): Boolean { override fun onConsoleMessage(consoleMessage: ConsoleMessage?): Boolean {
if (consoleMessage?.messageLevel() == ConsoleMessage.MessageLevel.ERROR) { if (consoleMessage?.messageLevel() == ConsoleMessage.MessageLevel.ERROR) {
onJsError?.invoke(consoleMessage) callback.onJsError(consoleMessage)
} }
return super.onConsoleMessage(consoleMessage) return super.onConsoleMessage(consoleMessage)
} }
override fun onJsPrompt(view: WebView?, url: String?, message: String?, defaultValue: String?, result: JsPromptResult?): Boolean { override fun onJsPrompt(view: WebView?, url: String?, message: String?, defaultValue: String?, result: JsPromptResult?): Boolean {
onJsPrompt?.invoke(defaultValue) callback.onJsPrompt(defaultValue)
result?.confirm() result?.confirm()
return true return true
} }
override fun onProgressChanged(view: WebView?, newProgress: Int) {
super.onProgressChanged(view, newProgress)
callback.onProgressChanged(newProgress)
}
} }

View File

@ -3,15 +3,14 @@ package ru.touchin.roboswag.webview.web_view
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Color
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.webkit.ConsoleMessage
import android.webkit.CookieManager import android.webkit.CookieManager
import android.webkit.WebView
import androidx.core.content.withStyledAttributes import androidx.core.content.withStyledAttributes
import androidx.core.widget.TextViewCompat import androidx.core.widget.TextViewCompat
import ru.touchin.extensions.getColorOrNull
import ru.touchin.extensions.getResourceIdOrNull
import ru.touchin.extensions.setOnRippleClickListener import ru.touchin.extensions.setOnRippleClickListener
import ru.touchin.roboswag.views.widget.Switcher import ru.touchin.roboswag.views.widget.Switcher
import ru.touchin.roboswag.webview.R import ru.touchin.roboswag.webview.R
@ -26,17 +25,9 @@ open class BaseWebView @JvmOverloads constructor(
private val binding = BaseWebViewBinding.inflate(LayoutInflater.from(context), this) private val binding = BaseWebViewBinding.inflate(LayoutInflater.from(context), this)
var onWebViewLoaded: (() -> Unit)? = null private var isCircleProgressBar = true
var onWebViewRepeatButtonClicked: (() -> Unit)? = null
var onWebViewScrolled: ((WebView, Int, Int) -> Unit)? = null
var onCookieLoaded: ((cookies: Map<String, String>?) -> Unit)? = null
var onJsConfirm: (() -> Unit)? = null private var isPullToRefreshEnabled = false
var onJsAlert: (() -> Unit)? = null
var onJsPrompt: ((defaultValue: String?) -> Unit)? = null
var onJsError: ((error: ConsoleMessage) -> Unit)? = null
var isPullToRefreshEnable = false
set(value) { set(value) {
binding.pullToRefresh.isEnabled = value binding.pullToRefresh.isEnabled = value
binding.pullToRefresh.isRefreshing = false binding.pullToRefresh.isRefreshing = false
@ -45,45 +36,19 @@ open class BaseWebView @JvmOverloads constructor(
var redirectionController = RedirectionController() var redirectionController = RedirectionController()
/**
* If you need to do some action on url click inside WebView, just assign this parameter and disable isRedirectEnable
**/
var actionOnRedirect: ((String?, WebView) -> Unit)? = null
init { init {
binding.pullToRefresh.isEnabled = isPullToRefreshEnable initAttributes(attrs, defStyleAttr)
binding.pullToRefresh.isEnabled = isPullToRefreshEnabled
binding.apply { binding.apply {
context.withStyledAttributes(attrs, R.styleable.BaseWebView, defStyleAttr, 0) {
if (hasValue(R.styleable.BaseWebView_errorTextAppearance)) {
TextViewCompat.setTextAppearance(errorText, getResourceId(R.styleable.BaseWebView_errorTextAppearance, 0))
}
if (hasValue(R.styleable.BaseWebView_repeatButtonTextAppearance)) {
TextViewCompat.setTextAppearance(errorRepeatButton, getResourceId(R.styleable.BaseWebView_repeatButtonTextAppearance, 0))
}
if (hasValue(R.styleable.BaseWebView_progressBarTintColor)) {
progressBar.indeterminateTintList = ColorStateList.valueOf(getColor(R.styleable.BaseWebView_progressBarTintColor, Color.BLACK))
}
if (hasValue(R.styleable.BaseWebView_repeatButtonBackground)) {
errorRepeatButton.setBackgroundResource(getResourceId(R.styleable.BaseWebView_repeatButtonBackground, 0))
}
if (hasValue(R.styleable.BaseWebView_screenBackground)) {
setBackgroundResource(getResourceId(R.styleable.BaseWebView_screenBackground, 0))
}
if (hasValue(R.styleable.BaseWebView_errorText)) {
errorText.text = getString(R.styleable.BaseWebView_errorText)
}
if (hasValue(R.styleable.BaseWebView_repeatButtonText)) {
errorRepeatButton.text = getString(R.styleable.BaseWebView_repeatButtonText)
}
}
pullToRefresh.setOnRefreshListener { pullToRefresh.setOnRefreshListener {
webView.reload() webView.reload()
} }
errorRepeatButton.setOnRippleClickListener { errorRepeatButton.setOnRippleClickListener {
onWebViewRepeatButtonClicked?.invoke() onRepeatButtonClicked()
} }
webView.onScrollChanged = { scrollX, scrollY, _, _ -> webView.onScrollChanged = { scrollX, scrollY, _, _ ->
onWebViewScrolled?.invoke(binding.webView, scrollX, scrollY) onWebViewScrolled(scrollX, scrollY)
} }
setWebViewPreferences() setWebViewPreferences()
} }
@ -92,13 +57,11 @@ open class BaseWebView @JvmOverloads constructor(
override fun onStateChanged(newState: WebViewLoadingState) { override fun onStateChanged(newState: WebViewLoadingState) {
when { when {
newState == WebViewLoadingState.LOADED -> { newState == WebViewLoadingState.LOADED -> {
onWebViewLoaded?.invoke()
binding.pullToRefresh.isRefreshing = false binding.pullToRefresh.isRefreshing = false
showChild(R.id.pull_to_refresh) showChild(R.id.pull_to_refresh)
} }
newState == WebViewLoadingState.LOADING newState == WebViewLoadingState.LOADING && !binding.pullToRefresh.isRefreshing -> {
&& !binding.pullToRefresh.isRefreshing -> { showChild(if (isCircleProgressBar) R.id.progress_bar else R.id.linear_progress_bar)
showChild(R.id.progress_bar)
} }
newState == WebViewLoadingState.ERROR -> { newState == WebViewLoadingState.ERROR -> {
showChild(R.id.error_layout) showChild(R.id.error_layout)
@ -109,17 +72,21 @@ open class BaseWebView @JvmOverloads constructor(
override fun onOverrideUrlLoading(url: String?): Boolean = override fun onOverrideUrlLoading(url: String?): Boolean =
redirectionController.shouldRedirectToUrl(url) redirectionController.shouldRedirectToUrl(url)
override fun onPageCookiesLoaded(cookies: Map<String, String>?) { override fun onRepeatButtonClicked() {
onCookieLoaded?.invoke(cookies) loadUrl(getWebView().url)
} }
override fun actionOnRedirectInsideWebView(webView: WebView, url: String?) { override fun onProgressChanged(progress: Int) {
actionOnRedirect?.invoke(url, webView) binding.linearProgressBar.progress = progress
} }
fun setBaseWebViewClient(isSSlPinningEnable: Boolean = true) { fun setBaseWebViewClient(callback: WebViewCallback = this) {
binding.webView.webViewClient = BaseWebViewClient(this, isSSlPinningEnable) binding.webView.webViewClient = BaseWebViewClient(callback)
binding.webView.webChromeClient = BaseChromeWebViewClient(onJsConfirm, onJsAlert, onJsPrompt, onJsError) binding.webView.webChromeClient = BaseChromeWebViewClient(callback)
}
fun handleBackPressed(closeScreenAction: () -> Unit) {
with(getWebView()) { if (canGoBack()) goBack() else closeScreenAction.invoke() }
} }
fun getWebView() = binding.webView fun getWebView() = binding.webView
@ -168,14 +135,6 @@ open class BaseWebView @JvmOverloads constructor(
) )
} }
fun setState(newState: WebViewLoadingState) {
onStateChanged(newState)
}
fun setOnWebViewDisplayedContentAction(action: () -> Unit) {
binding.webView.onWebViewDisplayedContent = action
}
/** /**
* loadWithOverviewMode loads the WebView completely zoomed out * loadWithOverviewMode loads the WebView completely zoomed out
* useWideViewPort sets page size to fit screen * useWideViewPort sets page size to fit screen
@ -200,4 +159,33 @@ open class BaseWebView @JvmOverloads constructor(
} }
} }
private fun initAttributes(attrs: AttributeSet?, defStyleAttr: Int) = with(binding) {
context.withStyledAttributes(attrs, R.styleable.BaseWebView, defStyleAttr, 0) {
getResourceIdOrNull(R.styleable.BaseWebView_errorTextAppearance)?.let {
TextViewCompat.setTextAppearance(errorText, it)
}
getResourceIdOrNull(R.styleable.BaseWebView_repeatButtonTextAppearance)?.let {
TextViewCompat.setTextAppearance(errorRepeatButton, it)
}
getResourceIdOrNull(R.styleable.BaseWebView_repeatButtonBackground)?.let {
errorRepeatButton.setBackgroundResource(it)
}
getColorOrNull(R.styleable.BaseWebView_progressBarTintColor)?.let {
progressBar.indeterminateTintList = ColorStateList.valueOf(it)
linearProgressBar.indeterminateTintList = ColorStateList.valueOf(it)
}
getResourceIdOrNull(R.styleable.BaseWebView_repeatButtonBackground)?.let {
setBackgroundResource(getResourceId(R.styleable.BaseWebView_screenBackground, 0))
}
getString(R.styleable.BaseWebView_errorText)?.let { errorText.text = it }
getString(R.styleable.BaseWebView_repeatButtonText)?.let { errorRepeatButton.text = it }
isCircleProgressBar = getBoolean(R.styleable.BaseWebView_isCircleProgressBar, true)
isPullToRefreshEnabled = getBoolean(R.styleable.BaseWebView_isPullToRefreshEnabled, false)
onStateChanged(WebViewLoadingState.LOADING)
}
}
} }

View File

@ -62,7 +62,7 @@ open class BaseWebViewClient(
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean { override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
val url = request.url.toString() val url = request.url.toString()
return if (!callback.onOverrideUrlLoading(url) && view.originalUrl != null) { return if (!callback.onOverrideUrlLoading(url) && view.originalUrl != null) {
callback.actionOnRedirectInsideWebView(webView = view, url = url) callback.onRedirectInsideWebView(webView = view, url = url)
true true
} else { } else {
false false
@ -102,9 +102,3 @@ open class BaseWebViewClient(
} }
} }
enum class WebViewLoadingState {
LOADING,
ERROR,
LOADED
}

View File

@ -1,5 +1,6 @@
package ru.touchin.roboswag.webview.web_view package ru.touchin.roboswag.webview.web_view
import android.webkit.ConsoleMessage
import android.webkit.WebView import android.webkit.WebView
interface WebViewCallback { interface WebViewCallback {
@ -8,8 +9,27 @@ interface WebViewCallback {
fun onOverrideUrlLoading(url: String?): Boolean fun onOverrideUrlLoading(url: String?): Boolean
fun onPageCookiesLoaded(cookies: Map<String, String>?) fun onRepeatButtonClicked()
fun actionOnRedirectInsideWebView(webView: WebView, url: String?) fun onPageCookiesLoaded(cookies: Map<String, String>?) = Unit
fun onRedirectInsideWebView(webView: WebView, url: String?) = Unit
fun onWebViewScrolled(scrollX: Int, scrollY: Int) = Unit
fun onJsConfirm(message: String?) = Unit
fun onJsAlert(message: String?) = Unit
fun onJsPrompt(defaultValue: String?) = Unit
fun onJsError(error: ConsoleMessage) = Unit
fun onProgressChanged(progress: Int) = Unit
}
enum class WebViewLoadingState {
LOADING,
ERROR,
LOADED
} }

View File

@ -24,6 +24,14 @@
android:layout_height="32dp" android:layout_height="32dp"
android:layout_gravity="center" /> android:layout_gravity="center" />
<ProgressBar
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:id="@+id/linear_progress_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_gravity="center" />
<LinearLayout <LinearLayout
android:id="@+id/error_layout" android:id="@+id/error_layout"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -9,6 +9,8 @@
<attr name="repeatButtonTextAppearance" format="reference"/> <attr name="repeatButtonTextAppearance" format="reference"/>
<attr name="errorTextAppearance" format="reference"/> <attr name="errorTextAppearance" format="reference"/>
<attr name="progressBarTintColor" format="color"/> <attr name="progressBarTintColor" format="color"/>
<attr name="isCircleProgressBar" format="boolean"/>
<attr name="isPullToRefreshEnabled" format="boolean"/>
</declare-styleable> </declare-styleable>
</resources> </resources>