|
1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- |
|
2 * This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 package org.mozilla.gecko.gfx; |
|
7 |
|
8 import org.mozilla.gecko.GeckoAppShell; |
|
9 import org.mozilla.gecko.GeckoEvent; |
|
10 import org.mozilla.gecko.EventDispatcher; |
|
11 import org.mozilla.gecko.util.GeckoEventListener; |
|
12 |
|
13 import org.json.JSONException; |
|
14 import org.json.JSONObject; |
|
15 |
|
16 import android.graphics.PointF; |
|
17 import android.os.Handler; |
|
18 import android.util.Log; |
|
19 |
|
20 class SubdocumentScrollHelper implements GeckoEventListener { |
|
21 private static final String LOGTAG = "GeckoSubdocScroll"; |
|
22 |
|
23 private static String MESSAGE_PANNING_OVERRIDE = "Panning:Override"; |
|
24 private static String MESSAGE_CANCEL_OVERRIDE = "Panning:CancelOverride"; |
|
25 private static String MESSAGE_SCROLL = "Gesture:Scroll"; |
|
26 private static String MESSAGE_SCROLL_ACK = "Gesture:ScrollAck"; |
|
27 |
|
28 private final Handler mUiHandler; |
|
29 private final EventDispatcher mEventDispatcher; |
|
30 |
|
31 /* This is the amount of displacement we have accepted but not yet sent to JS; this is |
|
32 * only valid when mOverrideScrollPending is true. */ |
|
33 private final PointF mPendingDisplacement; |
|
34 |
|
35 /* When this is true, we're sending scroll events to JS to scroll the active subdocument. */ |
|
36 private boolean mOverridePanning; |
|
37 |
|
38 /* When this is true, we have received an ack for the last scroll event we sent to JS, and |
|
39 * are ready to send the next scroll event. Note we only ever have one scroll event inflight |
|
40 * at a time. */ |
|
41 private boolean mOverrideScrollAck; |
|
42 |
|
43 /* When this is true, we have a pending scroll that we need to send to JS; we were unable |
|
44 * to send it when it was initially requested because mOverrideScrollAck was not true. */ |
|
45 private boolean mOverrideScrollPending; |
|
46 |
|
47 /* When this is true, the last scroll event we sent actually did some amount of scrolling on |
|
48 * the subdocument; we use this to decide when we have reached the end of the subdocument. */ |
|
49 private boolean mScrollSucceeded; |
|
50 |
|
51 SubdocumentScrollHelper(EventDispatcher eventDispatcher) { |
|
52 // mUiHandler will be bound to the UI thread since that's where this constructor runs |
|
53 mUiHandler = new Handler(); |
|
54 mPendingDisplacement = new PointF(); |
|
55 |
|
56 mEventDispatcher = eventDispatcher; |
|
57 registerEventListener(MESSAGE_PANNING_OVERRIDE); |
|
58 registerEventListener(MESSAGE_CANCEL_OVERRIDE); |
|
59 registerEventListener(MESSAGE_SCROLL_ACK); |
|
60 } |
|
61 |
|
62 void destroy() { |
|
63 unregisterEventListener(MESSAGE_PANNING_OVERRIDE); |
|
64 unregisterEventListener(MESSAGE_CANCEL_OVERRIDE); |
|
65 unregisterEventListener(MESSAGE_SCROLL_ACK); |
|
66 } |
|
67 |
|
68 private void registerEventListener(String event) { |
|
69 mEventDispatcher.registerEventListener(event, this); |
|
70 } |
|
71 |
|
72 private void unregisterEventListener(String event) { |
|
73 mEventDispatcher.unregisterEventListener(event, this); |
|
74 } |
|
75 |
|
76 boolean scrollBy(PointF displacement) { |
|
77 if (! mOverridePanning) { |
|
78 return false; |
|
79 } |
|
80 |
|
81 if (! mOverrideScrollAck) { |
|
82 mOverrideScrollPending = true; |
|
83 mPendingDisplacement.x += displacement.x; |
|
84 mPendingDisplacement.y += displacement.y; |
|
85 return true; |
|
86 } |
|
87 |
|
88 JSONObject json = new JSONObject(); |
|
89 try { |
|
90 json.put("x", displacement.x); |
|
91 json.put("y", displacement.y); |
|
92 } catch (JSONException e) { |
|
93 Log.e(LOGTAG, "Error forming subwindow scroll message: ", e); |
|
94 } |
|
95 GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(MESSAGE_SCROLL, json.toString())); |
|
96 |
|
97 mOverrideScrollAck = false; |
|
98 mOverrideScrollPending = false; |
|
99 // clear the |mPendingDisplacement| after serializing |displacement| to |
|
100 // JSON because they might be the same object |
|
101 mPendingDisplacement.x = 0; |
|
102 mPendingDisplacement.y = 0; |
|
103 |
|
104 return true; |
|
105 } |
|
106 |
|
107 void cancel() { |
|
108 mOverridePanning = false; |
|
109 } |
|
110 |
|
111 boolean scrolling() { |
|
112 return mOverridePanning; |
|
113 } |
|
114 |
|
115 boolean lastScrollSucceeded() { |
|
116 return mScrollSucceeded; |
|
117 } |
|
118 |
|
119 // GeckoEventListener implementation |
|
120 |
|
121 @Override |
|
122 public void handleMessage(final String event, final JSONObject message) { |
|
123 // This comes in on the Gecko thread; hand off the handling to the UI thread. |
|
124 mUiHandler.post(new Runnable() { |
|
125 @Override |
|
126 public void run() { |
|
127 try { |
|
128 if (MESSAGE_PANNING_OVERRIDE.equals(event)) { |
|
129 mOverridePanning = true; |
|
130 mOverrideScrollAck = true; |
|
131 mOverrideScrollPending = false; |
|
132 mScrollSucceeded = true; |
|
133 } else if (MESSAGE_CANCEL_OVERRIDE.equals(event)) { |
|
134 mOverridePanning = false; |
|
135 } else if (MESSAGE_SCROLL_ACK.equals(event)) { |
|
136 mOverrideScrollAck = true; |
|
137 mScrollSucceeded = message.getBoolean("scrolled"); |
|
138 if (mOverridePanning && mOverrideScrollPending) { |
|
139 scrollBy(mPendingDisplacement); |
|
140 } |
|
141 } |
|
142 } catch (Exception e) { |
|
143 Log.e(LOGTAG, "Exception handling message", e); |
|
144 } |
|
145 } |
|
146 }); |
|
147 } |
|
148 } |