• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.inputmethod.latin.inputlogic;
18 
19 import android.os.Handler;
20 import android.os.HandlerThread;
21 import android.os.Message;
22 
23 import com.android.inputmethod.compat.LooperCompatUtils;
24 import com.android.inputmethod.latin.InputPointers;
25 import com.android.inputmethod.latin.LatinIME;
26 import com.android.inputmethod.latin.Suggest;
27 import com.android.inputmethod.latin.SuggestedWords;
28 import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback;
29 
30 /**
31  * A helper to manage deferred tasks for the input logic.
32  */
33 class InputLogicHandler implements Handler.Callback {
34     final Handler mNonUIThreadHandler;
35     // TODO: remove this reference.
36     final LatinIME mLatinIME;
37     final InputLogic mInputLogic;
38     private final Object mLock = new Object();
39     private boolean mInBatchInput; // synchronized using {@link #mLock}.
40 
41     private static final int MSG_GET_SUGGESTED_WORDS = 1;
42 
43     // A handler that never does anything. This is used for cases where events come before anything
44     // is initialized, though probably only the monkey can actually do this.
45     public static final InputLogicHandler NULL_HANDLER = new InputLogicHandler() {
46         @Override
47         public void reset() {}
48         @Override
49         public boolean handleMessage(final Message msg) { return true; }
50         @Override
51         public void onStartBatchInput() {}
52         @Override
53         public void onUpdateBatchInput(final InputPointers batchPointers,
54                 final int sequenceNumber) {}
55         @Override
56         public void onCancelBatchInput() {}
57         @Override
58         public void updateTailBatchInput(final InputPointers batchPointers,
59                 final int sequenceNumber) {}
60         @Override
61         public void getSuggestedWords(final int sessionId, final int sequenceNumber,
62                 final OnGetSuggestedWordsCallback callback) {}
63     };
64 
InputLogicHandler()65     private InputLogicHandler() {
66         mNonUIThreadHandler = null;
67         mLatinIME = null;
68         mInputLogic = null;
69     }
70 
InputLogicHandler(final LatinIME latinIME, final InputLogic inputLogic)71     public InputLogicHandler(final LatinIME latinIME, final InputLogic inputLogic) {
72         final HandlerThread handlerThread = new HandlerThread(
73                 InputLogicHandler.class.getSimpleName());
74         handlerThread.start();
75         mNonUIThreadHandler = new Handler(handlerThread.getLooper(), this);
76         mLatinIME = latinIME;
77         mInputLogic = inputLogic;
78     }
79 
reset()80     public void reset() {
81         mNonUIThreadHandler.removeCallbacksAndMessages(null);
82     }
83 
84     // In unit tests, we create several instances of LatinIME, which results in several instances
85     // of InputLogicHandler. To avoid these handlers lingering, we call this.
destroy()86     public void destroy() {
87         LooperCompatUtils.quitSafely(mNonUIThreadHandler.getLooper());
88     }
89 
90     /**
91      * Handle a message.
92      * @see android.os.Handler.Callback#handleMessage(android.os.Message)
93      */
94     // Called on the Non-UI handler thread by the Handler code.
95     @Override
handleMessage(final Message msg)96     public boolean handleMessage(final Message msg) {
97         switch (msg.what) {
98             case MSG_GET_SUGGESTED_WORDS:
99                 mLatinIME.getSuggestedWords(msg.arg1 /* inputStyle */,
100                         msg.arg2 /* sequenceNumber */, (OnGetSuggestedWordsCallback) msg.obj);
101                 break;
102         }
103         return true;
104     }
105 
106     // Called on the UI thread by InputLogic.
onStartBatchInput()107     public void onStartBatchInput() {
108         synchronized (mLock) {
109             mInBatchInput = true;
110         }
111     }
112 
isInBatchInput()113     public boolean isInBatchInput() {
114         return mInBatchInput;
115     }
116 
117     /**
118      * Fetch suggestions corresponding to an update of a batch input.
119      * @param batchPointers the updated pointers, including the part that was passed last time.
120      * @param sequenceNumber the sequence number associated with this batch input.
121      * @param isTailBatchInput true if this is the end of a batch input, false if it's an update.
122      */
123     // This method can be called from any thread and will see to it that the correct threads
124     // are used for parts that require it. This method will send a message to the Non-UI handler
125     // thread to pull suggestions, and get the inlined callback to get called on the Non-UI
126     // handler thread. If this is the end of a batch input, the callback will then proceed to
127     // send a message to the UI handler in LatinIME so that showing suggestions can be done on
128     // the UI thread.
updateBatchInput(final InputPointers batchPointers, final int sequenceNumber, final boolean isTailBatchInput)129     private void updateBatchInput(final InputPointers batchPointers,
130             final int sequenceNumber, final boolean isTailBatchInput) {
131         synchronized (mLock) {
132             if (!mInBatchInput) {
133                 // Batch input has ended or canceled while the message was being delivered.
134                 return;
135             }
136             mInputLogic.mWordComposer.setBatchInputPointers(batchPointers);
137             getSuggestedWords(isTailBatchInput ? SuggestedWords.INPUT_STYLE_TAIL_BATCH
138                     : SuggestedWords.INPUT_STYLE_UPDATE_BATCH, sequenceNumber,
139                     new OnGetSuggestedWordsCallback() {
140                         @Override
141                         public void onGetSuggestedWords(SuggestedWords suggestedWords) {
142                             // We're now inside the callback. This always runs on the Non-UI thread,
143                             // no matter what thread updateBatchInput was originally called on.
144                             if (suggestedWords.isEmpty()) {
145                                 // Use old suggestions if we don't have any new ones.
146                                 // Previous suggestions are found in InputLogic#mSuggestedWords.
147                                 // Since these are the most recent ones and we just recomputed
148                                 // new ones to update them, then the previous ones are there.
149                                 suggestedWords = mInputLogic.mSuggestedWords;
150                             }
151                             mLatinIME.mHandler.showGesturePreviewAndSuggestionStrip(suggestedWords,
152                                     isTailBatchInput /* dismissGestureFloatingPreviewText */);
153                             if (isTailBatchInput) {
154                                 mInBatchInput = false;
155                                 // The following call schedules onEndBatchInputInternal
156                                 // to be called on the UI thread.
157                                 mLatinIME.mHandler.showTailBatchInputResult(suggestedWords);
158                             }
159                         }
160                     });
161         }
162     }
163 
164     /**
165      * Update a batch input.
166      *
167      * This fetches suggestions and updates the suggestion strip and the floating text preview.
168      *
169      * @param batchPointers the updated batch pointers.
170      * @param sequenceNumber the sequence number associated with this batch input.
171      */
172     // Called on the UI thread by InputLogic.
onUpdateBatchInput(final InputPointers batchPointers, final int sequenceNumber)173     public void onUpdateBatchInput(final InputPointers batchPointers,
174             final int sequenceNumber) {
175         updateBatchInput(batchPointers, sequenceNumber, false /* isTailBatchInput */);
176     }
177 
178     /**
179      * Cancel a batch input.
180      *
181      * Note that as opposed to updateTailBatchInput, we do the UI side of this immediately on the
182      * same thread, rather than get this to call a method in LatinIME. This is because
183      * canceling a batch input does not necessitate the long operation of pulling suggestions.
184      */
185     // Called on the UI thread by InputLogic.
onCancelBatchInput()186     public void onCancelBatchInput() {
187         synchronized (mLock) {
188             mInBatchInput = false;
189         }
190     }
191 
192     /**
193      * Trigger an update for a tail batch input.
194      *
195      * A tail batch input is the last update for a gesture, the one that is triggered after the
196      * user lifts their finger. This method schedules fetching suggestions on the non-UI thread,
197      * then when the suggestions are computed it comes back on the UI thread to update the
198      * suggestion strip, commit the first suggestion, and dismiss the floating text preview.
199      *
200      * @param batchPointers the updated batch pointers.
201      * @param sequenceNumber the sequence number associated with this batch input.
202      */
203     // Called on the UI thread by InputLogic.
updateTailBatchInput(final InputPointers batchPointers, final int sequenceNumber)204     public void updateTailBatchInput(final InputPointers batchPointers,
205             final int sequenceNumber) {
206         updateBatchInput(batchPointers, sequenceNumber, true /* isTailBatchInput */);
207     }
208 
getSuggestedWords(final int inputStyle, final int sequenceNumber, final OnGetSuggestedWordsCallback callback)209     public void getSuggestedWords(final int inputStyle, final int sequenceNumber,
210             final OnGetSuggestedWordsCallback callback) {
211         mNonUIThreadHandler.obtainMessage(
212                 MSG_GET_SUGGESTED_WORDS, inputStyle, sequenceNumber, callback).sendToTarget();
213     }
214 }
215