aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/kotlin/com/pitchedapps/frost/views/SwipeRefreshLayout.kt
blob: f02adcf0f9c34bb73574f77c16c512e27fc8e08f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/*
 * Copyright 2019 Allan Wang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
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

    private var nestedCanChildScrollUp: OnChildScrollUpCallback? = null

    /**
     * Copy of [canChildScrollUp], with additional support if necessary
     */
    private val canChildScrollUp = OnChildScrollUpCallback { parent, child ->
        nestedCanChildScrollUp?.canChildScrollUp(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 setOnChildScrollUpCallback(callback: OnChildScrollUpCallback?) {
        this.nestedCanChildScrollUp = callback
    }

    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)
        }
    }
}