aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/kotlin/com/pitchedapps/frost/web
diff options
context:
space:
mode:
authorAllan Wang <me@allanwang.ca>2017-12-21 02:16:34 -0500
committerGitHub <noreply@github.com>2017-12-21 02:16:34 -0500
commitd683cae6ffe644a9f63eea6cf3b7e59d2bde617b (patch)
tree517fe1d44c27084ccd87507d9804ba28f15c1647 /app/src/main/kotlin/com/pitchedapps/frost/web
parent82f9aca96493316bc62008f2b3167d34a6029b38 (diff)
downloadfrost-d683cae6ffe644a9f63eea6cf3b7e59d2bde617b.tar.gz
frost-d683cae6ffe644a9f63eea6cf3b7e59d2bde617b.tar.bz2
frost-d683cae6ffe644a9f63eea6cf3b7e59d2bde617b.zip
Enhancement/fragment interface (#564)
* Begin fragment interfaces and themable contracts * Prepare swiperefresh interface * Snapshot * Add compilable version * Revamp once more * Finalize layouts * Cleanup
Diffstat (limited to 'app/src/main/kotlin/com/pitchedapps/frost/web')
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt23
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt39
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt3
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt92
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt33
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewCore.kt203
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/NestedWebView.kt113
7 files changed, 160 insertions, 346 deletions
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt
index 2fa80830..344fcb27 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt
@@ -5,9 +5,10 @@ import android.webkit.*
import ca.allanwang.kau.permissions.PERMISSION_ACCESS_FINE_LOCATION
import ca.allanwang.kau.permissions.kauRequestPermissions
import com.pitchedapps.frost.R
-import com.pitchedapps.frost.contracts.ActivityWebContract
+import com.pitchedapps.frost.contracts.ActivityContract
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.frostSnackbar
+import com.pitchedapps.frost.views.FrostWebView
import io.reactivex.subjects.BehaviorSubject
import io.reactivex.subjects.Subject
@@ -45,12 +46,12 @@ class HeadlessChromeClient : WebChromeClient() {
/**
* The default chrome client
*/
-class FrostChromeClient(webCore: FrostWebViewCore) : WebChromeClient() {
+class FrostChromeClient(web: FrostWebView) : WebChromeClient() {
- val progressObservable: Subject<Int> = webCore.progressObservable
- val titleObservable: BehaviorSubject<String> = webCore.titleObservable
- val activityContract = (webCore.context as? ActivityWebContract)
- val context = webCore.context!!
+ private val progress: Subject<Int> = web.parent.progressObservable
+ private val title: BehaviorSubject<String> = web.parent.titleObservable
+ private val activity = (web.context as? ActivityContract)
+ private val context = web.context!!
override fun onConsoleMessage(consoleMessage: ConsoleMessage): Boolean {
if (consoleBlacklist.any { consoleMessage.message().contains(it) }) return true
@@ -60,18 +61,18 @@ class FrostChromeClient(webCore: FrostWebViewCore) : WebChromeClient() {
override fun onReceivedTitle(view: WebView, title: String) {
super.onReceivedTitle(view, title)
- if (title.contains("http") || titleObservable.value == title) return
- titleObservable.onNext(title)
+ if (title.contains("http") || this.title.value == title) return
+ this.title.onNext(title)
}
override fun onProgressChanged(view: WebView, newProgress: Int) {
super.onProgressChanged(view, newProgress)
- progressObservable.onNext(newProgress)
+ progress.onNext(newProgress)
}
override fun onShowFileChooser(webView: WebView, filePathCallback: ValueCallback<Array<Uri>?>, fileChooserParams: FileChooserParams): Boolean {
- activityContract?.openFileChooser(filePathCallback, fileChooserParams) ?: webView.frostSnackbar(R.string.file_chooser_not_found)
- return activityContract != null
+ activity?.openFileChooser(filePathCallback, fileChooserParams) ?: webView.frostSnackbar(R.string.file_chooser_not_found)
+ return activity != null
}
override fun onGeolocationPermissionsShowPrompt(origin: String, callback: GeolocationPermissions.Callback) {
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt
index 6bdb459e..e8135f5b 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt
@@ -1,31 +1,24 @@
package com.pitchedapps.frost.web
-import android.content.Context
import android.support.v4.widget.SwipeRefreshLayout
import android.webkit.JavascriptInterface
import com.pitchedapps.frost.activities.MainActivity
import com.pitchedapps.frost.contracts.VideoViewHolder
-import com.pitchedapps.frost.dbflow.CookieModel
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.utils.*
+import com.pitchedapps.frost.views.FrostWebView
import io.reactivex.subjects.Subject
/**
* Created by Allan Wang on 2017-06-01.
*/
-class FrostJSI(val webView: FrostWebViewCore) {
+class FrostJSI(val web: FrostWebView) {
- val context: Context
- get() = webView.context
-
- val activity: MainActivity?
- get() = (context as? MainActivity)
-
- val headerObservable: Subject<String>? = activity?.headerBadgeObservable
-
- val cookies: ArrayList<CookieModel>
- get() = activity?.cookies() ?: arrayListOf()
+ private val context = web.context
+ private val activity = context as? MainActivity
+ private val header: Subject<String>? = activity?.headerBadgeObservable
+ private val cookies = activity?.cookies() ?: arrayListOf()
/**
* Attempts to load the url in an overlay
@@ -34,12 +27,12 @@ class FrostJSI(val webView: FrostWebViewCore) {
*/
@JavascriptInterface
fun loadUrl(url: String?): Boolean
- = if (url == null) false else webView.requestWebOverlay(url)
+ = if (url == null) false else web.requestWebOverlay(url)
@JavascriptInterface
fun loadVideo(url: String?, isGif: Boolean) {
if (url != null)
- webView.post {
+ web.post {
(context as? VideoViewHolder)?.showVideo(url, isGif)
?: L.d("Could not load video; contract not implemented")
}
@@ -48,9 +41,9 @@ class FrostJSI(val webView: FrostWebViewCore) {
@JavascriptInterface
fun reloadBaseUrl(animate: Boolean) {
L.d("FrostJSI reload")
- webView.post {
- webView.stopLoading()
- webView.loadBaseUrl(animate)
+ web.post {
+ web.stopLoading()
+ web.reloadBase(animate)
}
}
@@ -58,7 +51,7 @@ class FrostJSI(val webView: FrostWebViewCore) {
fun contextMenu(url: String, text: String?) {
if (!text.isIndependent) return
//url will be formatted through webcontext
- webView.post { context.showWebContextMenu(WebContext(url, text)) }
+ web.post { context.showWebContextMenu(WebContext(url, text)) }
}
/**
@@ -75,7 +68,7 @@ class FrostJSI(val webView: FrostWebViewCore) {
*/
@JavascriptInterface
fun disableSwipeRefresh(disable: Boolean) {
- webView.post { (webView.parent as? SwipeRefreshLayout)?.isEnabled = !disable }
+ web.post { (web.parent as? SwipeRefreshLayout)?.isEnabled = !disable }
}
@JavascriptInterface
@@ -93,19 +86,19 @@ class FrostJSI(val webView: FrostWebViewCore) {
@JavascriptInterface
fun emit(flag: Int) {
- webView.post { webView.frostWebClient.emit(flag) }
+ web.post { web.frostWebClient.emit(flag) }
}
@JavascriptInterface
fun handleHtml(html: String?) {
html ?: return
- webView.post { webView.frostWebClient.handleHtml(html) }
+ web.post { web.frostWebClient.handleHtml(html) }
}
@JavascriptInterface
fun handleHeader(html: String?) {
html ?: return
- headerObservable?.onNext(html)
+ header?.onNext(html)
}
} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt
index d1f144a6..9255b5bb 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt
@@ -9,6 +9,7 @@ import com.pitchedapps.frost.facebook.FbItem
import com.pitchedapps.frost.facebook.USER_AGENT_BASIC
import com.pitchedapps.frost.facebook.formattedFbUrl
import com.pitchedapps.frost.utils.*
+import com.pitchedapps.frost.views.FrostWebView
import org.jetbrains.anko.runOnUiThread
/**
@@ -27,7 +28,7 @@ import org.jetbrains.anko.runOnUiThread
* whether the user agent string should be changed. All propagated results will return false,
* as we have no need of sending a new intent to the same activity
*/
-fun FrostWebViewCore.requestWebOverlay(url: String): Boolean {
+fun FrostWebView.requestWebOverlay(url: String): Boolean {
if (url == "#" || !url.isIndependent) {
L.i("Forbid overlay switch", url)
return false
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt
deleted file mode 100644
index f6d64ab7..00000000
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt
+++ /dev/null
@@ -1,92 +0,0 @@
-package com.pitchedapps.frost.web
-
-import android.annotation.SuppressLint
-import android.content.Context
-import android.graphics.Color
-import android.os.Build
-import android.support.v4.widget.SwipeRefreshLayout
-import android.util.AttributeSet
-import android.view.View
-import android.widget.FrameLayout
-import android.widget.ProgressBar
-import ca.allanwang.kau.utils.*
-import com.pitchedapps.frost.R
-import com.pitchedapps.frost.facebook.FbItem
-import com.pitchedapps.frost.facebook.USER_AGENT_BASIC
-import com.pitchedapps.frost.utils.Prefs
-import com.pitchedapps.frost.utils.frostDownload
-import io.reactivex.android.schedulers.AndroidSchedulers
-
-/**
- * Created by Allan Wang on 2017-06-01.
- */
-class FrostWebView @JvmOverloads constructor(
- context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0
-) : FrameLayout(context, attrs, defStyleAttr, defStyleRes), SwipeRefreshLayout.OnRefreshListener {
-
- val refresh: SwipeRefreshLayout by bindView(R.id.swipe_refresh)
- val web: FrostWebViewCore by bindView(R.id.frost_webview_core)
- val progress: ProgressBar by bindView(R.id.progress_bar)
-
- init {
- inflate(getContext(), R.layout.swipe_webview, this)
- progress.tint(Prefs.textColor.withAlpha(180))
- refresh.setColorSchemeColors(Prefs.iconColor)
- refresh.setProgressBackgroundColorSchemeColor(Prefs.headerColor.withAlpha(255))
- web.progressObservable.observeOn(AndroidSchedulers.mainThread()).subscribe {
- progress.invisibleIf(it == 100)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) progress.setProgress(it, true)
- else progress.progress = it
- }
- web.refreshObservable.observeOn(AndroidSchedulers.mainThread()).subscribe {
- refresh.isRefreshing = it
- refresh.isEnabled = true
- }
- refresh.setOnRefreshListener(this)
- addOnAttachStateChangeListener(object : OnAttachStateChangeListener {
- override fun onViewDetachedFromWindow(v: View) {
- web.visible()
- }
-
- override fun onViewAttachedToWindow(v: View) {}
- })
- }
-
- @SuppressLint("SetJavaScriptEnabled")
- fun setupWebview(url: String, enum: FbItem? = null) {
- with(web) {
- baseUrl = url
- baseEnum = enum
- with(settings) {
- javaScriptEnabled = true
- if (url.shouldUseBasicAgent)
- userAgentString = USER_AGENT_BASIC
- allowFileAccess = true
- textZoom = Prefs.webTextScaling
- }
- setLayerType(View.LAYER_TYPE_HARDWARE, null)
- frostWebClient = baseEnum?.webClient?.invoke(this) ?: FrostWebViewClient(this)
- webViewClient = frostWebClient
- webChromeClient = FrostChromeClient(this)
- addJavascriptInterface(FrostJSI(this), "Frost")
- setBackgroundColor(Color.TRANSPARENT)
- setDownloadListener(context::frostDownload)
- }
- }
-
- //Some urls have postJavascript injections so make sure we load the base url
- override fun onRefresh() {
- when (web.baseUrl) {
- FbItem.MENU.url -> web.loadBaseUrl(true)
- else -> web.reload(true)
- }
- }
-
- fun onBackPressed(): Boolean {
- if (web.canGoBack()) {
- web.goBack()
- return true
- }
- return false
- }
-} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt
index 5fc1ab27..71c71b66 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt
@@ -15,6 +15,7 @@ import com.pitchedapps.frost.facebook.FbItem
import com.pitchedapps.frost.injectors.*
import com.pitchedapps.frost.utils.*
import com.pitchedapps.frost.utils.iab.IS_FROST_PRO
+import com.pitchedapps.frost.views.FrostWebView
import io.reactivex.subjects.Subject
import org.jetbrains.anko.withAlpha
@@ -38,16 +39,16 @@ open class BaseWebViewClient : WebViewClient() {
/**
* The default webview client
*/
-open class FrostWebViewClient(val webCore: FrostWebViewCore) : BaseWebViewClient() {
+open class FrostWebViewClient(val web: FrostWebView) : BaseWebViewClient() {
- val refreshObservable: Subject<Boolean> = webCore.refreshObservable
- val isMain = webCore.baseEnum != null
+ private val refresh: Subject<Boolean> = web.parent.refreshObservable
+ private val isMain = web.parent.baseEnum != null
override fun onPageStarted(view: WebView, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
if (url == null) return
L.d("FWV Loading", url)
- refreshObservable.onNext(true)
+ refresh.onNext(true)
}
fun launchLogin(c: Context) {
@@ -58,10 +59,10 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : BaseWebViewClient
}
private fun injectBackgroundColor() {
- webCore.setBackgroundColor(
+ web.setBackgroundColor(
when {
isMain -> Color.TRANSPARENT
- webCore.url.isFacebookUrl -> Prefs.bgColor.withAlpha(255)
+ web.url.isFacebookUrl -> Prefs.bgColor.withAlpha(255)
else -> Color.WHITE
}
)
@@ -80,7 +81,7 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : BaseWebViewClient
CssHider.PEOPLE_YOU_MAY_KNOW.maybe(!Prefs.showSuggestedFriends && IS_FROST_PRO),
CssHider.SUGGESTED_GROUPS.maybe(!Prefs.showSuggestedGroups && IS_FROST_PRO),
Prefs.themeInjector,
- CssHider.NON_RECENT.maybe((webCore.url?.contains("?sk=h_chr") ?: false)
+ CssHider.NON_RECENT.maybe((web.url?.contains("?sk=h_chr") ?: false)
&& Prefs.aggressiveRecents))
}
@@ -88,7 +89,7 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : BaseWebViewClient
url ?: return
L.d("Page finished", url)
if (!url.isFacebookUrl) {
- refreshObservable.onNext(false)
+ refresh.onNext(false)
return
}
onPageFinishedActions(url)
@@ -96,22 +97,22 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : BaseWebViewClient
open internal fun onPageFinishedActions(url: String) {
if (url.startsWith("${FbItem.MESSAGES.url}/read/") && Prefs.messageScrollToBottom)
- webCore.pageDown(true)
+ web.pageDown(true)
injectAndFinish()
}
internal fun injectAndFinish() {
L.d("Page finished reveal")
- refreshObservable.onNext(false)
+ refresh.onNext(false)
injectBackgroundColor()
- webCore.jsInject(
+ web.jsInject(
JsActions.LOGIN_CHECK,
JsAssets.CLICK_A,
JsAssets.TEXTAREA_LISTENER,
CssHider.ADS.maybe(!Prefs.showFacebookAds && IS_FROST_PRO),
JsAssets.CONTEXT_A,
JsAssets.MEDIA,
- JsAssets.HEADER_BADGES.maybe(webCore.baseEnum != null)
+ JsAssets.HEADER_BADGES.maybe(web.parent.baseEnum != null)
)
}
@@ -130,13 +131,13 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : BaseWebViewClient
*/
private fun launchRequest(request: WebResourceRequest): Boolean {
L.d("Launching Url", request.url?.toString() ?: "null")
- return webCore.requestWebOverlay(request.url.toString())
+ return web.requestWebOverlay(request.url.toString())
}
private fun launchImage(url: String, text: String? = null): Boolean {
L.d("Launching Image", url)
- webCore.context.launchImageActivity(url, text)
- if (webCore.canGoBack()) webCore.goBack()
+ web.context.launchImageActivity(url, text)
+ if (web.canGoBack()) web.goBack()
return true
}
@@ -161,7 +162,7 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : BaseWebViewClient
/**
* Client variant for the menu view
*/
-class FrostWebViewClientMenu(webCore: FrostWebViewCore) : FrostWebViewClient(webCore) {
+class FrostWebViewClientMenu(web: FrostWebView) : FrostWebViewClient(web) {
private val String.shouldInjectMenu
get() = when (removePrefix(FB_URL_BASE)) {
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewCore.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewCore.kt
deleted file mode 100644
index 15383a50..00000000
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewCore.kt
+++ /dev/null
@@ -1,203 +0,0 @@
-package com.pitchedapps.frost.web
-
-import android.animation.ValueAnimator
-import android.annotation.SuppressLint
-import android.content.Context
-import android.support.v4.view.NestedScrollingChild
-import android.support.v4.view.NestedScrollingChildHelper
-import android.support.v4.view.ViewCompat
-import android.util.AttributeSet
-import android.view.MotionEvent
-import android.view.animation.DecelerateInterpolator
-import android.webkit.WebView
-import ca.allanwang.kau.utils.circularReveal
-import ca.allanwang.kau.utils.fadeIn
-import ca.allanwang.kau.utils.fadeOut
-import ca.allanwang.kau.utils.isVisible
-import com.pitchedapps.frost.facebook.FbItem
-import com.pitchedapps.frost.utils.Prefs
-import io.reactivex.Scheduler
-import io.reactivex.android.schedulers.AndroidSchedulers
-import io.reactivex.disposables.Disposable
-import io.reactivex.subjects.BehaviorSubject
-import io.reactivex.subjects.PublishSubject
-
-/**
- * Created by Allan Wang on 2017-05-29.
- *
- */
-class FrostWebViewCore @JvmOverloads constructor(
- context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
-) : WebView(context, attrs, defStyleAttr), NestedScrollingChild {
-
- private val childHelper = NestedScrollingChildHelper(this)
- private var lastY: Int = 0
- private val scrollOffset = IntArray(2)
- private val scrollConsumed = IntArray(2)
- private var nestedOffsetY: Int = 0
- val progressObservable: PublishSubject<Int> // Keeps track of every progress change
- val refreshObservable: PublishSubject<Boolean> // Only emits on page loads
- val titleObservable: BehaviorSubject<String> // Only emits on different non http titles
-
-
- var baseUrl: String? = null
- var baseEnum: FbItem? = null //only viewpager items should pass the base enum
- internal lateinit var frostWebClient: FrostWebViewClient
-
- /**
- * Wrapper to the main userAgentString to cache it.
- * This decouples it from the UiThread
- *
- * Note that this defaults to null, but the main purpose is to
- * check if we've set our own agent.
- *
- * A null value may be interpreted as the default value
- */
- var userAgentString: String? = null
- get() = field
- set(value) {
- field = value
- settings.userAgentString = value
- }
-
- init {
- isNestedScrollingEnabled = true
- progressObservable = PublishSubject.create<Int>()
- refreshObservable = PublishSubject.create<Boolean>()
- titleObservable = BehaviorSubject.create<String>()
- }
-
- fun loadUrl(url: String?, animate: Boolean) {
- if (url == null) return
- registerTransition(animate)
- super.loadUrl(url)
- }
-
- fun reload(animate: Boolean) {
- registerTransition(animate)
- super.reload()
- }
-
- /**
- * Hook onto the refresh observable for one cycle
- * Animate toggles between the fancy ripple and the basic fade
- * The cycle only starts on the first load since there may have been another process when this is registered
- */
- fun registerTransition(animate: Boolean) {
- var dispose: Disposable? = null
- var loading = false
- dispose = refreshObservable.subscribeOn(AndroidSchedulers.mainThread()).subscribe {
- if (it) {
- loading = true
- if (isVisible) fadeOut(duration = 200L)
- } else if (loading) {
- dispose?.dispose()
- if (animate && Prefs.animate) circularReveal(offset = WEB_LOAD_DELAY)
- else fadeIn(duration = 100L)
- }
- }
- }
-
- fun loadBaseUrl(animate: Boolean = true) {
- loadUrl(baseUrl, animate)
- }
-
- fun addTitleListener(subscriber: (title: String) -> Unit, scheduler: Scheduler = AndroidSchedulers.mainThread()): Disposable
- = titleObservable.observeOn(scheduler).subscribe(subscriber)
-
- /**
- * Handle nested scrolling against SwipeRecyclerView
- * Courtesy of takahirom
- *
- * https://github.com/takahirom/webview-in-coordinatorlayout/blob/master/app/src/main/java/com/github/takahirom/webview_in_coodinator_layout/NestedWebView.java
- */
- @SuppressLint("ClickableViewAccessibility")
- override fun onTouchEvent(ev: MotionEvent): Boolean {
- val event = MotionEvent.obtain(ev)
- val action = event.action
- if (action == MotionEvent.ACTION_DOWN)
- nestedOffsetY = 0
- val eventY = event.y.toInt()
- event.offsetLocation(0f, nestedOffsetY.toFloat())
- val returnValue: Boolean
- when (action) {
- MotionEvent.ACTION_MOVE -> {
- var deltaY = lastY - eventY
- // NestedPreScroll
- if (dispatchNestedPreScroll(0, deltaY, scrollConsumed, scrollOffset)) {
- deltaY -= scrollConsumed[1]
- event.offsetLocation(0f, -scrollOffset[1].toFloat())
- nestedOffsetY += scrollOffset[1]
- }
- lastY = eventY - scrollOffset[1]
- returnValue = super.onTouchEvent(event)
- // NestedScroll
- if (dispatchNestedScroll(0, scrollOffset[1], 0, deltaY, scrollOffset)) {
- event.offsetLocation(0f, scrollOffset[1].toFloat())
- nestedOffsetY += scrollOffset[1]
- lastY -= scrollOffset[1]
- }
- }
- MotionEvent.ACTION_DOWN -> {
- returnValue = super.onTouchEvent(event)
- lastY = eventY
- startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
- }
- MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
- returnValue = super.onTouchEvent(event)
- stopNestedScroll()
- }
- else -> return false
- }
- return returnValue
- }
-
- /**
- * If webview is already at the top, refresh
- * Otherwise scroll to top
- */
- fun scrollOrRefresh() {
- if (scrollY < 5) loadBaseUrl()
- else scrollToTop()
- }
-
- fun scrollToTop() {
- flingScroll(0, 0) // stop fling
- if (scrollY > 10000) {
- scrollTo(0, 0)
- } else {
- ValueAnimator.ofInt(scrollY, 0).apply {
- duration = Math.min(scrollY, 500).toLong()
- interpolator = DecelerateInterpolator()
- addUpdateListener { scrollY = it.animatedValue as Int }
- start()
- }
- }
- }
-
- // Nested Scroll implements
- override fun setNestedScrollingEnabled(enabled: Boolean) {
- childHelper.isNestedScrollingEnabled = enabled
- }
-
- override fun isNestedScrollingEnabled() = childHelper.isNestedScrollingEnabled
-
- override fun startNestedScroll(axes: Int) = childHelper.startNestedScroll(axes)
-
- override fun stopNestedScroll() = childHelper.stopNestedScroll()
-
- override fun hasNestedScrollingParent() = childHelper.hasNestedScrollingParent()
-
- override fun dispatchNestedScroll(dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int, offsetInWindow: IntArray?)
- = childHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow)
-
- override fun dispatchNestedPreScroll(dx: Int, dy: Int, consumed: IntArray?, offsetInWindow: IntArray?)
- = childHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow)
-
- override fun dispatchNestedFling(velocityX: Float, velocityY: Float, consumed: Boolean)
- = childHelper.dispatchNestedFling(velocityX, velocityY, consumed)
-
- override fun dispatchNestedPreFling(velocityX: Float, velocityY: Float)
- = childHelper.dispatchNestedPreFling(velocityX, velocityY)
-
-} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/NestedWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/NestedWebView.kt
new file mode 100644
index 00000000..f0bb6137
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/NestedWebView.kt
@@ -0,0 +1,113 @@
+package com.pitchedapps.frost.web
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.support.v4.view.NestedScrollingChild
+import android.support.v4.view.NestedScrollingChildHelper
+import android.support.v4.view.ViewCompat
+import android.util.AttributeSet
+import android.view.MotionEvent
+import android.webkit.WebView
+
+
+/**
+ * Created by Allan Wang on 20/12/17.
+ *
+ * Webview extension that handles nested scrolls
+ */
+open class NestedWebView @JvmOverloads constructor(
+ context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
+) : WebView(context, attrs, defStyleAttr), NestedScrollingChild {
+
+ private lateinit var childHelper: NestedScrollingChildHelper
+ private var lastY: Int = 0
+ private val scrollOffset = IntArray(2)
+ private val scrollConsumed = IntArray(2)
+ private var nestedOffsetY: Int = 0
+
+ init {
+ init()
+ }
+
+ fun init() {
+ // To avoid leaking constructor
+ childHelper = NestedScrollingChildHelper(this)
+ }
+
+ /**
+ * Handle nested scrolling against SwipeRecyclerView
+ * Courtesy of takahirom
+ *
+ * https://github.com/takahirom/webview-in-coordinatorlayout/blob/master/app/src/main/java/com/github/takahirom/webview_in_coodinator_layout/NestedWebView.java
+ */
+ @SuppressLint("ClickableViewAccessibility")
+ override final fun onTouchEvent(ev: MotionEvent): Boolean {
+ val event = MotionEvent.obtain(ev)
+ val action = event.action
+ if (action == MotionEvent.ACTION_DOWN)
+ nestedOffsetY = 0
+ val eventY = event.y.toInt()
+ event.offsetLocation(0f, nestedOffsetY.toFloat())
+ val returnValue: Boolean
+ when (action) {
+ MotionEvent.ACTION_MOVE -> {
+ var deltaY = lastY - eventY
+ // NestedPreScroll
+ if (dispatchNestedPreScroll(0, deltaY, scrollConsumed, scrollOffset)) {
+ deltaY -= scrollConsumed[1]
+ event.offsetLocation(0f, -scrollOffset[1].toFloat())
+ nestedOffsetY += scrollOffset[1]
+ }
+ lastY = eventY - scrollOffset[1]
+ returnValue = super.onTouchEvent(event)
+ // NestedScroll
+ if (dispatchNestedScroll(0, scrollOffset[1], 0, deltaY, scrollOffset)) {
+ event.offsetLocation(0f, scrollOffset[1].toFloat())
+ nestedOffsetY += scrollOffset[1]
+ lastY -= scrollOffset[1]
+ }
+ }
+ MotionEvent.ACTION_DOWN -> {
+ returnValue = super.onTouchEvent(event)
+ lastY = eventY
+ startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
+ }
+ MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
+ returnValue = super.onTouchEvent(event)
+ stopNestedScroll()
+ }
+ else -> return false
+ }
+ return returnValue
+ }
+
+ /*
+ * ---------------------------------------------
+ * Nested Scrolling Content
+ * ---------------------------------------------
+ */
+
+ override final fun setNestedScrollingEnabled(enabled: Boolean) {
+ childHelper.isNestedScrollingEnabled = enabled
+ }
+
+ override final fun isNestedScrollingEnabled() = childHelper.isNestedScrollingEnabled
+
+ override final fun startNestedScroll(axes: Int) = childHelper.startNestedScroll(axes)
+
+ override final fun stopNestedScroll() = childHelper.stopNestedScroll()
+
+ override final fun hasNestedScrollingParent() = childHelper.hasNestedScrollingParent()
+
+ override final fun dispatchNestedScroll(dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int, offsetInWindow: IntArray?)
+ = childHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow)
+
+ override final fun dispatchNestedPreScroll(dx: Int, dy: Int, consumed: IntArray?, offsetInWindow: IntArray?)
+ = childHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow)
+
+ override final fun dispatchNestedFling(velocityX: Float, velocityY: Float, consumed: Boolean)
+ = childHelper.dispatchNestedFling(velocityX, velocityY, consumed)
+
+ override final fun dispatchNestedPreFling(velocityX: Float, velocityY: Float)
+ = childHelper.dispatchNestedPreFling(velocityX, velocityY)
+} \ No newline at end of file