/tags/20100328/src/com/menny/android/anysoftkeyboard/AnySoftKeyboard.java

http://softkeyboard.googlecode.com/ · Java · 2061 lines · 1529 code · 191 blank · 341 comment · 438 complexity · c6fdc17c76195f15424e19e5b6f701a7 MD5 · raw file

Large files are truncated click here to view the full file

  1. /*
  2. * Copyright (C) 2008-2009 Google Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * 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, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.menny.android.anysoftkeyboard;
  17. import java.util.ArrayList;
  18. import java.util.List;
  19. import android.app.AlertDialog;
  20. import android.app.Notification;
  21. import android.app.NotificationManager;
  22. import android.app.PendingIntent;
  23. import android.content.Context;
  24. import android.content.DialogInterface;
  25. import android.content.Intent;
  26. import android.content.SharedPreferences;
  27. import android.content.SharedPreferences.Editor;
  28. import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
  29. import android.content.res.Configuration;
  30. import android.inputmethodservice.InputMethodService;
  31. import android.inputmethodservice.Keyboard;
  32. import android.inputmethodservice.KeyboardView;
  33. import android.media.AudioManager;
  34. import android.os.Debug;
  35. import android.os.Handler;
  36. import android.os.Message;
  37. import android.os.Vibrator;
  38. import android.preference.PreferenceManager;
  39. import android.text.AutoText;
  40. import android.text.TextUtils;
  41. import android.util.Log;
  42. import android.view.KeyEvent;
  43. import android.view.View;
  44. import android.view.Window;
  45. import android.view.WindowManager;
  46. import android.view.inputmethod.CompletionInfo;
  47. import android.view.inputmethod.EditorInfo;
  48. import android.view.inputmethod.InputConnection;
  49. import android.view.inputmethod.InputMethodManager;
  50. import android.widget.Toast;
  51. import com.menny.android.anysoftkeyboard.Dictionary.Dictionary;
  52. import com.menny.android.anysoftkeyboard.Dictionary.DictionaryFactory;
  53. import com.menny.android.anysoftkeyboard.Dictionary.ExternalDictionaryFactory;
  54. import com.menny.android.anysoftkeyboard.Dictionary.UserDictionaryBase;
  55. import com.menny.android.anysoftkeyboard.Dictionary.ExternalDictionaryFactory.DictionaryBuilder;
  56. import com.menny.android.anysoftkeyboard.KeyboardSwitcher.NextKeyboardType;
  57. import com.menny.android.anysoftkeyboard.keyboards.AnyKeyboard;
  58. import com.menny.android.anysoftkeyboard.keyboards.AnyKeyboard.HardKeyboardTranslator;
  59. import com.menny.android.anysoftkeyboard.tutorials.TutorialsProvider;
  60. /**
  61. * Input method implementation for Qwerty'ish keyboard.
  62. */
  63. public class AnySoftKeyboard extends InputMethodService implements
  64. KeyboardView.OnKeyboardActionListener,
  65. OnSharedPreferenceChangeListener, AnyKeyboardContextProvider {
  66. private final static String TAG = "ASK";
  67. private final boolean TRACE_SDCARD = false;
  68. private static final int MSG_UPDATE_SUGGESTIONS = 0;
  69. private static final int MSG_START_TUTORIAL = 1;
  70. private static final int KEYCODE_ENTER = 10;
  71. private static final int KEYCODE_SPACE = ' ';
  72. private static final int KEYBOARD_NOTIFICATION_ID = 1;
  73. private static final String PUNCTUATION_CHARACTERS = ".\n!?,:;@<>()[]{}";
  74. private final AnySoftKeyboardConfiguration mConfig;
  75. private boolean DEBUG;
  76. private AnyKeyboardView mInputView;
  77. private CandidateViewContainer mCandidateViewContainer;
  78. private CandidateView mCandidateView;
  79. private Suggest mSuggest;
  80. private CompletionInfo[] mCompletions;
  81. private AlertDialog mOptionsDialog;
  82. KeyboardSwitcher mKeyboardSwitcher;
  83. private final HardKeyboardActionImpl mHardKeyboardAction;
  84. private long mMetaState;
  85. private UserDictionaryBase mUserDictionary;
  86. private StringBuilder mComposing = new StringBuilder();
  87. private WordComposer mWord = new WordComposer();
  88. private int mCommittedLength;
  89. private boolean mPredicting;
  90. private CharSequence mBestWord;
  91. private final boolean mPredictionLandscape = false;
  92. private boolean mPredictionOn;
  93. private boolean mCompletionOn;
  94. private boolean mAutoSpace;
  95. private boolean mAutoCorrectOn;
  96. private boolean mCapsLock;
  97. // private boolean mVibrateOn;
  98. private int mVibrationDuration;
  99. private boolean mSoundOn;
  100. // between 0..100. This is the custom volume
  101. private int mSoundVolume;
  102. private boolean mSwitchKeyboardOnSpace;
  103. private boolean mSmileyOnShortPress;
  104. private boolean mAutoCap;
  105. private boolean mQuickFixes;
  106. private boolean mShowSuggestions = false;
  107. private boolean mAutoComplete;
  108. private int mCorrectionMode;
  109. private String mKeyboardChangeNotificationType;
  110. private static final String KEYBOARD_NOTIFICATION_ALWAYS = "1";
  111. private static final String KEYBOARD_NOTIFICATION_ON_PHYSICAL = "2";
  112. private static final String KEYBOARD_NOTIFICATION_NEVER = "3";
  113. // Indicates whether the suggestion strip is to be on in landscape
  114. private boolean mJustAccepted;
  115. private CharSequence mJustRevertedSeparator;
  116. private AudioManager mAudioManager;
  117. private NotificationManager mNotificationManager;
  118. // private final HardKeyboardTranslator mGenericKeyboardTranslator;
  119. Handler mHandler = new Handler() {
  120. @Override
  121. public void handleMessage(Message msg) {
  122. switch (msg.what) {
  123. case MSG_UPDATE_SUGGESTIONS:
  124. updateSuggestions();
  125. break;
  126. case MSG_START_TUTORIAL:
  127. if ((mInputView != null) && mInputView.isShown()) {
  128. TutorialsProvider.ShowTutorialsIfNeeded(
  129. AnySoftKeyboard.this, mInputView);
  130. } else {
  131. // Try again soon if the view is not yet showing
  132. sendMessageDelayed(obtainMessage(MSG_START_TUTORIAL), 100);
  133. }
  134. break;
  135. }
  136. }
  137. };
  138. private boolean mSpaceSent;
  139. public AnySoftKeyboard() {
  140. // mGenericKeyboardTranslator = new
  141. // GenericPhysicalKeyboardTranslator(this);
  142. mConfig = AnySoftKeyboardConfiguration.getInstance();
  143. mHardKeyboardAction = new HardKeyboardActionImpl();
  144. }
  145. @Override
  146. public void onCreate() {
  147. super.onCreate();
  148. // super.showStatusIcon(R.drawable.icon_8_key);
  149. Log.i("AnySoftKeyboard", "****** Starting AnySoftKeyboard:");
  150. ((AnySoftKeyboardConfiguration.AnySoftKeyboardConfigurationImpl) mConfig)
  151. .initializeConfiguration(this);
  152. DEBUG = mConfig.getDEBUG();
  153. // showToastMessage(R.string.toast_lengthy_start_up_operation, true);
  154. mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
  155. mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
  156. // setStatusIcon(R.drawable.ime_qwerty);
  157. loadSettings();
  158. mKeyboardSwitcher = new KeyboardSwitcher(this);
  159. if (mSuggest == null) {
  160. // should it be always on?
  161. if (mKeyboardChangeNotificationType
  162. .equals(KEYBOARD_NOTIFICATION_ALWAYS))
  163. notifyKeyboardChangeIfNeeded();
  164. initSuggest(/* getResources().getConfiguration().locale.toString() */);
  165. }
  166. SharedPreferences sp = PreferenceManager
  167. .getDefaultSharedPreferences(this);
  168. sp.registerOnSharedPreferenceChangeListener(this);
  169. }
  170. private void initSuggest(/* String locale */) {
  171. // mLocale = locale;
  172. mSuggest = new Suggest(this/* , R.raw.main */);
  173. mSuggest.setCorrectionMode(mCorrectionMode);
  174. mUserDictionary = DictionaryFactory.createUserDictionary(this);
  175. mSuggest.setUserDictionary(mUserDictionary);
  176. setMainDictionaryForCurrentKeyboard();
  177. // mWordSeparators = getResources().getString(R.string.word_separators);
  178. // mSentenceSeparators =
  179. // getResources().getString(R.string.sentence_separators);
  180. }
  181. @Override
  182. public void onDestroy() {
  183. DictionaryFactory.close();
  184. // unregisterReceiver(mReceiver);
  185. SharedPreferences sp = PreferenceManager
  186. .getDefaultSharedPreferences(this);
  187. sp.unregisterOnSharedPreferenceChangeListener(this);
  188. if (mSoundOn) {
  189. Log.i(TAG,
  190. "Releasing sounds effects from AUDIO_SERVICE");
  191. mAudioManager.unloadSoundEffects();
  192. }
  193. mNotificationManager.cancel(KEYBOARD_NOTIFICATION_ID);
  194. super.onDestroy();
  195. }
  196. @Override
  197. public void onFinishInputView(boolean finishingInput) {
  198. if (DEBUG)
  199. Log.d(TAG, "onFinishInputView(finishingInput:"
  200. + finishingInput + ")");
  201. super.onFinishInputView(finishingInput);
  202. if (!mKeyboardChangeNotificationType
  203. .equals(KEYBOARD_NOTIFICATION_ALWAYS)) {
  204. mNotificationManager.cancel(KEYBOARD_NOTIFICATION_ID);
  205. }
  206. if (finishingInput)
  207. resetComposing();// clearing any predications
  208. };
  209. @Override
  210. public View onCreateInputView() {
  211. mInputView = (AnyKeyboardView) getLayoutInflater().inflate(
  212. //the new layout will solve the "invalidateAllKeys" problem.
  213. Workarounds.isDonut()? R.layout.input_donut : R.layout.input_cupcake
  214. , null);
  215. mKeyboardSwitcher.setInputView(mInputView);
  216. mKeyboardSwitcher.makeKeyboards(false);
  217. mInputView.setOnKeyboardActionListener(this);
  218. mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_TEXT, null);
  219. startTutorial();
  220. return mInputView;
  221. }
  222. @Override
  223. public View onCreateCandidatesView() {
  224. mKeyboardSwitcher.makeKeyboards(false);
  225. mCandidateViewContainer = (CandidateViewContainer) getLayoutInflater()
  226. .inflate(R.layout.candidates, null);
  227. mCandidateViewContainer.initViews();
  228. mCandidateView = (CandidateView) mCandidateViewContainer
  229. .findViewById(R.id.candidates);
  230. mCandidateView.setService(this);
  231. setCandidatesViewShown(true);
  232. return mCandidateViewContainer;
  233. }
  234. @Override
  235. public void onStartInput(EditorInfo attribute, boolean restarting) {
  236. if (DEBUG)
  237. Log.d(TAG, "onStartInput(EditorInfo:"
  238. + attribute.imeOptions + "," + attribute.inputType
  239. + ", restarting:" + restarting + ")");
  240. super.onStartInput(attribute, restarting);
  241. mKeyboardSwitcher.makeKeyboards(false);
  242. resetComposing();// clearing any predications
  243. TextEntryState.newSession(this);
  244. if (!restarting) {
  245. // Clear shift states.
  246. mMetaState = 0;
  247. }
  248. mPredictionOn = false;
  249. mCompletionOn = false;
  250. mCompletions = null;
  251. mCapsLock = false;
  252. switch (attribute.inputType & EditorInfo.TYPE_MASK_CLASS) {
  253. case EditorInfo.TYPE_CLASS_NUMBER:
  254. case EditorInfo.TYPE_CLASS_DATETIME:
  255. case EditorInfo.TYPE_CLASS_PHONE:
  256. mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_PHONE,
  257. attribute);
  258. break;
  259. case EditorInfo.TYPE_CLASS_TEXT:
  260. mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_TEXT,
  261. attribute);
  262. // startPrediction();
  263. mPredictionOn = true;
  264. // Make sure that passwords are not displayed in candidate view
  265. final int variation = attribute.inputType
  266. & EditorInfo.TYPE_MASK_VARIATION;
  267. if (variation == EditorInfo.TYPE_TEXT_VARIATION_PASSWORD
  268. || variation == EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) {
  269. mPredictionOn = false;
  270. }
  271. if ((!AnySoftKeyboardConfiguration.getInstance().getInsertSpaceAfterCandidatePick()) ||//some users want to never get spaces added
  272. variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS ||
  273. variation == EditorInfo.TYPE_TEXT_VARIATION_PERSON_NAME)
  274. {
  275. mAutoSpace = false;
  276. } else {
  277. mAutoSpace = true;
  278. }
  279. if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS) {
  280. mPredictionOn = false;
  281. mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_EMAIL,
  282. attribute);
  283. } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_URI) {
  284. mPredictionOn = false;
  285. mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_URL,
  286. attribute);
  287. } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_SHORT_MESSAGE) {
  288. mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_IM,
  289. attribute);
  290. } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_FILTER) {
  291. mPredictionOn = false;
  292. }
  293. if ((attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) {
  294. mPredictionOn = false;
  295. mCompletionOn = true && isFullscreenMode();
  296. }
  297. updateShiftKeyState(attribute);
  298. break;
  299. default:
  300. mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_TEXT,
  301. attribute);
  302. updateShiftKeyState(attribute);
  303. }
  304. mComposing.setLength(0);
  305. mPredicting = false;
  306. // mDeleteCount = 0;
  307. setCandidatesViewShown(false);
  308. // loadSettings();
  309. if (mSuggest != null) {
  310. mSuggest.setCorrectionMode(mCorrectionMode);
  311. }
  312. mPredictionOn = mPredictionOn && mCorrectionMode > 0;
  313. if (mCandidateView != null)
  314. mCandidateView.setSuggestions(null, false, false, false);
  315. if (TRACE_SDCARD)
  316. Debug.startMethodTracing("anysoftkeyboard_log.trace");
  317. }
  318. @Override
  319. public void onStartInputView(EditorInfo attribute, boolean restarting) {
  320. if (DEBUG)
  321. Log.d(TAG, "onStartInputView(EditorInfo:"
  322. + attribute.imeOptions + "," + attribute.inputType
  323. + ", restarting:" + restarting + ")");
  324. super.onStartInputView(attribute, restarting);
  325. if (mInputView != null) {
  326. mInputView.closing();
  327. if (AutoText.getSize(mInputView) < 1)
  328. mQuickFixes = true;
  329. }
  330. }
  331. @Override
  332. public void onFinishInput() {
  333. if (DEBUG)
  334. Log.d(TAG, "onFinishInput()");
  335. super.onFinishInput();
  336. if (mInputView != null) {
  337. mInputView.closing();
  338. }
  339. if (!mKeyboardChangeNotificationType
  340. .equals(KEYBOARD_NOTIFICATION_ALWAYS)) {
  341. NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
  342. notificationManager.cancel(KEYBOARD_NOTIFICATION_ID);
  343. }
  344. // clearing any predications
  345. resetComposing();
  346. // releasing some memory. Dictionaries, completions, etc.
  347. System.gc();
  348. }
  349. @Override
  350. public void onUpdateSelection(int oldSelStart, int oldSelEnd,
  351. int newSelStart, int newSelEnd, int candidatesStart,
  352. int candidatesEnd) {
  353. super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd,
  354. candidatesStart, candidatesEnd);
  355. // If the current selection in the text view changes, we should
  356. // clear whatever candidate text we have.
  357. if (mComposing.length() > 0 && mPredicting
  358. && (newSelStart != candidatesEnd || newSelEnd != candidatesEnd)) {
  359. resetComposing();
  360. } else if (!mPredicting
  361. && !mJustAccepted
  362. && TextEntryState.getState() == TextEntryState.STATE_ACCEPTED_DEFAULT) {
  363. TextEntryState.reset();
  364. }
  365. mJustAccepted = false;
  366. }
  367. private void resetComposing() {
  368. InputConnection ic = getCurrentInputConnection();
  369. if (ic != null) {
  370. ic.finishComposingText();
  371. // commitTyped(ic);
  372. }
  373. mComposing.setLength(0);
  374. mPredicting = false;
  375. updateSuggestions();
  376. TextEntryState.reset();
  377. }
  378. @Override
  379. public void hideWindow() {
  380. if (TRACE_SDCARD)
  381. Debug.stopMethodTracing();
  382. if (mOptionsDialog != null && mOptionsDialog.isShowing()) {
  383. mOptionsDialog.dismiss();
  384. mOptionsDialog = null;
  385. }
  386. // if (mTutorial != null) {
  387. // mTutorial.close();
  388. // mTutorial = null;
  389. // }
  390. super.hideWindow();
  391. TextEntryState.endSession();
  392. }
  393. @Override
  394. public void onDisplayCompletions(CompletionInfo[] completions) {
  395. if (DEBUG) {
  396. Log.i(TAG, "Received completions:");
  397. for (int i = 0; i < (completions != null ? completions.length : 0); i++) {
  398. Log.i("AnySoftKeyboard", " #" + i + ": " + completions[i]);
  399. }
  400. }
  401. //completions should be shown if dictionary requires, or if we are in full-screen and have outside completeions
  402. if (mCompletionOn || (isFullscreenMode() && (completions != null))) {
  403. if (DEBUG) Log.v(TAG, "Received completions: completion should be shown: "+mCompletionOn+" fullscreen:"+isFullscreenMode());
  404. mCompletions = completions;
  405. if (completions == null) {
  406. if (DEBUG) Log.v(TAG, "Received completions: completion is NULL. Clearing suggestions.");
  407. mCandidateView.setSuggestions(null, false, false, false);
  408. return;
  409. }
  410. List<CharSequence> stringList = new ArrayList<CharSequence>();
  411. for (int i = 0; i < (completions != null ? completions.length : 0); i++) {
  412. CompletionInfo ci = completions[i];
  413. if (ci != null)
  414. stringList.add(ci.getText());
  415. }
  416. if (DEBUG) Log.v(TAG, "Received completions: setting to suggestions view "+stringList.size()+ " completions.");
  417. // CharSequence typedWord = mWord.getTypedWord();
  418. mCandidateView.setSuggestions(stringList, true, true, true);
  419. mBestWord = null;
  420. //I mean, if I'm here, it must be shown...
  421. setCandidatesViewShown(true);
  422. }
  423. else if (DEBUG) Log.v(TAG, "Received completions: completions should not be shown.");
  424. }
  425. @Override
  426. public void setCandidatesViewShown(boolean shown) {
  427. // we show predication only in on-screen keyboard
  428. // (onEvaluateInputViewShown)
  429. // or if the physical keyboard supports candidates
  430. // (mPredictionLandscape)
  431. super.setCandidatesViewShown(shouldCandidatesStripBeShown() && shown);
  432. }
  433. @Override
  434. public void onComputeInsets(InputMethodService.Insets outInsets) {
  435. super.onComputeInsets(outInsets);
  436. if (!isFullscreenMode()) {
  437. outInsets.contentTopInsets = outInsets.visibleTopInsets;
  438. }
  439. }
  440. @Override
  441. public boolean onEvaluateFullscreenMode() {
  442. if (AnySoftKeyboardConfiguration.getInstance().getUseFullScreenInput())
  443. return super.onEvaluateFullscreenMode();
  444. else
  445. return false;
  446. }
  447. @Override
  448. public boolean onKeyDown(int keyCode, KeyEvent event) {
  449. Log.d(TAG, "onKeyDown:"+keyCode);
  450. InputConnection ic = getCurrentInputConnection();
  451. if (!mPredictionLandscape) {
  452. // For all other keys, if we want to do transformations on
  453. // text being entered with a hard keyboard, we need to process
  454. // it and do the appropriate action.
  455. // using physical keyboard is more annoying with candidate view in
  456. // the way
  457. // so we disable it.
  458. // to clear the underline.
  459. commitTyped(ic);// to clear the underline.
  460. mPredicting = false;
  461. }
  462. if (DEBUG)
  463. Log.d(TAG, "Event: Key:" + event.getKeyCode()
  464. + " Shift:"
  465. + ((event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0)
  466. + " ALT:"
  467. + ((event.getMetaState() & KeyEvent.META_ALT_ON) != 0)
  468. + " Repeats:" + event.getRepeatCount());
  469. switch (keyCode) {
  470. case KeyEvent.KEYCODE_BACK:
  471. if (event.getRepeatCount() == 0 && mInputView != null) {
  472. if (mInputView.handleBack()) {
  473. // consuming the meta keys
  474. if (ic != null) {
  475. ic.clearMetaKeyStates(Integer.MAX_VALUE);// translated,
  476. // so we
  477. // also take
  478. // care of
  479. // the
  480. // metakeys.
  481. }
  482. mMetaState = 0;
  483. return true;
  484. } /*
  485. * else if (mTutorial != null) { mTutorial.close(); mTutorial =
  486. * null; }
  487. */
  488. }
  489. break;
  490. // case KeyEvent.KEYCODE_DPAD_DOWN:
  491. // case KeyEvent.KEYCODE_DPAD_UP:
  492. // case KeyEvent.KEYCODE_DPAD_LEFT:
  493. // case KeyEvent.KEYCODE_DPAD_RIGHT:
  494. // // If tutorial is visible, don't allow dpad to work
  495. // if (mTutorial != null) {
  496. // return true;
  497. // }
  498. // break;
  499. // case KeyEvent.KEYCODE_DEL:
  500. // onKey(Keyboard.KEYCODE_DELETE, new int[]{Keyboard.KEYCODE_DELETE});
  501. // return true;
  502. // case KeyEvent.KEYCODE_ENTER:
  503. // // Let the underlying text editor always handle these.
  504. // return false;
  505. case KeyEvent.KEYCODE_ALT_LEFT:
  506. case KeyEvent.KEYCODE_ALT_RIGHT:
  507. case KeyEvent.KEYCODE_SHIFT_LEFT:
  508. case KeyEvent.KEYCODE_SHIFT_RIGHT:
  509. case KeyEvent.KEYCODE_SYM:
  510. if (DEBUG)
  511. Log.d(TAG+"-meta-key",
  512. getMetaKeysStates("onKeyDown before handle"));
  513. mMetaState = MyMetaKeyKeyListener.handleKeyDown(mMetaState,
  514. keyCode, event);
  515. if (DEBUG)
  516. Log.d(TAG+"-meta-key",
  517. getMetaKeysStates("onKeyDown after handle"));
  518. break;
  519. case KeyEvent.KEYCODE_SPACE:
  520. if (event.isAltPressed()) {
  521. Log.d(TAG,
  522. "User pressed ALT+SPACE, moving to next physical keyboard.");
  523. // consuming the meta keys
  524. // mHardKeyboardAction.resetMetaState();
  525. if (ic != null) {
  526. ic.clearMetaKeyStates(Integer.MAX_VALUE);// translated, so
  527. // we also take
  528. // care of the
  529. // metakeys.
  530. }
  531. mMetaState = 0;
  532. // only physical keyboard
  533. nextKeyboard(getCurrentInputEditorInfo(),
  534. NextKeyboardType.AlphabetSupportsPhysical);
  535. return true;
  536. }
  537. //NOTE:
  538. // letting it fall through to the "default"
  539. // else
  540. // {
  541. // //still handling it
  542. // onKey(32, new int[]{32});
  543. // return true;
  544. // }
  545. default:
  546. // Fix issue 185, check if we should process key repeat
  547. if (!AnySoftKeyboardConfiguration.getInstance()
  548. .getUseRepeatingKeys()
  549. && event.getRepeatCount() > 0)
  550. return true;
  551. if (mKeyboardSwitcher.isCurrentKeyboardPhysical()) {
  552. // sometimes, the physical keyboard will delete input, and then
  553. // add some.
  554. // we'll try to make it nice
  555. if (ic != null)
  556. ic.beginBatchEdit();
  557. try {
  558. if (event.isPrintingKey()) {
  559. mHardKeyboardAction.initializeAction(event, mMetaState);
  560. // http://article.gmane.org/gmane.comp.handhelds.openmoko.android-freerunner/629
  561. AnyKeyboard current = mKeyboardSwitcher.getCurrentKeyboard();
  562. HardKeyboardTranslator keyTranslator = (HardKeyboardTranslator) current;
  563. if (DEBUG)
  564. {
  565. final String keyboardName = current.getKeyboardName();
  566. Log.d(TAG, "Asking '" + keyboardName
  567. + "' to translate key: " + keyCode);
  568. Log.v(TAG,
  569. "Hard Keyboard Action before translation: Shift: "
  570. + mHardKeyboardAction
  571. .isShiftActive()
  572. + ", Alt: "
  573. + mHardKeyboardAction.isAltActive()
  574. + ", Key code: "
  575. + mHardKeyboardAction.getKeyCode()
  576. + ", changed: "
  577. + mHardKeyboardAction.getKeyCodeWasChanged());
  578. }
  579. keyTranslator.translatePhysicalCharacter(mHardKeyboardAction);
  580. if (DEBUG)
  581. Log.v(TAG,
  582. "Hard Keyboard Action after translation: Key code: "
  583. + mHardKeyboardAction.getKeyCode()
  584. + ", changed: "
  585. + mHardKeyboardAction
  586. .getKeyCodeWasChanged());
  587. if (mHardKeyboardAction.getKeyCodeWasChanged()) {
  588. final int translatedChar = mHardKeyboardAction
  589. .getKeyCode();
  590. // typing my own.
  591. onKey(translatedChar, new int[] { translatedChar });
  592. // my handling
  593. // we are at a regular key press, so we'll update
  594. // our meta-state member
  595. mMetaState = MyMetaKeyKeyListener
  596. .adjustMetaAfterKeypress(mMetaState);
  597. if (DEBUG)
  598. Log.d(TAG+"-meta-key",
  599. getMetaKeysStates("onKeyDown after adjust - translated"));
  600. return true;
  601. }
  602. }
  603. } finally {
  604. if (ic != null)
  605. ic.endBatchEdit();
  606. }
  607. }
  608. if (event.isPrintingKey()) {
  609. // we are at a regular key press, so we'll update our meta-state
  610. // member
  611. mMetaState = MyMetaKeyKeyListener
  612. .adjustMetaAfterKeypress(mMetaState);
  613. if (DEBUG)
  614. Log.d(TAG+"-meta-key",
  615. getMetaKeysStates("onKeyDown after adjust"));
  616. }
  617. }
  618. return super.onKeyDown(keyCode, event);
  619. }
  620. // private boolean askTranslatorToTranslateHardKeyboardAction(int keyCode,
  621. // InputConnection ic, String keyboardName,
  622. // HardKeyboardTranslator keyTranslator)
  623. // {
  624. // if (AnySoftKeyboard.DEBUG) Log.d("AnySoftKeyborad", "Asking '" +
  625. // keyboardName + "' to translate key: " + keyCode);
  626. // if (DEBUG) Log.v("AnySoftKeyboard",
  627. // "Hard Keyboard Action before translation: Shift: "+mHardKeyboardAction.mPhysicalShiftState+", Alt: "+mHardKeyboardAction.mPhysicalAltState+", Key code: "+mHardKeyboardAction.getKeyCode()+", changed: "+mHardKeyboardAction.getKeyCodeWasChanged());
  628. // keyTranslator.translatePhysicalCharacter(mHardKeyboardAction);
  629. // if (DEBUG) Log.v("AnySoftKeyboard",
  630. // "Hard Keyboard Action after translation: Shift: "+mHardKeyboardAction.mPhysicalShiftState+", Alt: "+mHardKeyboardAction.mPhysicalAltState+", Key code: "+mHardKeyboardAction.getKeyCode()+", changed: "+mHardKeyboardAction.getKeyCodeWasChanged());
  631. //
  632. // final char translatedChar = (char)mHardKeyboardAction.consumeKeyCode();
  633. // if (DEBUG) Log.v("AnySoftKeyboard",
  634. // "Hard Keyboard Action after consumeKeyCode: Shift: "+mHardKeyboardAction.mPhysicalShiftState+", Alt: "+mHardKeyboardAction.mPhysicalAltState+", Key code: "+mHardKeyboardAction.getKeyCode());
  635. // if (mHardKeyboardAction.getKeyCodeWasChanged())
  636. // {
  637. // // consuming the meta keys
  638. // //Since I'm handling the physical keys, I also need to clear the meta
  639. // state
  640. // if (ic != null)
  641. // {
  642. // //the clear should be done only if we are not in sticky mode
  643. // int metaStateToClear = 0;
  644. // if (!mHardKeyboardAction.isShiftActive())
  645. // {
  646. // if (DEBUG) Log.v("AnySoftKeyboard",
  647. // "About to clear SHIFT state from input since shift state is:"+mHardKeyboardAction.mPhysicalShiftState);
  648. // metaStateToClear += KeyEvent.META_SHIFT_ON;
  649. // }
  650. // if (!mHardKeyboardAction.isAltActive())
  651. // {
  652. // if (DEBUG) Log.v("AnySoftKeyboard",
  653. // "About to clear ALT state from input since alt state is:"+mHardKeyboardAction.mPhysicalAltState);
  654. // metaStateToClear += KeyEvent.META_ALT_ON;
  655. // }
  656. //
  657. // ic.clearMetaKeyStates(metaStateToClear);//translated, so we also take
  658. // care of the metakeys.
  659. // }
  660. //
  661. // if (AnySoftKeyboard.DEBUG)
  662. // Log.d("AnySoftKeyborad", "'"+ keyboardName + "' translated key " +
  663. // keyCode + " to "+ translatedChar);
  664. //
  665. // onKey(translatedChar, new int[] { translatedChar });
  666. // return true;
  667. // } else {
  668. // if (AnySoftKeyboard.DEBUG)
  669. // Log.d("AnySoftKeyborad", "'"+ keyboardName+ "' did not translated key " +
  670. // keyCode+ ".");
  671. // return false;
  672. // }
  673. // }
  674. private void notifyKeyboardChangeIfNeeded() {
  675. // Log.d("anySoftKeyboard","notifyKeyboardChangeIfNeeded");
  676. // Thread.dumpStack();
  677. if (mKeyboardSwitcher == null)// happens on first onCreate.
  678. return;
  679. if ((mKeyboardSwitcher.isAlphabetMode())
  680. && !mKeyboardChangeNotificationType
  681. .equals(KEYBOARD_NOTIFICATION_NEVER)) {
  682. AnyKeyboard current = mKeyboardSwitcher.getCurrentKeyboard();
  683. // notifying the user about the keyboard.
  684. // creating the message
  685. final String keyboardName = current.getKeyboardName();
  686. Notification notification = new Notification(current.getKeyboardIconResId(), keyboardName, System.currentTimeMillis());
  687. Intent notificationIntent = new Intent();
  688. PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
  689. notificationIntent, 0);
  690. notification.setLatestEventInfo(getApplicationContext(),
  691. "Any Soft Keyboard", keyboardName,
  692. contentIntent);
  693. if (mKeyboardChangeNotificationType.equals("1")) {
  694. notification.flags |= Notification.FLAG_ONGOING_EVENT;
  695. notification.flags |= Notification.FLAG_NO_CLEAR;
  696. } else {
  697. notification.flags |= Notification.FLAG_AUTO_CANCEL;
  698. }
  699. notification.defaults = 0;// no sound, vibrate, etc.
  700. // notifying
  701. mNotificationManager.notify(KEYBOARD_NOTIFICATION_ID, notification);
  702. }
  703. }
  704. @Override
  705. public boolean onKeyUp(int keyCode, KeyEvent event) {
  706. switch (keyCode) {
  707. case KeyEvent.KEYCODE_DPAD_DOWN:
  708. case KeyEvent.KEYCODE_DPAD_UP:
  709. case KeyEvent.KEYCODE_DPAD_LEFT:
  710. case KeyEvent.KEYCODE_DPAD_RIGHT:
  711. // // If tutorial is visible, don't allow dpad to work
  712. // if (mTutorial != null) {
  713. // return true;
  714. // }
  715. // Enable shift key and DPAD to do selections
  716. if (mInputView != null && mInputView.isShown()
  717. && mInputView.isShifted()) {
  718. event = new KeyEvent(event.getDownTime(), event.getEventTime(),
  719. event.getAction(), event.getKeyCode(), event
  720. .getRepeatCount(), event.getDeviceId(), event
  721. .getScanCode(), KeyEvent.META_SHIFT_LEFT_ON
  722. | KeyEvent.META_SHIFT_ON);
  723. InputConnection ic = getCurrentInputConnection();
  724. if (ic != null)
  725. ic.sendKeyEvent(event);
  726. return true;
  727. }
  728. break;
  729. case KeyEvent.KEYCODE_ALT_LEFT:
  730. case KeyEvent.KEYCODE_ALT_RIGHT:
  731. case KeyEvent.KEYCODE_SHIFT_LEFT:
  732. case KeyEvent.KEYCODE_SHIFT_RIGHT:
  733. case KeyEvent.KEYCODE_SYM:
  734. mMetaState = MyMetaKeyKeyListener.handleKeyUp(mMetaState, keyCode,
  735. event);
  736. if (DEBUG)
  737. Log.d("AnySoftKeyboard-meta-key", getMetaKeysStates("onKeyUp"));
  738. setInputConnectionMetaStateAsCurrentMetaKeyKeyListenerState();
  739. break;
  740. }
  741. return super.onKeyUp(keyCode, event);
  742. }
  743. private String getMetaKeysStates(String place) {
  744. final int shiftState = MyMetaKeyKeyListener.getMetaState(mMetaState,
  745. MyMetaKeyKeyListener.META_SHIFT_ON);
  746. final int altState = MyMetaKeyKeyListener.getMetaState(mMetaState,
  747. MyMetaKeyKeyListener.META_ALT_ON);
  748. final int symState = MyMetaKeyKeyListener.getMetaState(mMetaState,
  749. MyMetaKeyKeyListener.META_SYM_ON);
  750. return "Meta keys state at " + place + "- SHIFT:" + shiftState
  751. + ", ALT:" + altState + " SYM:" + symState + " bits:"
  752. + MyMetaKeyKeyListener.getMetaState(mMetaState) + " state:"
  753. + mMetaState;
  754. }
  755. private void setInputConnectionMetaStateAsCurrentMetaKeyKeyListenerState() {
  756. InputConnection ic = getCurrentInputConnection();
  757. if (ic != null) {
  758. int clearStatesFlags = 0;
  759. if (MyMetaKeyKeyListener.getMetaState(mMetaState,
  760. MyMetaKeyKeyListener.META_ALT_ON) == 0)
  761. clearStatesFlags += KeyEvent.META_ALT_ON;
  762. if (MyMetaKeyKeyListener.getMetaState(mMetaState,
  763. MyMetaKeyKeyListener.META_SHIFT_ON) == 0)
  764. clearStatesFlags += KeyEvent.META_SHIFT_ON;
  765. if (MyMetaKeyKeyListener.getMetaState(mMetaState,
  766. MyMetaKeyKeyListener.META_SYM_ON) == 0)
  767. clearStatesFlags += KeyEvent.META_SYM_ON;
  768. if (DEBUG)
  769. Log
  770. .d(
  771. "AnySoftKeyboard-meta-key",
  772. getMetaKeysStates("setInputConnectionMetaStateAsCurrentMetaKeyKeyListenerState with flags: "
  773. + clearStatesFlags));
  774. ic.clearMetaKeyStates(clearStatesFlags);
  775. }
  776. }
  777. private void commitTyped(InputConnection inputConnection) {
  778. if (mPredicting) {
  779. mPredicting = false;
  780. if (mComposing.length() > 0) {
  781. if (inputConnection != null) {
  782. inputConnection.commitText(mComposing, 1);
  783. }
  784. mCommittedLength = mComposing.length();
  785. TextEntryState.acceptedTyped(mComposing);
  786. }
  787. updateSuggestions();
  788. }
  789. }
  790. public void updateShiftKeyState(EditorInfo attr) {
  791. InputConnection ic = getCurrentInputConnection();
  792. if (attr != null && mInputView != null
  793. && mKeyboardSwitcher.isAlphabetMode() && ic != null) {
  794. int caps = 0;
  795. EditorInfo ei = getCurrentInputEditorInfo();
  796. if (mAutoCap && ei != null && ei.inputType != EditorInfo.TYPE_NULL) {
  797. caps = ic.getCursorCapsMode(attr.inputType);
  798. }
  799. mInputView.setShifted(mCapsLock || caps != 0);
  800. }
  801. }
  802. private void swapPunctuationAndSpace() {
  803. final InputConnection ic = getCurrentInputConnection();
  804. if (ic == null)
  805. return;
  806. CharSequence lastTwo = ic.getTextBeforeCursor(2, 0);
  807. if (lastTwo != null && lastTwo.length() == 2
  808. && lastTwo.charAt(0) == KEYCODE_SPACE
  809. && isPunctuationCharacter(lastTwo.charAt(1))) {
  810. ic.beginBatchEdit();
  811. ic.deleteSurroundingText(2, 0);
  812. ic.commitText(lastTwo.charAt(1) + " ", 1);
  813. ic.endBatchEdit();
  814. updateShiftKeyState(getCurrentInputEditorInfo());
  815. }
  816. }
  817. private void doubleSpace() {
  818. // if (!mAutoPunctuate) return;
  819. if (mCorrectionMode == Suggest.CORRECTION_NONE)
  820. return;
  821. final InputConnection ic = getCurrentInputConnection();
  822. if (ic == null)
  823. return;
  824. CharSequence lastThree = ic.getTextBeforeCursor(3, 0);
  825. if (lastThree != null && lastThree.length() == 3
  826. && Character.isLetterOrDigit(lastThree.charAt(0))
  827. && lastThree.charAt(1) == KEYCODE_SPACE
  828. && lastThree.charAt(2) == KEYCODE_SPACE) {
  829. ic.beginBatchEdit();
  830. ic.deleteSurroundingText(2, 0);
  831. ic.commitText(". ", 1);
  832. ic.endBatchEdit();
  833. updateShiftKeyState(getCurrentInputEditorInfo());
  834. }
  835. }
  836. public boolean addWordToDictionary(String word) {
  837. mUserDictionary.addWord(word, 128);
  838. return true;
  839. }
  840. /**
  841. * Helper to determine if a given character code is alphabetic.
  842. */
  843. private boolean isAlphabet(int code) {
  844. return mKeyboardSwitcher.getCurrentKeyboard().isLetter((char) code);
  845. }
  846. // Implementation of KeyboardViewListener
  847. public void onKey(int primaryCode, int[] keyCodes) {
  848. if (DEBUG) Log.d("AnySoftKeyboard", "onKey " + primaryCode);
  849. //see issue 160, and "KEYCODE_SPACE" branch about this var.
  850. boolean switchToAlphabetAtTheEnd = false;
  851. switch (primaryCode) {
  852. case Keyboard.KEYCODE_DELETE:
  853. handleBackspace();
  854. break;
  855. case Keyboard.KEYCODE_SHIFT:
  856. handleShift();
  857. break;
  858. case AnyKeyboard.KEYCODE_CTRL:
  859. //mCtrl = true;
  860. break;
  861. case AnyKeyboard.KEYCODE_LEFT:
  862. sendDownUpKeyEvents(KeyEvent.KEYCODE_DPAD_LEFT);
  863. break;
  864. case AnyKeyboard.KEYCODE_RIGHT:
  865. sendDownUpKeyEvents(KeyEvent.KEYCODE_DPAD_RIGHT);
  866. break;
  867. case AnyKeyboard.KEYCODE_UP:
  868. sendDownUpKeyEvents(KeyEvent.KEYCODE_DPAD_UP);
  869. break;
  870. case AnyKeyboard.KEYCODE_DOWN:
  871. sendDownUpKeyEvents(KeyEvent.KEYCODE_DPAD_DOWN);
  872. break;
  873. case Keyboard.KEYCODE_CANCEL:
  874. if (mOptionsDialog == null || !mOptionsDialog.isShowing()) {
  875. handleClose();
  876. }
  877. break;
  878. case AnyKeyboardView.KEYCODE_OPTIONS:
  879. showOptionsMenu();
  880. break;
  881. case AnyKeyboard.KEYCODE_SMILEY:
  882. if (mSmileyOnShortPress) {
  883. // Log.d("AnySoftKeyboard", "SMILEY short: type smiley");
  884. onText(mConfig.getSmileyText());
  885. } else {
  886. // Log.d("AnySoftKeyboard", "SMILEY short: popup smileys");
  887. if (mInputView != null)
  888. mInputView.simulateLongPress(AnyKeyboard.KEYCODE_SMILEY);
  889. }
  890. break;
  891. case AnyKeyboardView.KEYCODE_SMILEY_LONGPRESS:
  892. if (mSmileyOnShortPress) {
  893. // Log.d("AnySoftKeyboard", "SMILEY long: popup smileys");
  894. if (mInputView != null)
  895. mInputView.simulateLongPress(AnyKeyboard.KEYCODE_SMILEY);
  896. } else {
  897. // Log.d("AnySoftKeyboard", "SMILEY long: type smiley");
  898. onText(mConfig.getSmileyText());
  899. }
  900. break;
  901. case Keyboard.KEYCODE_MODE_CHANGE:
  902. nextKeyboard(getCurrentInputEditorInfo(), NextKeyboardType.Symbols);
  903. break;
  904. case AnyKeyboard.KEYCODE_LANG_CHANGE:
  905. nextKeyboard(getCurrentInputEditorInfo(), NextKeyboardType.Alphabet);
  906. break;
  907. case AnyKeyboard.KEYCODE_ALTER_LAYOUT:
  908. nextAlterKeyboard(getCurrentInputEditorInfo());
  909. break;
  910. case AnyKeyboard.KEYCODE_KEYBOARD_CYCLE:
  911. nextKeyboard(getCurrentInputEditorInfo(), NextKeyboardType.Any);
  912. break;
  913. case AnyKeyboard.KEYCODE_KEYBOARD_REVERSE_CYCLE:
  914. nextKeyboard(getCurrentInputEditorInfo(), NextKeyboardType.PreviousAny);
  915. break;
  916. case KEYCODE_SPACE:
  917. // Issue 160: Space in symbols keyboards should switch to
  918. // alphabet keyboard
  919. if (DEBUG)
  920. Log.d(TAG, "SwitchKeyboardOnSpace: "
  921. + mSwitchKeyboardOnSpace);
  922. if (mSwitchKeyboardOnSpace
  923. && !mKeyboardSwitcher.isAlphabetMode()) {
  924. switchToAlphabetAtTheEnd = true;
  925. }
  926. //Note: letting the space fall to the DEFAULT
  927. default:
  928. primaryCode = translatePrimaryCodeFromCurrentKeyboard(primaryCode);
  929. // Issue 146: Right to left langs require reversed parenthesis
  930. if (mKeyboardSwitcher.isRightToLeftMode())
  931. primaryCode = Workarounds
  932. .workaroundParenthesisDirectionFix(primaryCode);
  933. if (isWordSeparator(primaryCode)) {
  934. handleSeparator(primaryCode);
  935. } else {
  936. handleCharacter(primaryCode, keyCodes);
  937. // reseting the mSpaceSent, which is set to true upon selecting
  938. // candidate
  939. mSpaceSent = false;
  940. }
  941. // Cancel the just reverted state
  942. mJustRevertedSeparator = null;
  943. if (switchToAlphabetAtTheEnd)
  944. {
  945. mKeyboardSwitcher.nextKeyboard(getCurrentInputEditorInfo(),
  946. NextKeyboardType.Alphabet);
  947. }
  948. break;
  949. }
  950. }
  951. public void onText(CharSequence text) {
  952. if (DEBUG)
  953. Log.d("AnySoftKeyboard", "onText: " + text);
  954. InputConnection ic = getCurrentInputConnection();
  955. if (ic == null)
  956. return;
  957. ic.beginBatchEdit();
  958. if (mPredicting) {
  959. commitTyped(ic);
  960. }
  961. ic.commitText(text, 1);
  962. ic.endBatchEdit();
  963. updateShiftKeyState(getCurrentInputEditorInfo());
  964. mJustRevertedSeparator = null;
  965. }
  966. private void handleBackspace() {
  967. boolean deleteChar = false;
  968. InputConnection ic = getCurrentInputConnection();
  969. if (ic == null)
  970. return;
  971. if (mPredicting) {
  972. final int length = mComposing.length();
  973. if (length > 0) {
  974. mComposing.delete(length - 1, length);
  975. mWord.deleteLast();
  976. ic.setComposingText(mComposing, 1);
  977. if (mComposing.length() == 0) {
  978. mPredicting = false;
  979. }
  980. postUpdateSuggestions();
  981. } else {
  982. ic.deleteSurroundingText(1, 0);
  983. }
  984. } else {
  985. deleteChar = true;
  986. }
  987. updateShiftKeyState(getCurrentInputEditorInfo());
  988. TextEntryState.backspace();
  989. if (TextEntryState.getState() == TextEntryState.STATE_UNDO_COMMIT) {
  990. revertLastWord(deleteChar);
  991. return;
  992. } else if (deleteChar) {
  993. sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
  994. // if (mDeleteCount > DELETE_ACCELERATE_AT) {
  995. // sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
  996. // }
  997. }
  998. mJustRevertedSeparator = null;
  999. }
  1000. private void handleShift() {
  1001. if (mKeyboardSwitcher.isAlphabetMode()) {
  1002. //shift pressed and this is an alphabet keyboard
  1003. //we want to do:
  1004. //1)if keyboard is unshifted -> shift view and keyboard
  1005. //2)if keyboard is shifted -> capslock keyboard
  1006. //3)if keyboard is capslocked -> unshift view and keyboard
  1007. final AnyKeyboard currentKeyboard = mKeyboardSwitcher.getCurrentKeyboard();
  1008. final boolean caps;
  1009. if (!currentKeyboard.isShifted())
  1010. {
  1011. if (DEBUG) Log.d(TAG, "handleShift: current keyboard is un-shifted");
  1012. mInputView.setShifted(true);
  1013. caps = false;
  1014. }
  1015. else
  1016. {
  1017. if (currentKeyboard.isShiftLocked())
  1018. {
  1019. if (DEBUG) Log.d(TAG, "handleShift: current keyboard is CAPSLOCKED");
  1020. mInputView.setShifted(false);
  1021. caps = false;
  1022. }
  1023. else
  1024. {
  1025. if (DEBUG) Log.d(TAG, "handleShift: current keyboard is shifted");
  1026. mInputView.setShifted(true);
  1027. caps = true;
  1028. }
  1029. mInputView.requestRedraw();
  1030. }
  1031. // if (!mInputView.setShifted(mCapsLock || !mInputView.isShifted()))
  1032. // {
  1033. // //forcing redraw if view thinks it is still in the same state
  1034. // mInputView.requestRedraw();
  1035. // }
  1036. mCapsLock = caps;
  1037. currentKeyboard.setShiftLocked(mCapsLock);
  1038. }
  1039. // else {
  1040. // mKeyboardSwitcher.toggleShift();
  1041. // }
  1042. }
  1043. private void handleCharacter(int primaryCode, int[] keyCodes) {
  1044. // Log.d("AnySoftKeyboard",
  1045. // "handleCharacter: "+primaryCode+", isPredictionOn:"+isPredictionOn()+", mPredicting:"+mPredicting);
  1046. if (isAlphabet(primaryCode) && isPredictionOn()
  1047. && !isCursorTouchingWord()) {
  1048. if (!mPredicting) {
  1049. mPredicting = true;
  1050. mComposing.setLength(0);
  1051. mWord.reset();
  1052. }
  1053. }
  1054. // if (mInputView.isShifted()) {
  1055. // primaryCode = Character.toUpperCase(primaryCode);
  1056. // }
  1057. if (mPredicting) {
  1058. if ((mInputView != null) && mInputView.isShifted()
  1059. && mComposing.length() == 0) {
  1060. mWord.setCapitalized(true);
  1061. }
  1062. mComposing.append((char) primaryCode);
  1063. mWord.add(primaryCode, keyCodes);
  1064. InputConnection ic = getCurrentInputConnection();
  1065. if (ic != null) {
  1066. ic.setComposingText(mComposing, 1);
  1067. }
  1068. postUpdateSuggestions();
  1069. } else {
  1070. sendKeyChar((char) primaryCode);
  1071. }
  1072. updateShiftKeyState(getCurrentInputEditorInfo());
  1073. // measureCps();
  1074. TextEntryState.typedCharacter((char) primaryCode,
  1075. isWordSeparator(primaryCode));
  1076. }
  1077. private int translatePrimaryCodeFromCurrentKeyboard(int primaryCode) {
  1078. if (DEBUG)
  1079. Log.d("AnySoftKeyboard",
  1080. "translatePrimaryCodeFromCurrentKeyboard: " + primaryCode);
  1081. if (isInputViewShown()) {
  1082. if (DEBUG)
  1083. Log
  1084. .v("AnySoftKeyboard",
  1085. "translatePrimaryCodeFromCurrentKeyboard: isInputViewShown");
  1086. if ((mInputView != null) && mInputView.isShifted()) {
  1087. if (DEBUG)
  1088. Log
  1089. .d("AnySoftKeyboard",
  1090. "translatePrimaryCodeFromCurrentKeyboard: mInputView.isShifted()");
  1091. return mKeyboardSwitcher.getCurrentKeyboard()
  1092. .getShiftedKeyValue(primaryCode);
  1093. }
  1094. }
  1095. return primaryCode;
  1096. }
  1097. private void handleSeparator(int primaryCode) {
  1098. boolean pickedDefault = false;
  1099. // Handle separator
  1100. InputConnection ic = getCurrentInputConnection();
  1101. if (ic != null) {
  1102. ic.beginBatchEdit();
  1103. }
  1104. if (mPredicting) {
  1105. // In certain languages where single quote is a separator, it's
  1106. // better
  1107. // not to auto correct, but accept the typed word. For instance,
  1108. // in Italian dov' should not be expanded to dove' because the
  1109. // elision
  1110. // requires the last vowel to be removed.
  1111. if (mAutoCorrectOn
  1112. && primaryCode != '\''
  1113. && (mJustRevertedSeparator == null
  1114. || mJustRevertedSeparator.length() == 0 || mJustRevertedSeparator
  1115. .charAt(0) != primaryCode)) {
  1116. pickDefaultSuggestion();
  1117. pickedDefault = true;
  1118. } else {
  1119. commitTyped(ic);
  1120. }
  1121. }
  1122. sendKeyChar((char) primaryCode);
  1123. TextEntryState.typedCharacter((char) primaryCode, true);
  1124. if (TextEntryState.getState() == TextEntryState.STATE_PUNCTUATION_AFTER_ACCEPTED
  1125. && primaryCode != KEYCODE_ENTER && mSpaceSent) {
  1126. swapPunctuationAndSpace();
  1127. } else if (isPredictionOn() && primaryCode == ' ') {
  1128. // else if (TextEntryState.STATE_SPACE_AFTER_ACCEPTED) {
  1129. doubleSpace();
  1130. }
  1131. if (pickedDefault && mBestWord != null) {
  1132. TextEntryState.acceptedDefault(mWord.getTypedWord(), mBestWord);
  1133. }
  1134. updateShiftKeyState(getCurrentInputEditorInfo());
  1135. if (ic != null) {
  1136. ic.endBatchEdit();
  1137. }
  1138. }
  1139. private void handleClose() {
  1140. commitTyped(getCurrentInputConnection());
  1141. requestHideSelf(0);
  1142. if (mInputView != null)
  1143. mInputView.closing();
  1144. TextEntryState.endSession();
  1145. }
  1146. // private void checkToggleCapsLock() {
  1147. // if (mKeyboardSwitcher.getCurrentKeyboard().isShifted()) {
  1148. // toggleCapsLock();
  1149. // }
  1150. // }
  1151. private void postUpdateSuggestions() {
  1152. mHandler.removeMessages(MSG_UPDATE_SUGGESTIONS);
  1153. mHandler.sendMessageDelayed(mHandler
  1154. .obtainMessage(MSG_UPDATE_SUGGESTIONS), 100);
  1155. }
  1156. private boolean isPredictionOn() {
  1157. boolean predictionOn = mPredictionOn;
  1158. // if (!onEvaluateInputViewShown()) predictionOn &=
  1159. // mPredictionLandscape;
  1160. return predictionOn;
  1161. }
  1162. private boolean shouldCandidatesStripBeShown() {
  1163. boolean shown = isPredictionOn() && (mShowSuggestions || isFullscreenMode());
  1164. if (!onEvaluateInputViewShown())
  1165. shown &= mPredictionLandscape;
  1166. return shown;
  1167. }
  1168. private void updateSuggestions() {
  1169. if (DEBUG)
  1170. Log.d("AnySoftKeyboard", "updateSuggestions: has mSuggest:"
  1171. + (mSuggest != null) + ", isPredictionOn:"
  1172. + isPredictionOn() + ", mPredicting:" + mPredicting
  1173. + ", mCorrectionMode:" + mCorrectionMode);
  1174. // Check if we have a suggestion engine attached.
  1175. if (mSuggest == null) {
  1176. return;
  1177. }
  1178. final boolean showSuggestions = (mCandidateView != null && mPredicting
  1179. && isPredictionOn() && shouldCandidatesStripBeShown());
  1180. if (!showSuggestions) {
  1181. if (mCandidateView != null)
  1182. mCandidateView.setSuggestions(null, false, false, false);
  1183. return;
  1184. }
  1185. List<CharSequence> stringList = mSuggest.getSuggestions(mInputView,
  1186. mWord, false);
  1187. boolean correctionAvailable = mSuggest.hasMinimalCorrection();
  1188. // || mCorrectionMode == mSuggest.CORRECTION_FULL;
  1189. CharSequence typedWord = mWord.getTypedWord();
  1190. // If we're in basic correct
  1191. boolean typedWordValid = mSuggest.isValidWord(typedWord);
  1192. if (mCorrectionMode == Suggest.CORRECTION_FULL) {
  1193. correctionAvailable |= typedWordValid;
  1194. }
  1195. mCandidateView.setSuggestions(stringList, false, typedWordValid,
  1196. correctionAvailable);
  1197. if (stringList.size() > 0) {
  1198. if (correctionAvailable && !typedWordValid && stringList.size() > 1) {
  1199. mBestWord = stringList.get(1);
  1200. } else {
  1201. mBestWord = typedWord;
  1202. }
  1203. } else {
  1204. mBestWord = null;
  1205. }
  1206. setCandidatesViewShown(shouldCandidatesStripBeShown() || mCompletionOn);
  1207. }
  1208. private void pickDefaultSuggestion() {
  1209. // Complete any pending candidate query first
  1210. if (mHandler.hasMessages(MSG_UPDATE_SUGGESTIONS)) {
  1211. mHandler.removeMessages(MSG_UPDATE_SUGGESTIONS);
  1212. updateSuggestions();
  1213. }
  1214. if (mBestWord != null) {
  1215. TextEntryState.acceptedDefault(mWord.getTypedWord(), mBestWord);
  1216. mJustAccepted = true;
  1217. pickSuggestion(mBestWord);
  1218. }
  1219. }
  1220. public void pickSuggestionManually(int index, CharSequence suggestion) {
  1221. if (mCompletionOn && mCompletions != null && index >= 0
  1222. && index < mCompletions.length) {
  1223. CompletionInfo ci = mCompletions[index];
  1224. InputConnection ic = getCurrentInputConnection();
  1225. if (ic != null) {
  1226. ic.commitCompletion(ci);
  1227. }
  1228. mCommittedLength = suggestion.length();
  1229. if (mCandidateView != null) {
  1230. mCandidateView.clear();
  1231. }
  1232. updateShiftKeyState(getCurrentInputEditorInfo());
  1233. return;
  1234. }
  1235. pickSuggestion(suggestion);
  1236. TextEntryState.acceptedSuggestion(mComposing.toString(), suggestion);
  1237. // Follow it with a space
  1238. if (mAutoSpace) {
  1239. mSpaceSent = true;
  1240. sendSpace();
  1241. }
  1242. // Fool the state watcher so that a subsequent backspace will not do a
  1243. // revert
  1244. TextEntryState.typedCharacter((char) KEYCODE_SPACE, true);
  1245. }
  1246. private void pickSuggestion(CharSequence suggestion) {
  1247. if (mCapsLock) {
  1248. suggestion = suggestion.toString().toUpperCase();
  1249. } else if (preferCapitalization()
  1250. || (mKeyboardSwitcher.isAlphabetMode() && (mInputView != null) && mInputView
  1251. .isShifted())) {
  1252. suggestion = Character.toUpperCase(suggestion.charAt(0))
  1253. + suggestion.subSequence(1, suggestion.length()).toString();
  1254. }
  1255. InputConnection ic = getCurrentInputConnection();
  1256. if (ic != null) {
  1257. ic.commitText(suggestion, 1);
  1258. }
  1259. mPredicting = false;
  1260. mCommittedLength = suggestion.length();
  1261. if (mCandidateView != null) {
  1262. mCandidateView.setSuggestions(null, false, false, false);
  1263. }
  1264. updateShiftKeyState(getCurrentInputEditorInfo());
  1265. }
  1266. private boolean isCursorTouchingWord() {
  1267. InputConnection ic = getCurrentInputConnection();
  1268. if (ic == null)
  1269. return false;
  1270. CharSequence toLeft = ic.getTextBeforeCursor(1, 0);
  1271. CharSequence toRight = ic.getTextAfterCursor(1, 0);
  1272. if (!TextUtils.isEmpty(toLeft) && !isWordSeparator(toLeft.charAt(0))) {
  1273. return true;
  1274. }
  1275. if (!TextUtils.isEmpty(toRight) && !isWordSeparator(toRight.charAt(0))) {
  1276. return true;
  1277. }
  1278. return false;
  1279. }
  1280. public void revertLastWord(boolean deleteChar) {
  1281. final int length = mComposing.length();
  1282. if (!mPredicting && length > 0) {
  1283. final InputConnection ic = getCurrentInputConnection();
  1284. mPredicting = true;
  1285. ic.beginBatchEdit();
  1286. mJustRevertedSeparator = ic.getTextBeforeCursor(1, 0);
  1287. if (deleteChar)
  1288. ic.deleteSurroundingText(1, 0);
  1289. int toDelete = mCommittedLength;
  1290. CharSequence toTheLeft = ic
  1291. .getTextBeforeCursor(mCommittedLength, 0);
  1292. if (toTheLeft != null && toTheLeft.length() > 0
  1293. && isWordSeparator(toTheLeft.charAt(0))) {
  1294. toDelete--;
  1295. }
  1296. ic.deleteSurroundingText(toDelete, 0);
  1297. ic.setComposingText(mComposing, 1);
  1298. TextEntryState.backspace();
  1299. ic.endBatchEdit();
  1300. postUpdateSuggestions();
  1301. } else {
  1302. sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
  1303. mJustRevertedSeparator = null;
  1304. }
  1305. }
  1306. // protected String getWordSeparators() {
  1307. // return mWordSeparators;
  1308. // }
  1309. public boolean isWordSeparator(int code) {
  1310. // String separators = getWordSeparators();
  1311. // return separators.contains(String.valueOf((char)code));
  1312. return (!isAlphabet(code));
  1313. }
  1314. public boolean isPunctuationCharacter(int code) {
  1315. return PUNCTUATION_CHARACTERS.contains(String.valueOf((char) code));
  1316. }
  1317. private void sendSpace() {
  1318. sendKeyChar((char) KEYCODE_SPACE);
  1319. updateShiftKeyState(getCurrentInputEditorInfo());
  1320. }
  1321. public boolean preferCapitalization() {
  1322. return mWord.isCapitalized();
  1323. }
  1324. public void swipeRight() {
  1325. //nextKeyboard(getCurrentInputEditorInfo(), NextKeyboardType.Alphabet);
  1326. final int keyCode = AnySoftKeyboardConfiguration.getInstance().getSwipeRightKeyCode();
  1327. if (keyCode != 0)
  1328. onKey(keyCode, new int[]{keyCode});
  1329. }
  1330. public void swipeLeft() {
  1331. //nextKeyboard(getCurrentInputEditorInfo(), NextKeyboardType.Symbols);
  1332. final int keyCode = AnySoftKeyboardConfiguration.getInstance().getSwipeLeftKeyCode();
  1333. if (keyCode != 0)
  1334. onKey(keyCode, new int[]{keyCode});
  1335. }
  1336. private void nextAlterKeyboard(EditorInfo currentEditorInfo)
  1337. {
  1338. Log.d("AnySoftKeyboard", "nextAlterKeyboard: currentEditorInfo.inputType="
  1339. + currentEditorInfo.inputType);
  1340. AnyKeyboard currentKeyboard = mKeyboardSwitcher.getCurrentKeyboard();
  1341. if (currentKeyboard == null) {
  1342. if (DEBUG) Log.d("AnySoftKeyboard", "nextKeyboard: Looking for next keyboard. No current keyboard.");
  1343. } else {
  1344. if (DEBUG) Log.d("AnySoftKeyboard", "nextKeyboard: Looking for next keyboard. Current keyboard is:"
  1345. + currentKeyboard.getKeyboardName());
  1346. }
  1347. currentKeyboard = mKeyboardSwitcher.nextAlterKeyboard(currentEditorInfo);
  1348. Log.i("AnySoftKeyboard", "nextAlterKeyboard: Setting next keyboard to: "
  1349. + currentKeyboard.getKeyboardName());
  1350. }
  1351. private void nextKeyboard(EditorInfo currentEditorInfo,
  1352. KeyboardSwitcher.NextKeyboardType type) {
  1353. Log.d("AnySoftKeyboard", "nextKeyboard: currentEditorInfo.inputType="
  1354. + currentEditorInfo.inputType + " type:" + type);
  1355. AnyKeyboard currentKeyboard = mKeyboardSwitcher.getCurrentKeyboard();
  1356. if (currentKeyboard == null) {
  1357. if (DEB