summaryrefslogtreecommitdiffstats
path: root/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/SubdocumentScrollHelper.java
blob: b581d3147ec1e16d708f9ed358dcbc6ef0ccd6b5 (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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.gecko.gfx;

//import org.mozilla.gecko.GeckoAppShell;
//import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.util.EventDispatcher;
import org.mozilla.gecko.util.GeckoEventListener;

import org.json.JSONException;
import org.json.JSONObject;

import android.graphics.PointF;
import android.os.Handler;
import android.util.Log;

class SubdocumentScrollHelper implements GeckoEventListener {
    private static final String LOGTAG = "GeckoSubdocScroll";

    private static String MESSAGE_PANNING_OVERRIDE = "Panning:Override";
    private static String MESSAGE_CANCEL_OVERRIDE = "Panning:CancelOverride";
    private static String MESSAGE_SCROLL = "Gesture:Scroll";
    private static String MESSAGE_SCROLL_ACK = "Gesture:ScrollAck";

    private final Handler mUiHandler;
    private final EventDispatcher mEventDispatcher;

    /* This is the amount of displacement we have accepted but not yet sent to JS; this is
     * only valid when mOverrideScrollPending is true. */
    private final PointF mPendingDisplacement;

    /* When this is true, we're sending scroll events to JS to scroll the active subdocument. */
    private boolean mOverridePanning;

    /* When this is true, we have received an ack for the last scroll event we sent to JS, and
     * are ready to send the next scroll event. Note we only ever have one scroll event inflight
     * at a time. */
    private boolean mOverrideScrollAck;

    /* When this is true, we have a pending scroll that we need to send to JS; we were unable
     * to send it when it was initially requested because mOverrideScrollAck was not true. */
    private boolean mOverrideScrollPending;

    /* When this is true, the last scroll event we sent actually did some amount of scrolling on
     * the subdocument; we use this to decide when we have reached the end of the subdocument. */
    private boolean mScrollSucceeded;

    SubdocumentScrollHelper(EventDispatcher eventDispatcher) {
        // mUiHandler will be bound to the UI thread since that's where this constructor runs
        mUiHandler = new Handler();
        mPendingDisplacement = new PointF();

        mEventDispatcher = eventDispatcher;
        registerEventListener(MESSAGE_PANNING_OVERRIDE);
        registerEventListener(MESSAGE_CANCEL_OVERRIDE);
        registerEventListener(MESSAGE_SCROLL_ACK);
    }

    void destroy() {
        unregisterEventListener(MESSAGE_PANNING_OVERRIDE);
        unregisterEventListener(MESSAGE_CANCEL_OVERRIDE);
        unregisterEventListener(MESSAGE_SCROLL_ACK);
    }

    private void registerEventListener(String event) {
        mEventDispatcher.registerEventListener(event, this);
    }

    private void unregisterEventListener(String event) {
        mEventDispatcher.unregisterEventListener(event, this);
    }

    boolean scrollBy(PointF displacement) {
        if (! mOverridePanning) {
            return false;
        }

        if (! mOverrideScrollAck) {
            mOverrideScrollPending = true;
            mPendingDisplacement.x += displacement.x;
            mPendingDisplacement.y += displacement.y;
            return true;
        }

        JSONObject json = new JSONObject();
        try {
            json.put("x", displacement.x);
            json.put("y", displacement.y);
        } catch (JSONException e) {
            Log.e(LOGTAG, "Error forming subwindow scroll message: ", e);
        }
        //GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(MESSAGE_SCROLL, json.toString()));

        mOverrideScrollAck = false;
        mOverrideScrollPending = false;
        // clear the |mPendingDisplacement| after serializing |displacement| to
        // JSON because they might be the same object
        mPendingDisplacement.x = 0;
        mPendingDisplacement.y = 0;

        return true;
    }

    void cancel() {
        mOverridePanning = false;
    }

    boolean scrolling() {
        return mOverridePanning;
    }

    boolean lastScrollSucceeded() {
        return mScrollSucceeded;
    }

    // GeckoEventListener implementation

    @Override
    public void handleMessage(final String event, final JSONObject message) {
        // This comes in on the Gecko thread; hand off the handling to the UI thread.
        mUiHandler.post(new Runnable() {
            @Override
            public void run() {
                try {
                    if (MESSAGE_PANNING_OVERRIDE.equals(event)) {
                        mOverridePanning = true;
                        mOverrideScrollAck = true;
                        mOverrideScrollPending = false;
                        mScrollSucceeded = true;
                    } else if (MESSAGE_CANCEL_OVERRIDE.equals(event)) {
                        mOverridePanning = false;
                    } else if (MESSAGE_SCROLL_ACK.equals(event)) {
                        mOverrideScrollAck = true;
                        mScrollSucceeded = message.getBoolean("scrolled");
                        if (mOverridePanning && mOverrideScrollPending) {
                            scrollBy(mPendingDisplacement);
                        }
                    }
                } catch (Exception e) {
                    Log.e(LOGTAG, "Exception handling message", e);
                }
            }
        });
    }
}