|
|
@@ -0,0 +1,674 @@
|
|
|
+package com.adealink.weparty.webview
|
|
|
+
|
|
|
+import android.annotation.SuppressLint
|
|
|
+import android.content.Context
|
|
|
+import android.os.Build
|
|
|
+import android.util.AttributeSet
|
|
|
+import android.view.MotionEvent
|
|
|
+import android.view.VelocityTracker
|
|
|
+import android.view.View
|
|
|
+import android.view.ViewConfiguration
|
|
|
+import android.view.ViewParent
|
|
|
+import android.webkit.WebSettings
|
|
|
+import android.webkit.WebView
|
|
|
+import android.widget.OverScroller
|
|
|
+import androidx.core.view.NestedScrollingChild3
|
|
|
+import androidx.core.view.NestedScrollingChildHelper
|
|
|
+import androidx.core.view.ViewCompat
|
|
|
+import com.adealink.frame.base.AppBase
|
|
|
+import com.adealink.frame.log.Log
|
|
|
+import com.adealink.weparty.webview.callback.IWebViewCallback
|
|
|
+import com.adealink.weparty.webview.constant.TAG_WEB_VIEW
|
|
|
+import com.adealink.weparty.webview.jsbridge.JSBridge
|
|
|
+import com.adealink.weparty.webview.jsbridge.data.NativeMessage
|
|
|
+import com.adealink.weparty.webview.jsbridge.method.JSNativeMethod
|
|
|
+import com.adealink.weparty.webview.jsnativemethod.addCommonMethod
|
|
|
+import com.ushareit.easysdk.web.view.SPWebView
|
|
|
+import kotlin.math.abs
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ */
|
|
|
+abstract class BaseWebView : SPWebView, IWebView, NestedScrollingChild3 {
|
|
|
+
|
|
|
+ companion object {
|
|
|
+ private const val INVALID_POINTER = -1
|
|
|
+ }
|
|
|
+
|
|
|
+ private var lUrl: String? = null
|
|
|
+
|
|
|
+ private var jsBridges = hashMapOf<String, JSBridge>()
|
|
|
+ override var webViewCallback: IWebViewCallback? = null
|
|
|
+
|
|
|
+ private var commonWebViewClient: CommonWebViewClient? = null
|
|
|
+ private var commonWebChromeClient: CommonWebChromeClient? = null
|
|
|
+
|
|
|
+ private val scrollOffset = IntArray(2)
|
|
|
+ private val scrollConsumed = IntArray(2)
|
|
|
+ private var lastMotionY = 0F
|
|
|
+ private lateinit var childHelper: NestedScrollingChildHelper
|
|
|
+ private var isBeingDragged = false
|
|
|
+ private var velocityTracker: VelocityTracker? = null
|
|
|
+ private var touchSlop = 0
|
|
|
+ private var activePointerId = INVALID_POINTER
|
|
|
+ private var nestedYOffset = 0F
|
|
|
+ private lateinit var scroller: OverScroller
|
|
|
+ private var minimumVelocity = 0F
|
|
|
+ private var maximumVelocity = 0F
|
|
|
+ private var lastScrollerY = 0
|
|
|
+
|
|
|
+ constructor(context: Context) : super(context) {
|
|
|
+ init()
|
|
|
+ }
|
|
|
+
|
|
|
+ constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
|
|
|
+ init()
|
|
|
+ }
|
|
|
+
|
|
|
+ constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) :
|
|
|
+ super(context, attrs, defStyleAttr) {
|
|
|
+ init()
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun init() {
|
|
|
+ initScrollView()
|
|
|
+ initWebViewSetting()
|
|
|
+ initJSBridge(createDefaultJSBridge())
|
|
|
+ initWebViewClient()
|
|
|
+ initWebChromeClient()
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressLint("SetJavaScriptEnabled")
|
|
|
+ open fun initWebViewSetting() {
|
|
|
+ settings.domStorageEnabled = true
|
|
|
+ settings.javaScriptEnabled = true
|
|
|
+ settings.useWideViewPort = true
|
|
|
+ settings.defaultTextEncodingName = "utf-8"
|
|
|
+ settings.cacheMode = WebSettings.LOAD_DEFAULT
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
|
+ //5.0以上开启混合模式加载
|
|
|
+ settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
|
|
|
+ }
|
|
|
+ if (!AppBase.isRelease && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
|
|
+ setWebContentsDebuggingEnabled(true)
|
|
|
+ }
|
|
|
+// settings.setUserAgentString(sb) 增加UA
|
|
|
+ settings.builtInZoomControls = false
|
|
|
+ settings.setSupportZoom(false)
|
|
|
+ settings.saveFormData = false
|
|
|
+ settings.savePassword = false
|
|
|
+ settings.allowFileAccess = true
|
|
|
+ settings.useWideViewPort = true
|
|
|
+ settings.loadWithOverviewMode = true
|
|
|
+ settings.javaScriptCanOpenWindowsAutomatically = true
|
|
|
+ settings.mediaPlaybackRequiresUserGesture = false
|
|
|
+ }
|
|
|
+
|
|
|
+ abstract fun createDefaultJSBridge(): JSBridge
|
|
|
+
|
|
|
+ @SuppressLint("JavascriptInterface")
|
|
|
+ protected fun initJSBridge(bridge: JSBridge) {
|
|
|
+ Log.d(
|
|
|
+ TAG_WEB_VIEW,
|
|
|
+ "initJSBridge, jsBridge:$bridge, interfaceName:${bridge.interfaceName()}"
|
|
|
+ )
|
|
|
+ addJSBridge(bridge)
|
|
|
+ }
|
|
|
+
|
|
|
+ open fun addJSNativeMethods(jsBridge: JSBridge) {}
|
|
|
+
|
|
|
+ open fun addJSNativeMethod(method: JSNativeMethod<*, *>) {
|
|
|
+ for (bridge in jsBridges.values) {
|
|
|
+ bridge.addNativeMethod(method)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressLint("JavascriptInterface")
|
|
|
+ fun addJSBridge(bridge: JSBridge) {
|
|
|
+ jsBridges[bridge.interfaceName()] = bridge
|
|
|
+ Log.d(
|
|
|
+ TAG_WEB_VIEW,
|
|
|
+ "addJSBridge, interfaceName:${bridge.interfaceName()}, jsBridge:$bridge"
|
|
|
+ )
|
|
|
+ addJavascriptInterface(bridge as Any, bridge.interfaceName())
|
|
|
+ bridge.addCommonMethod(this)
|
|
|
+ addJSNativeMethods(bridge)
|
|
|
+ }
|
|
|
+
|
|
|
+ fun getJSBridge(name: String): JSBridge? {
|
|
|
+ return jsBridges[name]
|
|
|
+ }
|
|
|
+
|
|
|
+ open fun <T> notifyNativeMessage(message: NativeMessage<T>) {
|
|
|
+ // TODO: zhangfei
|
|
|
+ for (bridge in jsBridges.values) {
|
|
|
+ bridge.notifyNativeMessage(message)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun shouldReloadUrl(url: String): Boolean {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ open fun initWebChromeClient() {
|
|
|
+ commonWebChromeClient = CommonWebChromeClient(this)
|
|
|
+ webChromeClient = commonWebChromeClient
|
|
|
+ }
|
|
|
+
|
|
|
+ open fun initWebViewClient() {
|
|
|
+ commonWebViewClient = CommonWebViewClient(this)
|
|
|
+ val constWebViewClient = commonWebViewClient ?: return
|
|
|
+ webViewClient = constWebViewClient
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun getCommonWebChromeClient(): CommonWebChromeClient? {
|
|
|
+ return commonWebChromeClient
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun getCommonWebViewClient(): CommonWebViewClient? {
|
|
|
+ return commonWebViewClient
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun loadUrl(url: String) {
|
|
|
+ lUrl = url
|
|
|
+ super.loadUrl(url)
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun getLoadUrl(): String? {
|
|
|
+ return lUrl
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun getWebView(): WebView {
|
|
|
+ return this
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun clearCache(includeDiskFiles: Boolean) {
|
|
|
+ super.clearCache(includeDiskFiles)
|
|
|
+ try {
|
|
|
+ context.deleteDatabase("webview.db")
|
|
|
+ context.deleteDatabase("webviewCache.db")
|
|
|
+ } catch (e: Exception) {
|
|
|
+ Log.e(TAG_WEB_VIEW, "clearCache, e:${e}")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ open fun initScrollView() {
|
|
|
+ childHelper = NestedScrollingChildHelper(this)
|
|
|
+ overScrollMode = WebView.OVER_SCROLL_NEVER
|
|
|
+ isNestedScrollingEnabled = true
|
|
|
+ scroller = OverScroller(context)
|
|
|
+ val configuration: ViewConfiguration = ViewConfiguration.get(context)
|
|
|
+ touchSlop = configuration.scaledTouchSlop
|
|
|
+ minimumVelocity = configuration.scaledMinimumFlingVelocity.toFloat()
|
|
|
+ maximumVelocity = configuration.scaledMaximumFlingVelocity.toFloat()
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
|
|
|
+ val action: Int = ev.action
|
|
|
+ if (action == MotionEvent.ACTION_MOVE && isBeingDragged) {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ when (action and MotionEvent.ACTION_MASK) {
|
|
|
+ MotionEvent.ACTION_MOVE -> {
|
|
|
+ val activePointerId = activePointerId
|
|
|
+ if (activePointerId == INVALID_POINTER) {
|
|
|
+ return isBeingDragged
|
|
|
+ }
|
|
|
+
|
|
|
+ val pointerIndex: Int = ev.findPointerIndex(activePointerId)
|
|
|
+ if (pointerIndex == -1) {
|
|
|
+ Log.e(
|
|
|
+ TAG_WEB_VIEW, "Invalid pointerId:$activePointerId in onInterceptTouchEvent"
|
|
|
+ )
|
|
|
+ return isBeingDragged
|
|
|
+ }
|
|
|
+ val y = ev.getY(pointerIndex)
|
|
|
+ val yDiff = abs(y - lastMotionY)
|
|
|
+ if (yDiff > touchSlop && nestedScrollAxes and ViewCompat.SCROLL_AXIS_VERTICAL === 0) {
|
|
|
+ isBeingDragged = true
|
|
|
+ lastMotionY = y
|
|
|
+ initVelocityTrackerIfNotExists()
|
|
|
+ velocityTracker?.addMovement(ev)
|
|
|
+ nestedYOffset = 0F
|
|
|
+ val parent: ViewParent? = parent
|
|
|
+ parent?.requestDisallowInterceptTouchEvent(true)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ MotionEvent.ACTION_DOWN -> {
|
|
|
+ lastMotionY = ev.y
|
|
|
+ activePointerId = ev.getPointerId(0)
|
|
|
+ initOrResetVelocityTracker()
|
|
|
+ velocityTracker?.addMovement(ev)
|
|
|
+ scroller.computeScrollOffset()
|
|
|
+ isBeingDragged = !scroller.isFinished
|
|
|
+ startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
|
|
|
+ }
|
|
|
+
|
|
|
+ MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {
|
|
|
+ isBeingDragged = false
|
|
|
+ activePointerId = INVALID_POINTER
|
|
|
+ recycleVelocityTracker()
|
|
|
+ if (scroller.springBack(scrollX, scrollY, 0, 0, 0, getScrollRange())) {
|
|
|
+ ViewCompat.postInvalidateOnAnimation(this)
|
|
|
+ }
|
|
|
+ stopNestedScroll()
|
|
|
+ }
|
|
|
+
|
|
|
+ MotionEvent.ACTION_POINTER_UP -> onSecondaryPointerUp(ev)
|
|
|
+ }
|
|
|
+ return isBeingDragged
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressLint("ClickableViewAccessibility")
|
|
|
+ override fun onTouchEvent(ev: MotionEvent): Boolean {
|
|
|
+ initVelocityTrackerIfNotExists()
|
|
|
+ val vtev: MotionEvent = MotionEvent.obtain(ev)
|
|
|
+ val actionMasked: Int = ev.actionMasked
|
|
|
+ if (actionMasked == MotionEvent.ACTION_DOWN) {
|
|
|
+ nestedYOffset = 0F
|
|
|
+ }
|
|
|
+ vtev.offsetLocation(0F, nestedYOffset)
|
|
|
+ when (actionMasked) {
|
|
|
+ MotionEvent.ACTION_DOWN -> {
|
|
|
+ if (!scroller.isFinished.also { isBeingDragged = it }) {
|
|
|
+ parent?.requestDisallowInterceptTouchEvent(true)
|
|
|
+ }
|
|
|
+ if (!scroller.isFinished) {
|
|
|
+ abortAnimatedScroll()
|
|
|
+ }
|
|
|
+ lastMotionY = ev.y
|
|
|
+ activePointerId = ev.getPointerId(0)
|
|
|
+ startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH)
|
|
|
+ }
|
|
|
+
|
|
|
+ MotionEvent.ACTION_MOVE -> {
|
|
|
+ val activePointerIndex: Int = ev.findPointerIndex(activePointerId)
|
|
|
+ if (activePointerIndex == -1) {
|
|
|
+ Log.e(TAG_WEB_VIEW, "Invalid pointerId:$activePointerId in onTouchEvent")
|
|
|
+ velocityTracker?.addMovement(vtev)
|
|
|
+ vtev.recycle()
|
|
|
+ return super.onTouchEvent(ev)
|
|
|
+ }
|
|
|
+ val y = ev.getY(activePointerIndex)
|
|
|
+ var deltaY = lastMotionY - y
|
|
|
+ if (dispatchNestedPreScroll(
|
|
|
+ 0,
|
|
|
+ deltaY.toInt(),
|
|
|
+ scrollConsumed,
|
|
|
+ scrollOffset,
|
|
|
+ ViewCompat.TYPE_TOUCH
|
|
|
+ )
|
|
|
+ ) {
|
|
|
+ deltaY -= scrollConsumed[1]
|
|
|
+ nestedYOffset += scrollOffset[1]
|
|
|
+ }
|
|
|
+ if (!isBeingDragged && abs(deltaY) > touchSlop) {
|
|
|
+ this.parent?.requestDisallowInterceptTouchEvent(true)
|
|
|
+ isBeingDragged = true
|
|
|
+ if (deltaY > 0) {
|
|
|
+ deltaY -= touchSlop
|
|
|
+ } else {
|
|
|
+ deltaY += touchSlop
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (isBeingDragged) {
|
|
|
+ lastMotionY = y - scrollOffset[1]
|
|
|
+ val oldY = scrollY
|
|
|
+ val range = getScrollRange()
|
|
|
+ // Calling overScrollByCompat will call onOverScrolled, which
|
|
|
+ // calls onScrollChanged if applicable.
|
|
|
+ if (overScrollByCompat(
|
|
|
+ 0, deltaY.toInt(), 0, oldY, 0, range, 0,
|
|
|
+ 0, true
|
|
|
+ ) && !hasNestedScrollingParent(ViewCompat.TYPE_TOUCH)
|
|
|
+ ) {
|
|
|
+ velocityTracker?.clear()
|
|
|
+ }
|
|
|
+ val scrolledDeltaY = scrollY - oldY
|
|
|
+ val unconsumedY = deltaY - scrolledDeltaY
|
|
|
+ scrollConsumed[1] = 0
|
|
|
+ dispatchNestedScroll(
|
|
|
+ 0, scrolledDeltaY, 0, unconsumedY.toInt(), scrollOffset,
|
|
|
+ ViewCompat.TYPE_TOUCH, scrollConsumed
|
|
|
+ )
|
|
|
+ lastMotionY -= scrollOffset[1]
|
|
|
+ nestedYOffset += scrollOffset[1]
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ MotionEvent.ACTION_UP -> {
|
|
|
+ velocityTracker?.computeCurrentVelocity(1000, maximumVelocity)
|
|
|
+ val initialVelocity = velocityTracker?.getYVelocity(activePointerId) ?: 0F
|
|
|
+ if (abs(initialVelocity) > minimumVelocity) {
|
|
|
+ if (!dispatchNestedPreFling(0f, -initialVelocity)) {
|
|
|
+ dispatchNestedFling(0f, -initialVelocity, true)
|
|
|
+ fling(-initialVelocity.toInt())
|
|
|
+ }
|
|
|
+ } else if (scroller.springBack(scrollX, scrollY, 0, 0, 0, getScrollRange())) {
|
|
|
+ ViewCompat.postInvalidateOnAnimation(this)
|
|
|
+ }
|
|
|
+ activePointerId = INVALID_POINTER
|
|
|
+ endDrag()
|
|
|
+ }
|
|
|
+
|
|
|
+ MotionEvent.ACTION_CANCEL -> {
|
|
|
+ if (isBeingDragged) {
|
|
|
+ if (scroller.springBack(scrollX, scrollY, 0, 0, 0, getScrollRange())) {
|
|
|
+ ViewCompat.postInvalidateOnAnimation(this)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ activePointerId = INVALID_POINTER
|
|
|
+ endDrag()
|
|
|
+ }
|
|
|
+
|
|
|
+ MotionEvent.ACTION_POINTER_DOWN -> {
|
|
|
+ val index: Int = ev.actionIndex
|
|
|
+ lastMotionY = ev.getY(index)
|
|
|
+ activePointerId = ev.getPointerId(index)
|
|
|
+ }
|
|
|
+
|
|
|
+ MotionEvent.ACTION_POINTER_UP -> {
|
|
|
+ onSecondaryPointerUp(ev)
|
|
|
+ lastMotionY = ev.getY(ev.findPointerIndex(activePointerId))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ velocityTracker?.addMovement(vtev)
|
|
|
+ vtev.recycle()
|
|
|
+ return super.onTouchEvent(ev)
|
|
|
+ }
|
|
|
+
|
|
|
+ open fun abortAnimatedScroll() {
|
|
|
+ scroller.abortAnimation()
|
|
|
+ stopNestedScroll(ViewCompat.TYPE_NON_TOUCH)
|
|
|
+ }
|
|
|
+
|
|
|
+ open fun endDrag() {
|
|
|
+ isBeingDragged = false
|
|
|
+ recycleVelocityTracker()
|
|
|
+ stopNestedScroll()
|
|
|
+ }
|
|
|
+
|
|
|
+ open fun onSecondaryPointerUp(ev: MotionEvent) {
|
|
|
+ val pointerIndex: Int =
|
|
|
+ (ev.action and MotionEvent.ACTION_POINTER_INDEX_MASK shr MotionEvent.ACTION_POINTER_INDEX_SHIFT)
|
|
|
+ val pointerId: Int = ev.getPointerId(pointerIndex)
|
|
|
+ if (pointerId == activePointerId) {
|
|
|
+ val newPointerIndex = if (pointerIndex == 0) 1 else 0
|
|
|
+ lastMotionY = ev.getY(newPointerIndex)
|
|
|
+ activePointerId = ev.getPointerId(newPointerIndex)
|
|
|
+ velocityTracker?.clear()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ open fun fling(velocityY: Int) {
|
|
|
+ val height = height
|
|
|
+ scroller.fling(
|
|
|
+ scrollX, scrollY,
|
|
|
+ 0, velocityY,
|
|
|
+ 0, 0, Int.MIN_VALUE, Int.MAX_VALUE,
|
|
|
+ 0, height / 2
|
|
|
+ )
|
|
|
+ runAnimatedScroll(true)
|
|
|
+ }
|
|
|
+
|
|
|
+ open fun runAnimatedScroll(participateInNestedScrolling: Boolean) {
|
|
|
+ if (participateInNestedScrolling) {
|
|
|
+ startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_NON_TOUCH)
|
|
|
+ } else {
|
|
|
+ stopNestedScroll(ViewCompat.TYPE_NON_TOUCH)
|
|
|
+ }
|
|
|
+ lastScrollerY = scrollY
|
|
|
+ ViewCompat.postInvalidateOnAnimation(this)
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
|
|
|
+ if (disallowIntercept) {
|
|
|
+ recycleVelocityTracker()
|
|
|
+ }
|
|
|
+ super.requestDisallowInterceptTouchEvent(disallowIntercept)
|
|
|
+ }
|
|
|
+
|
|
|
+ open fun initOrResetVelocityTracker() {
|
|
|
+ if (velocityTracker == null) {
|
|
|
+ velocityTracker = VelocityTracker.obtain()
|
|
|
+ } else {
|
|
|
+ velocityTracker?.clear()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ open fun initVelocityTrackerIfNotExists() {
|
|
|
+ if (velocityTracker == null) {
|
|
|
+ velocityTracker = VelocityTracker.obtain()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ open fun recycleVelocityTracker() {
|
|
|
+ velocityTracker?.recycle()
|
|
|
+ velocityTracker = null
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun overScrollBy(
|
|
|
+ deltaX: Int, deltaY: Int,
|
|
|
+ scrollX: Int, scrollY: Int,
|
|
|
+ scrollRangeX: Int, scrollRangeY: Int,
|
|
|
+ maxOverScrollX: Int, maxOverScrollY: Int,
|
|
|
+ isTouchEvent: Boolean
|
|
|
+ ): Boolean {
|
|
|
+ if (!isBeingDragged) overScrollByCompat(
|
|
|
+ deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY,
|
|
|
+ maxOverScrollX, maxOverScrollY, isTouchEvent
|
|
|
+ )
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ open fun getScrollRange(): Int {
|
|
|
+ return computeVerticalScrollRange()
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun isNestedScrollingEnabled(): Boolean {
|
|
|
+ return childHelper.isNestedScrollingEnabled
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun setNestedScrollingEnabled(enabled: Boolean) {
|
|
|
+ childHelper.isNestedScrollingEnabled = enabled
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun startNestedScroll(axes: Int, type: Int): Boolean {
|
|
|
+ return childHelper.startNestedScroll(axes, type)
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun startNestedScroll(axes: Int): Boolean {
|
|
|
+ return startNestedScroll(axes, ViewCompat.TYPE_TOUCH)
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun stopNestedScroll(type: Int) {
|
|
|
+ childHelper.stopNestedScroll(type)
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun stopNestedScroll() {
|
|
|
+ stopNestedScroll(ViewCompat.TYPE_TOUCH)
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun hasNestedScrollingParent(type: Int): Boolean {
|
|
|
+ return childHelper.hasNestedScrollingParent(type)
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun hasNestedScrollingParent(): Boolean {
|
|
|
+ return hasNestedScrollingParent(ViewCompat.TYPE_TOUCH)
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun dispatchNestedScroll(
|
|
|
+ dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int,
|
|
|
+ offsetInWindow: IntArray?
|
|
|
+ ): Boolean {
|
|
|
+ return dispatchNestedScroll(
|
|
|
+ dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
|
|
|
+ offsetInWindow, ViewCompat.TYPE_TOUCH
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun dispatchNestedScroll(
|
|
|
+ dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int,
|
|
|
+ offsetInWindow: IntArray?, type: Int
|
|
|
+ ): Boolean {
|
|
|
+ return childHelper.dispatchNestedScroll(
|
|
|
+ dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
|
|
|
+ offsetInWindow, type
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun dispatchNestedScroll(
|
|
|
+ dxConsumed: Int,
|
|
|
+ dyConsumed: Int,
|
|
|
+ dxUnconsumed: Int,
|
|
|
+ dyUnconsumed: Int,
|
|
|
+ offsetInWindow: IntArray?,
|
|
|
+ type: Int,
|
|
|
+ consumed: IntArray
|
|
|
+ ) {
|
|
|
+ childHelper.dispatchNestedScroll(
|
|
|
+ dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
|
|
|
+ offsetInWindow, type, consumed
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun dispatchNestedPreScroll(
|
|
|
+ dx: Int,
|
|
|
+ dy: Int,
|
|
|
+ consumed: IntArray?,
|
|
|
+ offsetInWindow: IntArray?
|
|
|
+ ): Boolean {
|
|
|
+ return dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, ViewCompat.TYPE_TOUCH)
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun dispatchNestedPreScroll(
|
|
|
+ dx: Int,
|
|
|
+ dy: Int,
|
|
|
+ consumed: IntArray?,
|
|
|
+ offsetInWindow: IntArray?,
|
|
|
+ type: Int
|
|
|
+ ): Boolean {
|
|
|
+ return childHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, type)
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun dispatchNestedFling(
|
|
|
+ velocityX: Float,
|
|
|
+ velocityY: Float,
|
|
|
+ consumed: Boolean
|
|
|
+ ): Boolean {
|
|
|
+ return childHelper.dispatchNestedFling(velocityX, velocityY, false)
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun dispatchNestedPreFling(velocityX: Float, velocityY: Float): Boolean {
|
|
|
+ return childHelper.dispatchNestedPreFling(velocityX, velocityY)
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun getNestedScrollAxes(): Int {
|
|
|
+ return ViewCompat.SCROLL_AXIS_VERTICAL
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun computeScroll() {
|
|
|
+ if (scroller.isFinished) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ scroller.computeScrollOffset()
|
|
|
+ val y: Int = scroller.currY
|
|
|
+ var unconsumed = y - lastScrollerY
|
|
|
+ lastScrollerY = y
|
|
|
+
|
|
|
+ // Nested Scrolling Pre Pass
|
|
|
+ scrollConsumed[1] = 0
|
|
|
+ dispatchNestedPreScroll(
|
|
|
+ 0, unconsumed, scrollConsumed, null,
|
|
|
+ ViewCompat.TYPE_NON_TOUCH
|
|
|
+ )
|
|
|
+ unconsumed -= scrollConsumed[1]
|
|
|
+ if (unconsumed != 0) {
|
|
|
+ // Internal Scroll
|
|
|
+ val oldScrollY = scrollY
|
|
|
+ overScrollByCompat(
|
|
|
+ 0, unconsumed, scrollX, oldScrollY, 0, getScrollRange(),
|
|
|
+ 0, 0, false
|
|
|
+ )
|
|
|
+ val scrolledByMe = scrollY - oldScrollY
|
|
|
+ unconsumed -= scrolledByMe
|
|
|
+
|
|
|
+ // Nested Scrolling Post Pass
|
|
|
+ scrollConsumed[1] = 0
|
|
|
+ dispatchNestedScroll(
|
|
|
+ 0, 0, 0, unconsumed, scrollOffset,
|
|
|
+ ViewCompat.TYPE_NON_TOUCH, scrollConsumed
|
|
|
+ )
|
|
|
+ unconsumed -= scrollConsumed[1]
|
|
|
+ }
|
|
|
+ if (unconsumed != 0) {
|
|
|
+ abortAnimatedScroll()
|
|
|
+ }
|
|
|
+ if (!scroller.isFinished) {
|
|
|
+ ViewCompat.postInvalidateOnAnimation(this)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // copied from NestedScrollView exacly as it looks, leaving overscroll related code, maybe future use
|
|
|
+ open fun overScrollByCompat(
|
|
|
+ deltaX: Int, deltaY: Int,
|
|
|
+ scrollX: Int, scrollY: Int,
|
|
|
+ scrollRangeX: Int, scrollRangeY: Int,
|
|
|
+ maxOverScrollX: Int, maxOverScrollY: Int,
|
|
|
+ isTouchEvent: Boolean
|
|
|
+ ): Boolean {
|
|
|
+ var maxOverScrollX = maxOverScrollX
|
|
|
+ var maxOverScrollY = maxOverScrollY
|
|
|
+ val overScrollMode = overScrollMode
|
|
|
+ val canScrollHorizontal = computeHorizontalScrollRange() > computeHorizontalScrollExtent()
|
|
|
+ val canScrollVertical = computeVerticalScrollRange() > computeVerticalScrollExtent()
|
|
|
+ val overScrollHorizontal = (overScrollMode == View.OVER_SCROLL_ALWAYS
|
|
|
+ || overScrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollHorizontal)
|
|
|
+ val overScrollVertical = (overScrollMode == View.OVER_SCROLL_ALWAYS
|
|
|
+ || overScrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollVertical)
|
|
|
+ var newScrollX = scrollX + deltaX
|
|
|
+ if (!overScrollHorizontal) {
|
|
|
+ maxOverScrollX = 0
|
|
|
+ }
|
|
|
+ var newScrollY = scrollY + deltaY
|
|
|
+ if (!overScrollVertical) {
|
|
|
+ maxOverScrollY = 0
|
|
|
+ }
|
|
|
+
|
|
|
+ // Clamp values if at the limits and record
|
|
|
+ val left = -maxOverScrollX
|
|
|
+ val right = maxOverScrollX + scrollRangeX
|
|
|
+ val top = -maxOverScrollY
|
|
|
+ val bottom = maxOverScrollY + scrollRangeY
|
|
|
+ var clampedX = false
|
|
|
+ if (newScrollX > right) {
|
|
|
+ newScrollX = right
|
|
|
+ clampedX = true
|
|
|
+ } else if (newScrollX < left) {
|
|
|
+ newScrollX = left
|
|
|
+ clampedX = true
|
|
|
+ }
|
|
|
+ var clampedY = false
|
|
|
+ if (newScrollY > bottom) {
|
|
|
+ newScrollY = bottom
|
|
|
+ clampedY = true
|
|
|
+ } else if (newScrollY < top) {
|
|
|
+ newScrollY = top
|
|
|
+ clampedY = true
|
|
|
+ }
|
|
|
+ if (clampedY && !hasNestedScrollingParent(ViewCompat.TYPE_NON_TOUCH)) {
|
|
|
+ scroller.springBack(newScrollX, newScrollY, 0, 0, 0, getScrollRange())
|
|
|
+ }
|
|
|
+ onOverScrolled(newScrollX, newScrollY, clampedX, clampedY)
|
|
|
+ return clampedX || clampedY
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onDetachedFromWindow() {
|
|
|
+ super.onDetachedFromWindow()
|
|
|
+ commonWebViewClient?.onPageBack(this, originalUrl)
|
|
|
+ }
|
|
|
+
|
|
|
+}
|