From 92aad9bf4cd6ec5c1568420e9a2514bd4861438b Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Mon, 1 Jul 2019 11:54:38 -0700 Subject: Disable refresh if not at the top, resolves #1450 --- .../pitchedapps/frost/activities/LoginActivity.kt | 3 + .../pitchedapps/frost/views/SwipeRefreshLayout.kt | 78 ++++++++++++++++++++++ app/src/main/res/layout/activity_debug.xml | 4 +- app/src/main/res/layout/login_webview.xml | 4 +- .../main/res/layout/view_content_base_recycler.xml | 4 +- app/src/main/res/layout/view_content_base_web.xml | 4 +- 6 files changed, 89 insertions(+), 8 deletions(-) create mode 100644 app/src/main/kotlin/com/pitchedapps/frost/views/SwipeRefreshLayout.kt (limited to 'app/src/main') diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt index ed207896..42a0f6bb 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt @@ -87,6 +87,9 @@ class LoginActivity : BaseActivity() { toolbar(toolbar) } profileLoader = GlideApp.with(profile) + swipeRefresh.setOnChildScrollUpCallback { parent, child -> + web.canScrollVertically(-1) + } launch { for (refreshing in refreshChannel.uniqueOnly(this)) { if (refreshing) swipeRefresh.isEnabled = true diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/SwipeRefreshLayout.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/SwipeRefreshLayout.kt new file mode 100644 index 00000000..0ab11fc6 --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/SwipeRefreshLayout.kt @@ -0,0 +1,78 @@ +package com.pitchedapps.frost.views + +import android.content.Context +import android.util.AttributeSet +import android.view.MotionEvent +import android.view.View +import android.view.ViewConfiguration +import android.webkit.WebView +import android.widget.ListView +import androidx.core.widget.ListViewCompat +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnChildScrollUpCallback +import com.pitchedapps.frost.utils.L + +/** + * Variant that forbids refreshing if child layout is not at the top + * Inspired by https://github.com/slapperwan/gh4a/blob/master/app/src/main/java/com/gh4a/widget/SwipeRefreshLayout.java + * + */ +class SwipeRefreshLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : + SwipeRefreshLayout(context, attrs) { + + private var preventRefresh: Boolean = false + private var downY: Float = -1f + private val touchSlop = ViewConfiguration.get(context).scaledTouchSlop + + /** + * Copy of [canChildScrollUp], with additional support if necessary + */ + private val canChildScrollUp = OnChildScrollUpCallback { parent, child -> + when (child) { + is WebView -> child.canScrollVertically(-1).apply { + L.d { "Webview can scroll up $this" } + } + is ListView -> ListViewCompat.canScrollList(child, -1) + // Supports webviews as well + else -> child?.canScrollVertically(-1) ?: false + } + } + + init { + setOnChildScrollUpCallback(canChildScrollUp) + } + + override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { + if (ev.action != MotionEvent.ACTION_DOWN && preventRefresh) { + return false + } + when (ev.action) { + MotionEvent.ACTION_DOWN -> { + downY = ev.y + preventRefresh = canChildScrollUp() + } + MotionEvent.ACTION_MOVE -> { + if (downY - ev.y > touchSlop) { + preventRefresh = true + return false + } + } + } + return super.onInterceptTouchEvent(ev) + } + + override fun onNestedScroll(target: View, dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int) { + if (preventRefresh) { + /* + * Ignoring offsetInWindow since + * 1. It doesn't seem to matter in the typical use case + * 2. It isn't being transferred to the underlying array used by the super class + */ + dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, null) + } else { + super.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed) + } + } + + interface OnRefreshListener : SwipeRefreshLayout.OnRefreshListener +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_debug.xml b/app/src/main/res/layout/activity_debug.xml index 55553f42..c3818749 100644 --- a/app/src/main/res/layout/activity_debug.xml +++ b/app/src/main/res/layout/activity_debug.xml @@ -16,7 +16,7 @@ app:layout_scrollFlags="scroll|enterAlways" app:popupTheme="@style/AppTheme.PopupOverlay" /> - - + diff --git a/app/src/main/res/layout/login_webview.xml b/app/src/main/res/layout/login_webview.xml index 8816383a..48d48589 100644 --- a/app/src/main/res/layout/login_webview.xml +++ b/app/src/main/res/layout/login_webview.xml @@ -1,5 +1,5 @@ - @@ -36,4 +36,4 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/view_content_base_recycler.xml b/app/src/main/res/layout/view_content_base_recycler.xml index 05b14215..ce33ce13 100644 --- a/app/src/main/res/layout/view_content_base_recycler.xml +++ b/app/src/main/res/layout/view_content_base_recycler.xml @@ -6,7 +6,7 @@ android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"> - @@ -19,7 +19,7 @@ android:focusableInTouchMode="true" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> - + - @@ -19,7 +19,7 @@ android:focusableInTouchMode="true" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> - +