OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 package org.chromium.chrome.browser.contextualsearch; | |
6 | |
7 import android.util.Log; | |
8 | |
9 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.StateChange Reason; | |
10 import org.chromium.chrome.browser.contextualsearch.ContextualSearchSelectionCon troller.SelectionType; | |
11 | |
12 import javax.annotation.Nullable; | |
13 | |
14 /** | |
15 * Controls the State of the Contextual Search Manager. | |
16 * <p> | |
17 * This class keeps track of the current state of the {@code ContextualSearchMan ager} and helps it | |
18 * to transition between states return to the idle state when work has been inte rrupted. | |
19 * <p> | |
20 * Usage: Call {@link #reset(StateChangeReason)} to reset to the {@code IDLE} st ate.<br> | |
21 * Call {@link #enter(State)} to enter a start-state (when a gesture is recogniz ed). | |
22 * When doing some work in an asynchronous manner:<ol> | |
23 * <li>call {@link #notifyStartingWorkOn(State)} to note that work is starting o n that state | |
24 * <li>call {@link #notifyFinishedWorkOn(State)} when work is completed. | |
25 * <li>If a handler needs to do additional work, such as updating the UX, it sho uld first call | |
26 * {@link #isStillWorkingOn(State)} to check that work has not been interrupted. | |
27 * </ol><p> | |
28 * The {@link #notifyFinishedWorkOn(State)} method will automatically start a tr ansition to the | |
29 * next state. | |
30 * <p> | |
31 * Policy decisions about state transitions should only be done in the private | |
32 * {@link #transitionTo(State)} method of this class, not in the {@code Contextu alSearchManager}. | |
33 */ | |
34 class ContextualSearchStateController { | |
35 private static final String TAG = "Contextual Search"; | |
36 | |
37 private final ContextualSearchSelectionController mSelectionController; | |
38 private final ContextualSearchPolicy mPolicy; | |
39 private final ContextualSearchStateControlled mControlled; | |
Theresa
2017/03/28 15:52:55
I'd prefer a different name for this class. Maybe
Donn Denman
2017/03/29 18:56:51
Changed to ContextualSearchInternalStateHandler.
| |
40 | |
41 /** The current state of the manager. */ | |
42 public static enum State { | |
43 // When not yet initialized or already destroyed only | |
44 UNDEFINED, | |
45 // This is the default resting state. | |
46 IDLE, | |
Theresa
2017/03/28 15:52:55
Is this equivalent to the panel "closed" state (as
Donn Denman
2017/03/29 18:56:51
Yes, updated the comment to reflect that the IDLE
| |
47 | |
48 // This is a sequence of transition states needed to get to the SHOWING_ TAP resting state. | |
Theresa
2017/03/28 15:52:55
s/SHOWING_STATE/SHOWING_LONGPRESS_SEARCH?
Donn Denman
2017/03/29 18:56:51
Done.
| |
49 LONG_PRESS_RECOGNIZED, | |
50 // Transitional state when extending the selection. | |
51 EXTENDING_SELECTION, // needed??? | |
Theresa
2017/03/28 15:52:55
Would this be used when the user is dragging the s
Donn Denman
2017/03/29 18:56:51
Yep. Still not sure it's needed, but we're suppos
| |
52 // Resting state when showing the panel in response to a Long-press gest ure. | |
53 SHOWING_LONGPRESS_SEARCH, | |
54 | |
55 // This is a sequence of transition states needed to get to the SHOWING_ TAP resting state. | |
56 TAP_RECOGNIZED, | |
57 // May be done for Tap or Long-press. | |
58 GATHERING_SURROUNDINGS, | |
59 DECIDING_SUPPRESSION, | |
Theresa
2017/03/28 15:52:55
I expected tap suppression be synchronous (for now
Donn Denman
2017/03/29 18:56:51
This CL adds both sequencing and handling of async
| |
60 WAITING_FOR_POSSIBLE_NAVIGATION, | |
61 SELECTING_WORD, | |
62 // Resolving the Search Term using the surrounding text and additional c ontext. | |
63 RESOLVING, | |
64 // Resting state when showing the panel in response to a Tap gesture. | |
65 SHOWING_TAP_SEARCH | |
66 } | |
67 | |
68 // The current state of this instance. | |
69 private State mState; | |
70 | |
71 // Whether work has started on the current state. | |
72 private boolean mDidStartWork; | |
73 | |
74 /** | |
75 * Constructs an instance of this class, which has the same lifetime as the | |
76 * {@code ContextualSearchManager} and the given parameters. | |
77 */ | |
78 ContextualSearchStateController(ContextualSearchSelectionController selectio nController, | |
79 ContextualSearchPolicy policy, ContextualSearchStateControlled contr olled) { | |
80 mSelectionController = selectionController; | |
81 mPolicy = policy; | |
82 mControlled = controlled; | |
83 } | |
84 | |
85 // ========================================================================= =================== | |
86 // State-transition management. | |
87 // This code is designed to solve several problems: | |
88 // 1) Document the sequencing of handling a gesture in code. Now there's a single method that | |
89 // determines the sequence that should be followed for Tap handling (our most complicated | |
90 // case. | |
91 // 2) Document the initiation and subsequent notification/handling of operat ions. Now the | |
92 // method that starts an operation and the notification handler are tied together by their | |
93 // references to the same state. This allows a simple search to find the | |
94 // initiation and handler together (which is not always easy, e.g. Select WordAroundCaret | |
95 // does not yet have an ACK so we infer that it's complete when the selec tion change -- or | |
96 // does not change after some short waiting period). | |
97 // 3) Gracefully handle sequence interruptions. When an asynchronous operat ion is in progress | |
98 // the user may start a new sequence or abort the current sequence. Now the handler for an | |
99 // asynchronous operation can easily detect that it's no longer working o n that operation | |
100 // and skip the normal completion of the operation. | |
101 // ========================================================================= =================== | |
102 | |
103 /** | |
104 * Reset the current state to the IDLE state. | |
105 * @param reason the reason for the reset | |
106 */ | |
107 void reset(StateChangeReason reason) { | |
108 transitionTo(State.IDLE, reason); | |
109 } | |
110 | |
111 /** | |
112 * Enters the given starting state immediately. | |
113 * @param state the new starting {@link State} we're now in | |
114 */ | |
115 void enter(State state) { | |
116 assert state == State.UNDEFINED || state == State.IDLE | |
117 || state == State.LONG_PRESS_RECOGNIZED || state == State.TAP_RE COGNIZED; | |
118 mState = state; | |
119 System.out.println("ctxs enter : " + mSelectionController.getSelectionTy pe()); | |
120 notifyStartingWorkOn(mState); | |
121 notifyFinishedWorkOn(mState); | |
122 } | |
123 | |
124 /** | |
125 * Confirms that work is starting on the given state. | |
126 * @param state the {@link State} that we're now working on | |
127 */ | |
128 void notifyStartingWorkOn(State state) { | |
129 assert mState == state; | |
130 mDidStartWork = true; | |
131 } | |
132 | |
133 /** | |
134 * @return whether we're still working on the given state | |
135 */ | |
136 boolean isStillWorkingOn(State state) { | |
137 return mState == state; | |
138 } | |
139 | |
140 /** | |
141 * Confirms that work has been finished on the given state. | |
142 * This should be called by every operation that waits for some kind of comp letion when it | |
143 * completes. The operation's start must be flagged using {@link #starting} . | |
144 * @param state the {@link State} that we've finished working on. | |
145 */ | |
146 void notifyFinishedWorkOn(State state) { | |
Theresa
2017/03/28 15:52:55
There's nothing here enforcing the order of state
Donn Denman
2017/03/29 18:56:51
I tried to do this with the initial implementation
| |
147 if (state != mState) return; | |
148 | |
149 assert mDidStartWork; | |
150 | |
151 if (mState == State.IDLE) { | |
152 Log.w(TAG, "Warning, the " + state.toString() + " state was aborted. "); | |
153 return; | |
154 } | |
155 | |
156 switch (state) { | |
157 case LONG_PRESS_RECOGNIZED: | |
158 transitionTo(State.GATHERING_SURROUNDINGS); | |
159 break; | |
160 case TAP_RECOGNIZED: | |
161 transitionTo(State.GATHERING_SURROUNDINGS); | |
162 break; | |
163 case GATHERING_SURROUNDINGS: | |
164 System.out.println("ctxs finished gathering, checking if tap..." | |
165 + mSelectionController.getSelectionType()); | |
166 if (mSelectionController.getSelectionType() == SelectionType.TAP ) { | |
167 transitionTo(State.DECIDING_SUPPRESSION); | |
168 } else { | |
169 // No suppression yet for Long-press. | |
170 transitionTo(State.SHOWING_LONGPRESS_SEARCH, | |
171 StateChangeReason.TEXT_SELECT_LONG_PRESS); | |
172 } | |
173 break; | |
174 case DECIDING_SUPPRESSION: | |
175 transitionTo(State.WAITING_FOR_POSSIBLE_NAVIGATION); | |
176 break; | |
177 case WAITING_FOR_POSSIBLE_NAVIGATION: | |
178 transitionTo(State.SELECTING_WORD); | |
179 break; | |
180 case SELECTING_WORD: | |
181 System.out.println("checking if we should resolve..."); | |
182 if (mPolicy.shouldPreviousTapResolve()) { | |
183 transitionTo(State.RESOLVING); | |
184 } else { | |
185 transitionTo(State.SHOWING_TAP_SEARCH); | |
186 } | |
187 break; | |
188 case RESOLVING: | |
189 transitionTo(State.SHOWING_TAP_SEARCH); | |
190 break; | |
191 default: | |
192 System.out.println("The state " + state.toString() + " is not tr ansitional!"); | |
193 Log.e(TAG, "The state " + state.toString() + " is not transition al!"); | |
194 assert false; | |
195 } | |
196 } | |
197 | |
198 /** | |
199 * @return The current State. | |
200 */ | |
201 State getState() { | |
202 return mState; | |
203 } | |
204 | |
205 /** | |
206 * Establishes the given state by calling code that starts work on that stat e. | |
207 * @param state the new {@link State} to establish. | |
208 */ | |
209 private void transitionTo(State state) { | |
210 transitionTo(state, null); | |
211 } | |
212 | |
213 // TODO(donnd): remove! | |
214 private String nameOf(State state) { | |
215 if (state == null) return "null"; | |
216 return state.name(); | |
217 } | |
218 | |
219 /** | |
220 * Establishes the given state by calling code that starts work on that stat e or simply | |
221 * displays the appropriate UX for that state. | |
222 * @param state the new {@link State} to establish. | |
223 * @param reason the reason we're starting this state, or {@code null} if no t significant | |
224 * or known. Only needed when we enter the IDLE state. | |
225 */ | |
226 private void transitionTo(State state, @Nullable StateChangeReason reason) { | |
227 State previousState = mState; | |
228 mState = state; | |
229 mDidStartWork = false; | |
230 System.out.println( | |
231 "ctxs transition from " + nameOf(previousState) + " to " + nameO f(mState)); | |
232 | |
233 switch (state) { | |
234 case IDLE: | |
235 assert reason != null; | |
236 mControlled.hideContextualSearchUx(reason); | |
237 break; | |
238 case SHOWING_LONGPRESS_SEARCH: | |
239 mControlled.showContextualSearchUx(StateChangeReason.TEXT_SELECT _LONG_PRESS); | |
240 break; | |
241 | |
242 case TAP_RECOGNIZED: | |
243 // Fall through: | |
244 case LONG_PRESS_RECOGNIZED: | |
245 // Fall through: | |
246 case GATHERING_SURROUNDINGS: | |
247 System.out.println( | |
248 "ctxs gather... is tap? " + mSelectionController.getSel ectionType()); | |
249 mControlled.gatherSurroundingText(); | |
250 break; | |
251 case WAITING_FOR_POSSIBLE_NAVIGATION: | |
252 mControlled.waitForPossibleNavigation(); | |
253 break; | |
254 case SELECTING_WORD: | |
255 mControlled.selectWordAroundCaret(); | |
256 break; | |
257 case DECIDING_SUPPRESSION: | |
258 mControlled.decideTapSuppression(); | |
259 break; | |
260 case RESOLVING: | |
261 System.out.println("RESOLVING..."); | |
262 mControlled.showContextualSearchUx(StateChangeReason.TEXT_SELECT _TAP); | |
263 mControlled.startSearchTermResolutionRequest(); | |
264 break; | |
265 case SHOWING_TAP_SEARCH: | |
266 System.out.println("SHOWING_TAP_SEARCH..."); | |
267 // The panel may already be showing a Tap, but we'll make sure i t's showing now. | |
268 mControlled.showContextualSearchUx(StateChangeReason.TEXT_SELECT _TAP); | |
269 break; | |
270 | |
271 default: | |
272 System.out.println("ctxs Warning! Transition ignored to " + stat e); | |
273 break; | |
274 } | |
275 } | |
276 } | |
OLD | NEW |