PageRenderTime 47ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/core/java/com/android/internal/view/InputConnectionWrapper.java

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