OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / frameworks / base / core / java / com / android / internal / view / InputConnectionWrapper.java
1 /*
2  * Copyright (C) 2008 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.internal.view;
18
19 import android.os.Bundle;
20 import android.os.RemoteException;
21 import android.os.SystemClock;
22 import android.util.Log;
23 import android.view.KeyEvent;
24 import android.view.inputmethod.CompletionInfo;
25 import android.view.inputmethod.ExtractedText;
26 import android.view.inputmethod.ExtractedTextRequest;
27 import android.view.inputmethod.InputConnection;
28
29 public class InputConnectionWrapper implements InputConnection {
30     private static final int MAX_WAIT_TIME_MILLIS = 2000;
31     private final IInputContext mIInputContext;
32     
33     static class InputContextCallback extends IInputContextCallback.Stub {
34         private static final String TAG = "InputConnectionWrapper.ICC";
35         public int mSeq;
36         public boolean mHaveValue;
37         public CharSequence mTextBeforeCursor;
38         public CharSequence mTextAfterCursor;
39         public CharSequence mSelectedText;
40         public ExtractedText mExtractedText;
41         public int mCursorCapsMode;
42         
43         // A 'pool' of one InputContextCallback.  Each ICW request will attempt to gain
44         // exclusive access to this object.
45         private static InputContextCallback sInstance = new InputContextCallback();
46         private static int sSequenceNumber = 1;
47         
48         /**
49          * Returns an InputContextCallback object that is guaranteed not to be in use by
50          * any other thread.  The returned object's 'have value' flag is cleared and its expected
51          * sequence number is set to a new integer.  We use a sequence number so that replies that
52          * occur after a timeout has expired are not interpreted as replies to a later request.
53          */
54         private static InputContextCallback getInstance() {
55             synchronized (InputContextCallback.class) {
56                 // Return sInstance if it's non-null, otherwise construct a new callback
57                 InputContextCallback callback;
58                 if (sInstance != null) {
59                     callback = sInstance;
60                     sInstance = null;
61                     
62                     // Reset the callback
63                     callback.mHaveValue = false;
64                 } else {
65                     callback = new InputContextCallback();
66                 }
67                 
68                 // Set the sequence number
69                 callback.mSeq = sSequenceNumber++;
70                 return callback;
71             }
72         }
73         
74         /**
75          * Makes the given InputContextCallback available for use in the future.
76          */
77         private void dispose() {
78             synchronized (InputContextCallback.class) {
79                 // If sInstance is non-null, just let this object be garbage-collected
80                 if (sInstance == null) {
81                     // Allow any objects being held to be gc'ed
82                     mTextAfterCursor = null;
83                     mTextBeforeCursor = null;
84                     mExtractedText = null;
85                     sInstance = this;
86                 }
87             }
88         }
89         
90         public void setTextBeforeCursor(CharSequence textBeforeCursor, int seq) {
91             synchronized (this) {
92                 if (seq == mSeq) {
93                     mTextBeforeCursor = textBeforeCursor;
94                     mHaveValue = true;
95                     notifyAll();
96                 } else {
97                     Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
98                             + ") in setTextBeforeCursor, ignoring.");
99                 }
100             }
101         }
102
103         public void setTextAfterCursor(CharSequence textAfterCursor, int seq) {
104             synchronized (this) {
105                 if (seq == mSeq) {
106                     mTextAfterCursor = textAfterCursor;
107                     mHaveValue = true;
108                     notifyAll();
109                 } else {
110                     Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
111                             + ") in setTextAfterCursor, ignoring.");
112                 }
113             }
114         }
115
116         public void setSelectedText(CharSequence selectedText, int seq) {
117             synchronized (this) {
118                 if (seq == mSeq) {
119                     mSelectedText = selectedText;
120                     mHaveValue = true;
121                     notifyAll();
122                 } else {
123                     Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
124                             + ") in setSelectedText, ignoring.");
125                 }
126             }
127         }
128
129         public void setCursorCapsMode(int capsMode, int seq) {
130             synchronized (this) {
131                 if (seq == mSeq) {
132                     mCursorCapsMode = capsMode; 
133                     mHaveValue = true;  
134                     notifyAll();
135                 } else {
136                     Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
137                             + ") in setCursorCapsMode, ignoring.");
138                 }
139             }
140         }
141
142         public void setExtractedText(ExtractedText extractedText, int seq) {
143             synchronized (this) {
144                 if (seq == mSeq) {
145                     mExtractedText = extractedText;
146                     mHaveValue = true;
147                     notifyAll();
148                 } else {
149                     Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
150                             + ") in setExtractedText, ignoring.");
151                 }
152             }
153         }
154         
155         /**
156          * Waits for a result for up to {@link #MAX_WAIT_TIME_MILLIS} milliseconds.
157          * 
158          * <p>The caller must be synchronized on this callback object.
159          */
160         void waitForResultLocked() {
161             long startTime = SystemClock.uptimeMillis();
162             long endTime = startTime + MAX_WAIT_TIME_MILLIS;
163
164             while (!mHaveValue) {
165                 long remainingTime = endTime - SystemClock.uptimeMillis();
166                 if (remainingTime <= 0) {
167                     Log.w(TAG, "Timed out waiting on IInputContextCallback");
168                     return;
169                 }
170                 try {
171                     wait(remainingTime);
172                 } catch (InterruptedException e) {
173                 }
174             }
175         }
176     }
177
178     public InputConnectionWrapper(IInputContext inputContext) {
179         mIInputContext = inputContext;
180     }
181
182     public CharSequence getTextAfterCursor(int length, int flags) {
183         CharSequence value = null;
184         try {
185             InputContextCallback callback = InputContextCallback.getInstance();
186             mIInputContext.getTextAfterCursor(length, flags, callback.mSeq, callback);
187             synchronized (callback) {
188                 callback.waitForResultLocked();
189                 if (callback.mHaveValue) {
190                     value = callback.mTextAfterCursor;
191                 }
192             }
193             callback.dispose();
194         } catch (RemoteException e) {
195             return null;
196         }
197         return value;
198     }
199     
200     public CharSequence getTextBeforeCursor(int length, int flags) {
201         CharSequence value = null;
202         try {
203             InputContextCallback callback = InputContextCallback.getInstance();
204             mIInputContext.getTextBeforeCursor(length, flags, callback.mSeq, callback);
205             synchronized (callback) {
206                 callback.waitForResultLocked();
207                 if (callback.mHaveValue) {
208                     value = callback.mTextBeforeCursor;
209                 }
210             }
211             callback.dispose();
212         } catch (RemoteException e) {
213             return null;
214         }
215         return value;
216     }
217     
218     public CharSequence getSelectedText(int flags) {
219         CharSequence value = null;
220         try {
221             InputContextCallback callback = InputContextCallback.getInstance();
222             mIInputContext.getSelectedText(flags, callback.mSeq, callback);
223             synchronized (callback) {
224                 callback.waitForResultLocked();
225                 if (callback.mHaveValue) {
226                     value = callback.mSelectedText;
227                 }
228             }
229             callback.dispose();
230         } catch (RemoteException e) {
231             return null;
232         }
233         return value;
234     }
235
236     public int getCursorCapsMode(int reqModes) {
237         int value = 0;
238         try {
239             InputContextCallback callback = InputContextCallback.getInstance();
240             mIInputContext.getCursorCapsMode(reqModes, callback.mSeq, callback);
241             synchronized (callback) {
242                 callback.waitForResultLocked();
243                 if (callback.mHaveValue) {
244                     value = callback.mCursorCapsMode;
245                 }
246             }
247             callback.dispose();
248         } catch (RemoteException e) {
249             return 0;
250         }
251         return value;
252     }
253     
254     public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
255         ExtractedText value = null;
256         try {
257             InputContextCallback callback = InputContextCallback.getInstance();
258             mIInputContext.getExtractedText(request, flags, callback.mSeq, callback);
259             synchronized (callback) {
260                 callback.waitForResultLocked();
261                 if (callback.mHaveValue) {
262                     value = callback.mExtractedText;
263                 }
264             }
265             callback.dispose();
266         } catch (RemoteException e) {
267             return null;
268         }
269         return value;
270     }
271     
272     public boolean commitText(CharSequence text, int newCursorPosition) {
273         try {
274             mIInputContext.commitText(text, newCursorPosition);
275             return true;
276         } catch (RemoteException e) {
277             return false;
278         }
279     }
280
281     public boolean commitCompletion(CompletionInfo text) {
282         try {
283             mIInputContext.commitCompletion(text);
284             return true;
285         } catch (RemoteException e) {
286             return false;
287         }
288     }
289
290     public boolean setSelection(int start, int end) {
291         try {
292             mIInputContext.setSelection(start, end);
293             return true;
294         } catch (RemoteException e) {
295             return false;
296         }
297     }
298     
299     public boolean performEditorAction(int actionCode) {
300         try {
301             mIInputContext.performEditorAction(actionCode);
302             return true;
303         } catch (RemoteException e) {
304             return false;
305         }
306     }
307     
308     public boolean performContextMenuAction(int id) {
309         try {
310             mIInputContext.performContextMenuAction(id);
311             return true;
312         } catch (RemoteException e) {
313             return false;
314         }
315     }
316
317     public boolean setComposingRegion(int start, int end) {
318         try {
319             mIInputContext.setComposingRegion(start, end);
320             return true;
321         } catch (RemoteException e) {
322             return false;
323         }
324     }
325
326     public boolean setComposingText(CharSequence text, int newCursorPosition) {
327         try {
328             mIInputContext.setComposingText(text, newCursorPosition);
329             return true;
330         } catch (RemoteException e) {
331             return false;
332         }
333     }
334
335     public boolean finishComposingText() {
336         try {
337             mIInputContext.finishComposingText();
338             return true;
339         } catch (RemoteException e) {
340             return false;
341         }
342     }
343
344     public boolean beginBatchEdit() {
345         try {
346             mIInputContext.beginBatchEdit();
347             return true;
348         } catch (RemoteException e) {
349             return false;
350         }
351     }
352     
353     public boolean endBatchEdit() {
354         try {
355             mIInputContext.endBatchEdit();
356             return true;
357         } catch (RemoteException e) {
358             return false;
359         }
360     }
361     
362     public boolean sendKeyEvent(KeyEvent event) {
363         try {
364             mIInputContext.sendKeyEvent(event);
365             return true;
366         } catch (RemoteException e) {
367             return false;
368         }
369     }
370
371     public boolean clearMetaKeyStates(int states) {
372         try {
373             mIInputContext.clearMetaKeyStates(states);
374             return true;
375         } catch (RemoteException e) {
376             return false;
377         }
378     }
379     
380     public boolean deleteSurroundingText(int leftLength, int rightLength) {
381         try {
382             mIInputContext.deleteSurroundingText(leftLength, rightLength);
383             return true;
384         } catch (RemoteException e) {
385             return false;
386         }
387     }
388
389     public boolean reportFullscreenMode(boolean enabled) {
390         try {
391             mIInputContext.reportFullscreenMode(enabled);
392             return true;
393         } catch (RemoteException e) {
394             return false;
395         }
396     }
397
398     public boolean performPrivateCommand(String action, Bundle data) {
399         try {
400             mIInputContext.performPrivateCommand(action, data);
401             return true;
402         } catch (RemoteException e) {
403             return false;
404         }
405     }
406 }