/ime/latinime/src/com/googlecode/eyesfree/inputmethod/latin/LatinIME.java
Java | 3197 lines | 2536 code | 347 blank | 314 comment | 755 complexity | d20e6071ec0049b14d3d7f255612dd4e MD5 | raw file
Large files files are truncated, but you can click here to view the full file
1/* 2 * Copyright (C) 2008 The Android Open Source Project 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 17package com.googlecode.eyesfree.inputmethod.latin; 18 19import com.google.android.marvin.aime.AccessibleInputConnection; 20 21import android.app.AlertDialog; 22import android.content.BroadcastReceiver; 23import android.content.Context; 24import android.content.DialogInterface; 25import android.content.Intent; 26import android.content.IntentFilter; 27import android.content.SharedPreferences; 28import android.content.res.Configuration; 29import android.content.res.Resources; 30import android.content.res.XmlResourceParser; 31import android.inputmethodservice.InputMethodService; 32import android.inputmethodservice.Keyboard; 33import android.inputmethodservice.Keyboard.Key; 34import android.media.AudioManager; 35import android.os.Build; 36import android.os.Debug; 37import android.os.Handler; 38import android.os.Message; 39import android.os.SystemClock; 40import android.preference.PreferenceActivity; 41import android.preference.PreferenceManager; 42import android.provider.Settings; 43import android.speech.SpeechRecognizer; 44import android.telephony.PhoneStateListener; 45import android.telephony.TelephonyManager; 46import android.text.TextUtils; 47import android.util.DisplayMetrics; 48import android.util.Log; 49import android.util.PrintWriterPrinter; 50import android.util.Printer; 51import android.view.HapticFeedbackConstants; 52import android.view.KeyCharacterMap; 53import android.view.KeyEvent; 54import android.view.LayoutInflater; 55import android.view.View; 56import android.view.ViewConfiguration; 57import android.view.ViewGroup; 58import android.view.ViewParent; 59import android.view.Window; 60import android.view.WindowManager; 61import android.view.inputmethod.CompletionInfo; 62import android.view.inputmethod.EditorInfo; 63import android.view.inputmethod.ExtractedText; 64import android.view.inputmethod.ExtractedTextRequest; 65import android.view.inputmethod.InputConnection; 66import android.view.inputmethod.InputMethodManager; 67import android.widget.LinearLayout; 68 69import com.googlecode.eyesfree.inputmethod.FeedbackUtil; 70import com.googlecode.eyesfree.inputmethod.PersistentInputMethodService; 71import com.googlecode.eyesfree.inputmethod.latin.LatinIMEUtil.RingCharBuffer; 72import com.googlecode.eyesfree.inputmethod.latin.tutorial.LatinTutorialDialog; 73import com.googlecode.eyesfree.inputmethod.voice.FieldContext; 74import com.googlecode.eyesfree.inputmethod.voice.SettingsUtil; 75import com.googlecode.eyesfree.inputmethod.voice.VoiceInput; 76 77import org.xmlpull.v1.XmlPullParserException; 78 79import java.io.FileDescriptor; 80import java.io.IOException; 81import java.io.PrintWriter; 82import java.util.ArrayList; 83import java.util.Collections; 84import java.util.HashMap; 85import java.util.List; 86import java.util.Locale; 87import java.util.Map; 88 89/** 90 * Input method implementation for Qwerty'ish keyboard. 91 */ 92public class LatinIME extends PersistentInputMethodService implements VoiceInput.UiListener, 93 SharedPreferences.OnSharedPreferenceChangeListener { 94 private static final String TAG = "LatinIME"; 95 private static final boolean PERF_DEBUG = false; 96 static final boolean DEBUG = false; 97 static final boolean TRACE = false; 98 static final boolean VOICE_INSTALLED = supportsVoiceInput(); 99 static final boolean ENABLE_VOICE_BUTTON = supportsVoiceInput(); 100 101 /** 102 * Permission to send {@link Intent} broadcast requests to LatinIME. 103 */ 104 public static final String PERMISSION_REQUEST = "com.googlecode.eyesfree.inputmethod.latin.PERMISSION_REQUEST"; 105 106 // Mode change request broadcast constants 107 public static final String REQUEST_KEYBOARD_MODE_CHANGE = "com.googlecode.eyesfree.inputmethod.latin.REQUEST_KEYBOARD_MODE_CHANGE"; 108 public static final String REQUEST_KEYBOARD_MODE_UPDATE = "com.googlecode.eyesfree.inputmethod.latin.REQUEST_KEYBOARD_MODE_UPDATE"; 109 110 // Mode change broadcast constants 111 public static final String BROADCAST_KEYBOARD_MODE_CHANGE = "com.googlecode.eyesfree.inputmethod.latin.KEYBOARD_MODE_CHANGE"; 112 public static final String BROADCAST_KEYBOARD_MODE_UPDATE = "com.googlecode.eyesfree.inputmethod.latin.KEYBOARD_MODE_UPDATE"; 113 public static final String BROADCAST_LEFT_KEYBOARD_AREA = "com.googlecode.eyesfree.inputmethod.latin.LEFT_KEYBOARD_AREA"; 114 public static final String BROADCAST_ENTERED_KEYBOARD_AREA = "com.googlecode.eyesfree.inputmethod.latin.ENTERED_KEYBOARD_AREA"; 115 public static final String BROADCAST_UP_OUTSIDE_KEYBOARD = "com.googlecode.eyesfree.inputmethod.latin.TYPED_OUTSIDE_KEYBOARD"; 116 public static final String BROADCAST_GRANULARITY_CHANGE = "com.googlecode.eyesfree.inputmethod.latin.GRANULARITY_CHANGE"; 117 118 public static final String EXTRA_MODE = "mode"; 119 public static final String EXTRA_GRANULARITY = "granularity"; 120 121 // Vibration pattern constants 122 private static final long[] VIBRATE_PATTERN_SELECTED = new long[] { 123 0, 30 124 }; 125 private static final long[] VIBRATE_PATTERN_SWIPE = new long[] { 126 0, 20, 0, 20 127 }; 128 private static final long[] VIBRATE_PATTERN_TAP = new long[] { 129 0, 20 130 }; 131 private static final long[] VIBRATE_PATTERN_DOUBLE_TAP = new long[] { 132 0, 20, 0, 20 133 }; 134 private static final long[] VIBRATE_PATTERN_MODE_CHANGE = new long[] { 135 0, 50 136 }; 137 private static final long[] VIBRATE_PATTERN_ENTERED = new long[] { 138 0, 20, 0, 50 139 }; 140 private static final long[] VIBRATE_PATTERN_LEFT = new long[] { 141 0, 50, 0, 20 142 }; 143 private static final long[] VIBRATE_PATTERN_LONG_PRESS = new long[] { 144 0, 20, 40, 20, 40, 20, 40, 20 145 }; 146 147 // Sound resource constants 148 private static final int SOUND_RESOURCE_SELECTED = R.raw.tick; 149 private static final int SOUND_RESOURCE_SWIPE = R.raw.tick; 150 private static final int SOUND_RESOURCE_TAP = R.raw.tick; 151 private static final int SOUND_RESOURCE_DOUBLE_TAP = R.raw.tick; 152 private static final int SOUND_RESOURCE_LONG_PRESS = R.raw.tick; 153 154 private static final String PREF_AUTO_SWITCH = "auto_switch"; 155 private static final String PREF_VIBRATE_ON = "vibrate_on"; 156 private static final String PREF_SOUND_ON = "sound_on"; 157 private static final String PREF_POPUP_ON = "popup_on"; 158 private static final String PREF_AUTO_CAP = "auto_cap"; 159 private static final String PREF_QUICK_FIXES = "quick_fixes"; 160 private static final String PREF_SHOW_SUGGESTIONS = "show_suggestions"; 161 private static final String PREF_AUTO_COMPLETE = "auto_complete"; 162 private static final String PREF_BIGRAM_SUGGESTIONS = "bigram_suggestion"; 163 private static final String PREF_VOICE_MODE = "voice_mode"; 164 private static final String PREF_LINEAR_NAVIGATION = "linear_navigation"; 165 166 // Whether or not the user has used voice input before (and thus, whether to 167 // show the first-run warning dialog or not). 168 private static final String PREF_HAS_USED_VOICE_INPUT = "has_used_voice_input"; 169 170 // Whether or not the user has used voice input from an unsupported locale 171 // UI before. For example, the user has a Chinese UI but activates voice 172 // input. 173 private static final String PREF_HAS_USED_VOICE_INPUT_UNSUPPORTED_LOCALE = "has_used_voice_input_unsupported_locale"; 174 175 // A list of locales which are supported by default for voice input, unless 176 // we get a different list from Gservices. 177 public static final String DEFAULT_VOICE_INPUT_SUPPORTED_LOCALES = "en " + "en_US " + "en_GB " 178 + "en_AU " + "en_CA " + "en_IE " + "en_IN " + "en_NZ " + "en_SG " + "en_ZA "; 179 180 // The private IME option used to indicate that no microphone should be 181 // shown for a given text field. For instance this is specified by the 182 // search dialog 183 // when the dialog is already showing a voice search button. 184 private static final String IME_OPTION_NO_MICROPHONE = "nm"; 185 186 public static final String PREF_HAS_RUN_TUTORIAL = "has_run_tutorial"; 187 public static final String PREF_SELECTED_LANGUAGES = "selected_languages"; 188 public static final String PREF_INPUT_LANGUAGE = "input_language"; 189 private static final String PREF_RECORRECTION_ENABLED = "recorrection_enabled"; 190 191 private static final int MSG_UPDATE_SUGGESTIONS = 0; 192 private static final int MSG_UPDATE_SHIFT_STATE = 1; 193 private static final int MSG_VOICE_RESULTS = 2; 194 private static final int MSG_UPDATE_OLD_SUGGESTIONS = 3; 195 196 // How many continuous deletes at which to start deleting at a higher speed. 197 private static final int DELETE_ACCELERATE_AT = 20; 198 // Key events coming any faster than this are long-presses. 199 private static final int QUICK_PRESS = 200; 200 201 static final int KEYCODE_ENTER = '\n'; 202 static final int KEYCODE_SPACE = ' '; 203 static final int KEYCODE_PERIOD = '.'; 204 205 // Contextual menu positions 206 private static final int POS_METHOD = 0; 207 private static final int POS_SETTINGS = 1; 208 private static final int POS_ACCESSIBILITY_SETTINGS = 2; 209 210 // Receive phone call status updates. 211 private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() { 212 @Override 213 public void onCallStateChanged(int state, String incomingNumber) { 214 super.onCallStateChanged(state, incomingNumber); 215 216 switch (state) { 217 case TelephonyManager.CALL_STATE_IDLE: 218 // This is a no-op if the cache has been invalidated. 219 mInCallScreen = false; 220 requestKeyboardMode(mCachedForcedMode); 221 break; 222 case TelephonyManager.CALL_STATE_RINGING: 223 // Cache the current forced mode and switch to FORCE_HIDDEN. 224 int currentMode = getKeyboardMode(); 225 requestKeyboardMode(FORCE_HIDDEN); 226 mCachedForcedMode = currentMode; 227 mInCallScreen = true; 228 break; 229 case TelephonyManager.CALL_STATE_OFFHOOK: 230 // The user picked up the call. Do nothing. 231 break; 232 } 233 } 234 }; 235 236 // Receive ringer mode changes to detect silent mode and broadcast requests. 237 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 238 @Override 239 public void onReceive(Context context, Intent intent) { 240 String action = intent.getAction(); 241 242 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) { 243 updateRingerMode(); 244 } else if (REQUEST_KEYBOARD_MODE_CHANGE.equals(action)) { 245 // Don't make changes unless accessibility is enabled. 246 if (isAccessibilityEnabled()) { 247 requestKeyboardMode(intent.getIntExtra(EXTRA_MODE, FORCE_DPAD)); 248 } 249 } else if (REQUEST_KEYBOARD_MODE_UPDATE.equals(action)) { 250 Intent broadcast = new Intent(BROADCAST_KEYBOARD_MODE_UPDATE); 251 broadcast.putExtra(EXTRA_MODE, mForcedMode); 252 sendBroadcast(broadcast, PERMISSION_REQUEST); 253 } 254 } 255 }; 256 257 // private LatinKeyboardView mInputView; 258 private LinearLayout mCandidateViewContainer; 259 private CandidateView mCandidateView; 260 private Suggest mSuggest; 261 private CompletionInfo[] mCompletions; 262 263 private AlertDialog mOptionsDialog; 264 private AlertDialog mVoiceWarningDialog; 265 266 /* package */KeyboardSwitcher mKeyboardSwitcher; 267 268 private UserDictionary mUserDictionary; 269 private UserBigramDictionary mUserBigramDictionary; 270 private ContactsDictionary mContactsDictionary; 271 private AutoDictionary mAutoDictionary; 272 273 private Hints mHints; 274 275 private Resources mResources; 276 private TelephonyManager mTelephonyManager; 277 private AccessibilityUtils mAccessibilityUtils; 278 private FeedbackUtil mFeedbackUtil; 279 280 public static final int FORCE_HIDDEN = 0; 281 public static final int FORCE_DPAD = 1; 282 public static final int FORCE_CACHED = 2; 283 private static final int NUM_FORCE_MODES = 3; 284 private static final int FORCE_NONE = -1; 285 286 private int mForcedMode = FORCE_DPAD; 287 private int mCachedMode = KeyboardSwitcher.MODE_NONE; 288 private boolean mCachedPredictionOn = false; 289 private int mCachedForcedMode = FORCE_NONE; 290 private boolean mInCallScreen = false; 291 292 private String mInputLocale; 293 private String mSystemLocale; 294 private LanguageSwitcher mLanguageSwitcher; 295 296 private StringBuilder mComposing = new StringBuilder(); 297 private WordComposer mWord = new WordComposer(); 298 private int mCommittedLength; 299 private boolean mPredicting; 300 private boolean mRecognizing; 301 private boolean mAfterVoiceInput; 302 private boolean mImmediatelyAfterVoiceInput; 303 private boolean mShowingVoiceSuggestions; 304 private boolean mVoiceInputHighlighted; 305 private boolean mEnableVoiceButton; 306 private CharSequence mBestWord; 307 private boolean mPredictionOn; 308 private boolean mCompletionOn; 309 private boolean mHasDictionary; 310 private boolean mAutoSpace; 311 private boolean mJustAddedAutoSpace; 312 private boolean mAutoCorrectEnabled; 313 private boolean mReCorrectionEnabled; 314 private boolean mBigramSuggestionEnabled; 315 private boolean mAutoCorrectOn; 316 // TODO move this state variable outside LatinIME 317 private boolean mCapsLock; 318 private boolean mPasswordText; 319 private boolean mAutoSwitch; 320 private boolean mVibrateOn; 321 private boolean mSoundOn; 322 private boolean mPopupOn; 323 private boolean mAutoCap; 324 private boolean mQuickFixes; 325 private boolean mHasUsedVoiceInput; 326 private boolean mHasUsedVoiceInputUnsupportedLocale; 327 private boolean mLocaleSupportedForVoiceInput; 328 private boolean mShowSuggestions; 329 private boolean mIsShowingHint; 330 private int mCorrectionMode; 331 private boolean mEnableVoice = true; 332 private boolean mVoiceOnPrimary; 333 private int mOrientation; 334 private List<CharSequence> mSuggestPuncList; 335 // Keep track of the last selection range to decide if we need to show word 336 // alternatives 337 private int mLastSelectionStart; 338 private int mLastSelectionEnd; 339 340 // Input type is such that we should not auto-correct 341 private boolean mInputTypeNoAutoCorrect; 342 343 // Indicates whether the suggestion strip is to be on in landscape 344 private boolean mJustAccepted; 345 private CharSequence mJustRevertedSeparator; 346 private int mDeleteCount; 347 private long mLastKeyTime; 348 private int mPressedKey = LatinKeyboardView.KEYCODE_UNKNOWN; 349 350 // Modifier keys state 351 private ModifierKeyState mShiftKeyState = new ModifierKeyState(); 352 private ModifierKeyState mSymbolKeyState = new ModifierKeyState(); 353 354 private AudioManager mAudioManager; 355 // Align sound effect volume on music volume 356 private final float FX_VOLUME = -1.0f; 357 private boolean mSilentMode; 358 359 /* package */String mWordSeparators; 360 private String mSentenceSeparators; 361 private String mSuggestPuncs; 362 private VoiceInput mVoiceInput; 363 private VoiceResults mVoiceResults = new VoiceResults(); 364 private boolean mConfigurationChanging; 365 366 // Keeps track of most recently inserted text (multi-character key) for 367 // reverting 368 private CharSequence mEnteredText; 369 private boolean mRefreshKeyboardRequired; 370 371 // For each word, a list of potential replacements, usually from voice. 372 private Map<String, List<CharSequence>> mWordToSuggestions = new HashMap<String, List<CharSequence>>(); 373 374 private ArrayList<WordAlternatives> mWordHistory = new ArrayList<WordAlternatives>(); 375 376 private class VoiceResults { 377 List<String> candidates; 378 Map<String, List<CharSequence>> alternatives; 379 } 380 381 public abstract static class WordAlternatives { 382 protected CharSequence mChosenWord; 383 384 public WordAlternatives() { 385 // Nothing 386 } 387 388 public WordAlternatives(CharSequence chosenWord) { 389 mChosenWord = chosenWord; 390 } 391 392 @Override 393 public int hashCode() { 394 return mChosenWord.hashCode(); 395 } 396 397 public abstract CharSequence getOriginalWord(); 398 399 public CharSequence getChosenWord() { 400 return mChosenWord; 401 } 402 403 public abstract List<CharSequence> getAlternatives(); 404 } 405 406 public class TypedWordAlternatives extends WordAlternatives { 407 private WordComposer word; 408 409 public TypedWordAlternatives() { 410 // Nothing 411 } 412 413 public TypedWordAlternatives(CharSequence chosenWord, WordComposer wordComposer) { 414 super(chosenWord); 415 word = wordComposer; 416 } 417 418 @Override 419 public CharSequence getOriginalWord() { 420 return word.getTypedWord(); 421 } 422 423 @Override 424 public List<CharSequence> getAlternatives() { 425 return getTypedSuggestions(word); 426 } 427 } 428 429 /** 430 * This class sends a key event when it runs. This is useful for sending delayed key events, 431 * such as an UP event for a simulated long-press. 432 * 433 * @author alanv@google.com (Alan Viverette) 434 */ 435 private class KeyEventRunnable implements Runnable { 436 private final int mKeyCode; 437 private final int mMetaState; 438 private final long mDownTime; 439 440 public KeyEventRunnable(int keyCode, int metaState, long downTime) { 441 mKeyCode = keyCode; 442 mMetaState = metaState; 443 mDownTime = downTime; 444 } 445 446 @Override 447 public void run() { 448 final KeyEvent event = new KeyEvent(SystemClock.uptimeMillis(), mDownTime, 449 KeyEvent.ACTION_UP, mKeyCode, 0, mMetaState, KeyCharacterMap.BUILT_IN_KEYBOARD, 450 0, KeyEvent.FLAG_SOFT_KEYBOARD); 451 452 // First attempt to send the key to the IME, then to the input 453 // connection. 454 if (!onKeyUp(mKeyCode, event)) { 455 final InputConnection ic = getCurrentInputConnection(); 456 457 if (ic != null) { 458 ic.sendKeyEvent(event); 459 } 460 } 461 } 462 } 463 464 /* package */Handler mHandler = new Handler() { 465 @Override 466 public void handleMessage(Message msg) { 467 switch (msg.what) { 468 case MSG_UPDATE_SUGGESTIONS: 469 updateSuggestions(); 470 break; 471 case MSG_UPDATE_OLD_SUGGESTIONS: 472 setOldSuggestions(); 473 break; 474 case MSG_UPDATE_SHIFT_STATE: 475 updateShiftKeyState(getCurrentInputEditorInfo()); 476 break; 477 case MSG_VOICE_RESULTS: 478 handleVoiceResults(); 479 break; 480 } 481 } 482 }; 483 484 /** 485 * Returns <code>true<code> if this Android build supports voice input. * * 486 * * * @return <code>true<code>if this Android build supports voice input 487 */ 488 public static boolean supportsVoiceInput() { 489 return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO); 490 } 491 492 @Override 493 public void onCreate() { 494 super.onCreate(); 495 LatinImeLogger.init(this); 496 // setStatusIcon(R.drawable.ime_qwerty); 497 mResources = getResources(); 498 final Configuration conf = mResources.getConfiguration(); 499 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); 500 mLanguageSwitcher = new LanguageSwitcher(this); 501 mLanguageSwitcher.loadLocales(prefs); 502 mKeyboardSwitcher = new KeyboardSwitcher(this); 503 mKeyboardSwitcher.setLanguageSwitcher(mLanguageSwitcher); 504 mAccessibilityUtils = new AccessibilityUtils(this); 505 mFeedbackUtil = new FeedbackUtil(this); 506 mSystemLocale = conf.locale.toString(); 507 mLanguageSwitcher.setSystemLocale(conf.locale); 508 String inputLanguage = mLanguageSwitcher.getInputLanguage(); 509 if (inputLanguage == null) { 510 inputLanguage = conf.locale.toString(); 511 } 512 mReCorrectionEnabled = prefs.getBoolean(PREF_RECORRECTION_ENABLED, getResources() 513 .getBoolean(R.bool.default_recorrection_enabled)); 514 515 LatinIMEUtil.GCUtils.getInstance().reset(); 516 boolean tryGC = true; 517 for (int i = 0; i < LatinIMEUtil.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) { 518 try { 519 initSuggest(inputLanguage); 520 tryGC = false; 521 } catch (OutOfMemoryError e) { 522 tryGC = LatinIMEUtil.GCUtils.getInstance().tryGCOrWait(inputLanguage, e); 523 } 524 } 525 526 mOrientation = conf.orientation; 527 initSuggestPuncList(); 528 529 // Register to receive phone status changes. We need to switch to hidden 530 // mode when the user is receiving a phone call, then switch back when 531 // they're done. 532 mTelephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE); 533 mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); 534 535 // Register to receive ringer mode changes for silent mode and keyboard 536 // mode requests. 537 IntentFilter filter = new IntentFilter(); 538 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); 539 filter.addAction(REQUEST_KEYBOARD_MODE_CHANGE); 540 filter.addAction(REQUEST_KEYBOARD_MODE_UPDATE); 541 registerReceiver(mReceiver, filter); 542 543 if (VOICE_INSTALLED) { 544 mVoiceInput = new VoiceInput(this, this); 545 mHints = new Hints(this, new Hints.Display() { 546 @Override 547 public void showHint(int viewResource) { 548 LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); 549 View view = inflater.inflate(viewResource, null); 550 setCandidatesView(view); 551 setCandidatesViewShown(true); 552 mIsShowingHint = true; 553 } 554 }); 555 } 556 prefs.registerOnSharedPreferenceChangeListener(this); 557 } 558 559 @Override 560 public void onWindowShown() { 561 super.onWindowShown(); 562 563 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); 564 boolean hasRunTutorial = prefs.getBoolean(PREF_HAS_RUN_TUTORIAL, false); 565 if (!hasRunTutorial) { 566 Intent showDialog = new Intent(this, LatinTutorialDialog.class); 567 showDialog.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 568 startActivity(showDialog); 569 570 SharedPreferences.Editor editor = prefs.edit(); 571 editor.putBoolean(PREF_HAS_RUN_TUTORIAL, true); 572 editor.commit(); 573 } 574 } 575 576 /** 577 * Loads a dictionary or multiple separated dictionary 578 * 579 * @return returns array of dictionary resource ids 580 */ 581 /* package */static int[] getDictionary(Resources res) { 582 String packageName = res.getResourcePackageName(R.xml.dictionary); 583 XmlResourceParser xrp = res.getXml(R.xml.dictionary); 584 ArrayList<Integer> dictionaries = new ArrayList<Integer>(); 585 586 try { 587 int current = xrp.getEventType(); 588 while (current != XmlResourceParser.END_DOCUMENT) { 589 if (current == XmlResourceParser.START_TAG) { 590 String tag = xrp.getName(); 591 if (tag != null) { 592 if (tag.equals("part")) { 593 String dictFileName = xrp.getAttributeValue(null, "name"); 594 dictionaries.add(res.getIdentifier(dictFileName, "raw", packageName)); 595 } 596 } 597 } 598 xrp.next(); 599 current = xrp.getEventType(); 600 } 601 } catch (XmlPullParserException e) { 602 Log.e(TAG, "Dictionary XML parsing failure"); 603 } catch (IOException e) { 604 Log.e(TAG, "Dictionary XML IOException"); 605 } 606 607 int count = dictionaries.size(); 608 int[] dict = new int[count]; 609 for (int i = 0; i < count; i++) { 610 dict[i] = dictionaries.get(i); 611 } 612 613 return dict; 614 } 615 616 private void initSuggest(String locale) { 617 mInputLocale = locale; 618 619 Resources orig = getResources(); 620 Configuration conf = orig.getConfiguration(); 621 Locale saveLocale = conf.locale; 622 conf.locale = new Locale(locale); 623 orig.updateConfiguration(conf, orig.getDisplayMetrics()); 624 if (mSuggest != null) { 625 mSuggest.close(); 626 } 627 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); 628 mQuickFixes = sp.getBoolean(PREF_QUICK_FIXES, true); 629 630 int[] dictionaries = getDictionary(orig); 631 mSuggest = new Suggest(this, dictionaries); 632 updateAutoTextEnabled(saveLocale); 633 if (mUserDictionary != null) 634 mUserDictionary.close(); 635 mUserDictionary = new UserDictionary(this, mInputLocale); 636 if (mContactsDictionary == null) { 637 mContactsDictionary = new ContactsDictionary(this, Suggest.DIC_CONTACTS); 638 } 639 if (mAutoDictionary != null) { 640 mAutoDictionary.close(); 641 } 642 mAutoDictionary = new AutoDictionary(this, this, mInputLocale, Suggest.DIC_AUTO); 643 if (mUserBigramDictionary != null) { 644 mUserBigramDictionary.close(); 645 } 646 mUserBigramDictionary = new UserBigramDictionary(this, this, mInputLocale, Suggest.DIC_USER); 647 mSuggest.setUserBigramDictionary(mUserBigramDictionary); 648 mSuggest.setUserDictionary(mUserDictionary); 649 mSuggest.setContactsDictionary(mContactsDictionary); 650 mSuggest.setAutoDictionary(mAutoDictionary); 651 updateCorrectionMode(); 652 mWordSeparators = mResources.getString(R.string.word_separators); 653 mSentenceSeparators = mResources.getString(R.string.sentence_separators); 654 655 conf.locale = saveLocale; 656 orig.updateConfiguration(conf, orig.getDisplayMetrics()); 657 } 658 659 @Override 660 public void onDestroy() { 661 if (mUserDictionary != null) { 662 mUserDictionary.close(); 663 } 664 665 if (mContactsDictionary != null) { 666 mContactsDictionary.close(); 667 } 668 669 // Unregister the phone state listener. 670 mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); 671 672 // Unregister the broadcast receiver. 673 try { 674 unregisterReceiver(mReceiver); 675 } catch (IllegalArgumentException e) { 676 // Ignore the case where broadcast receiver is already unregistered. 677 } 678 679 if (VOICE_INSTALLED && mVoiceInput != null) { 680 mVoiceInput.destroy(); 681 } 682 683 LatinImeLogger.commit(); 684 LatinImeLogger.onDestroy(); 685 686 super.onDestroy(); 687 } 688 689 @Override 690 public void onConfigurationChanged(Configuration conf) { 691 // If the system locale changes and is different from the saved 692 // locale (mSystemLocale), then reload the input locale list from the 693 // latin ime settings (shared prefs) and reset the input locale 694 // to the first one. 695 final String systemLocale = conf.locale.toString(); 696 if (!TextUtils.equals(systemLocale, mSystemLocale)) { 697 mSystemLocale = systemLocale; 698 if (mLanguageSwitcher != null) { 699 mLanguageSwitcher.loadLocales(PreferenceManager.getDefaultSharedPreferences(this)); 700 mLanguageSwitcher.setSystemLocale(conf.locale); 701 toggleLanguage(true, true); 702 } else { 703 reloadKeyboards(); 704 } 705 } 706 // If orientation changed while predicting, commit the change 707 if (conf.orientation != mOrientation) { 708 InputConnection ic = getCurrentInputConnection(); 709 commitTyped(ic); 710 if (ic != null) 711 ic.finishComposingText(); // For voice input 712 mOrientation = conf.orientation; 713 reloadKeyboards(); 714 } 715 mConfigurationChanging = true; 716 super.onConfigurationChanged(conf); 717 if (mRecognizing) { 718 switchToRecognitionStatusView(); 719 } 720 mConfigurationChanging = false; 721 } 722 723 @Override 724 public View onCreateInputView() { 725 mKeyboardSwitcher.recreateInputView(); 726 mKeyboardSwitcher.makeKeyboards(true); 727 mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_TEXT, 0, 728 shouldShowVoiceButton(makeFieldContext(), getCurrentInputEditorInfo())); 729 return mKeyboardSwitcher.getInputView(); 730 } 731 732 @Override 733 public View onCreateCandidatesView() { 734 mKeyboardSwitcher.makeKeyboards(true); 735 mCandidateViewContainer = (LinearLayout) getLayoutInflater().inflate(R.layout.candidates, 736 null); 737 mCandidateView = (CandidateView) mCandidateViewContainer.findViewById(R.id.candidates); 738 mCandidateView.setService(this); 739 setCandidatesViewShown(true); 740 return mCandidateViewContainer; 741 } 742 743 @Override 744 public void onStartInputView(EditorInfo attribute, boolean restarting) { 745 LatinKeyboardView inputView = mKeyboardSwitcher.getInputView(); 746 // In landscape mode, this method gets called without the input view 747 // being created. 748 if (inputView == null) { 749 return; 750 } 751 752 if (mRefreshKeyboardRequired) { 753 mRefreshKeyboardRequired = false; 754 toggleLanguage(true, true); 755 } 756 757 mKeyboardSwitcher.makeKeyboards(false); 758 759 TextEntryState.newSession(this); 760 761 // Most such things we decide below in the switch statement, but we need 762 // to know now whether this is a password text field, because we need to 763 // know now (before the switch statement) whether we want to enable the 764 // voice button. 765 mPasswordText = false; 766 int variation = attribute.inputType & EditorInfo.TYPE_MASK_VARIATION; 767 if (variation == EditorInfo.TYPE_TEXT_VARIATION_PASSWORD 768 || variation == EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) { 769 mPasswordText = true; 770 } 771 772 mEnableVoiceButton = shouldShowVoiceButton(makeFieldContext(), attribute); 773 final boolean enableVoiceButton = mEnableVoiceButton && mEnableVoice; 774 775 mAfterVoiceInput = false; 776 mImmediatelyAfterVoiceInput = false; 777 mShowingVoiceSuggestions = false; 778 mVoiceInputHighlighted = false; 779 mInputTypeNoAutoCorrect = false; 780 mPredictionOn = false; 781 mCompletionOn = false; 782 mCompletions = null; 783 mCapsLock = false; 784 mEnteredText = null; 785 786 mCachedPredictionOn = false; 787 mCachedMode = KeyboardSwitcher.MODE_TEXT; 788 789 switch (attribute.inputType & EditorInfo.TYPE_MASK_CLASS) { 790 case EditorInfo.TYPE_CLASS_NUMBER: 791 case EditorInfo.TYPE_CLASS_DATETIME: 792 // fall through 793 // NOTE: For now, we use the phone keyboard for NUMBER and 794 // DATETIME until we get 795 // a dedicated number entry keypad. 796 // TODO: Use a dedicated number entry keypad here when we get 797 // one. 798 case EditorInfo.TYPE_CLASS_PHONE: 799 mCachedMode = KeyboardSwitcher.MODE_PHONE; 800 break; 801 case EditorInfo.TYPE_CLASS_TEXT: 802 mCachedMode = KeyboardSwitcher.MODE_TEXT; 803 mCachedPredictionOn = true; 804 // Make sure that passwords are not displayed in candidate view 805 if (variation == EditorInfo.TYPE_TEXT_VARIATION_PASSWORD 806 || variation == EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) { 807 mCachedPredictionOn = false; 808 } 809 if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS 810 || variation == EditorInfo.TYPE_TEXT_VARIATION_PERSON_NAME) { 811 mAutoSpace = false; 812 } else { 813 mAutoSpace = true; 814 } 815 if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS) { 816 mCachedPredictionOn = false; 817 mCachedMode = KeyboardSwitcher.MODE_EMAIL; 818 } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_URI) { 819 mCachedPredictionOn = false; 820 mCachedMode = KeyboardSwitcher.MODE_URL; 821 } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_SHORT_MESSAGE) { 822 mCachedMode = KeyboardSwitcher.MODE_IM; 823 } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_FILTER) { 824 mCachedPredictionOn = false; 825 } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT) { 826 mCachedMode = KeyboardSwitcher.MODE_WEB; 827 // If it's a browser edit field and auto correct is not ON 828 // explicitly, then 829 // disable auto correction, but keep suggestions on. 830 if ((attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0) { 831 mInputTypeNoAutoCorrect = true; 832 } 833 } 834 835 // If NO_SUGGESTIONS is set, don't do prediction. 836 if ((attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS) != 0) { 837 mCachedPredictionOn = false; 838 mInputTypeNoAutoCorrect = true; 839 } 840 // If it's not multiline and the autoCorrect flag is not set, 841 // then don't correct 842 if ((attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0 843 && (attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE) == 0) { 844 mInputTypeNoAutoCorrect = true; 845 } 846 if ((attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) { 847 mCachedPredictionOn = false; 848 mCompletionOn = isFullscreenMode(); 849 } 850 break; 851 } 852 853 int keyboardMode = mCachedMode; 854 boolean predictionOn = mCachedPredictionOn; 855 856 if (isAccessibilityEnabled()) { 857 // If auto-switch is on AND we're not already auto-switched AND we're 858 // focused on an editable field THEN cache the current mode and force 859 // typing mode. 860 if (mAutoSwitch && mCachedForcedMode == FORCE_NONE 861 && (attribute.inputType & EditorInfo.TYPE_MASK_CLASS) != EditorInfo.TYPE_NULL) { 862 mCachedForcedMode = mForcedMode; 863 mForcedMode = FORCE_CACHED; 864 mAccessibilityUtils.speakDescription(getString(R.string.spoken_mode_switch_keyboard)); 865 } else if (mForcedMode == FORCE_HIDDEN) { 866 keyboardMode = KeyboardSwitcher.MODE_HIDDEN; 867 predictionOn = false; 868 } else if (mForcedMode == FORCE_DPAD) { 869 keyboardMode = KeyboardSwitcher.MODE_DPAD; 870 predictionOn = false; 871 } 872 } 873 874 mPredictionOn = predictionOn; 875 mKeyboardSwitcher.setKeyboardMode(keyboardMode, attribute.imeOptions, enableVoiceButton); 876 877 inputView.closing(); 878 mComposing.setLength(0); 879 mPredicting = false; 880 mDeleteCount = 0; 881 mJustAddedAutoSpace = false; 882 loadSettings(); 883 updateShiftKeyState(attribute); 884 885 setCandidatesViewShownInternal(isCandidateStripVisible() || mCompletionOn, false /* needsInputViewShown */); 886 updateSuggestions(); 887 888 // If the dictionary is not big enough, don't auto correct 889 mHasDictionary = mSuggest.hasMainDictionary(); 890 891 updateCorrectionMode(); 892 893 inputView.setPreviewEnabled(mPopupOn); 894 inputView.setProximityCorrectionEnabled(true); 895 inputView.setAccessibilityEnabled(isAccessibilityEnabled()); 896 mPredictionOn = mPredictionOn && (mCorrectionMode > 0 || mShowSuggestions); 897 // If we just entered a text field, maybe it has some old text that 898 // requires correction 899 checkReCorrectionOnStart(); 900 if (TRACE) 901 Debug.startMethodTracing("/data/trace/latinime"); 902 } 903 904 private void checkReCorrectionOnStart() { 905 if (mReCorrectionEnabled && isPredictionOn()) { 906 // First get the cursor position. This is required by 907 // setOldSuggestions(), so that 908 // it can pass the correct range to setComposingRegion(). At this 909 // point, we don't 910 // have valid values for mLastSelectionStart/Stop because 911 // onUpdateSelection() has 912 // not been called yet. 913 InputConnection ic = getCurrentInputConnection(); 914 if (ic == null) 915 return; 916 ExtractedTextRequest etr = new ExtractedTextRequest(); 917 etr.token = 0; // anything is fine here 918 ExtractedText et = ic.getExtractedText(etr, 0); 919 if (et == null) 920 return; 921 922 mLastSelectionStart = et.startOffset + et.selectionStart; 923 mLastSelectionEnd = et.startOffset + et.selectionEnd; 924 925 // Then look for possible corrections in a delayed fashion 926 if (!TextUtils.isEmpty(et.text) && isCursorTouchingWord()) { 927 postUpdateOldSuggestions(); 928 } 929 } 930 } 931 932 @Override 933 public void onFinishInput() { 934 super.onFinishInput(); 935 936 LatinImeLogger.commit(); 937 onAutoCompletionStateChanged(false); 938 939 if (VOICE_INSTALLED && !mConfigurationChanging) { 940 if (mAfterVoiceInput) { 941 mVoiceInput.flushAllTextModificationCounters(); 942 mVoiceInput.logInputEnded(); 943 } 944 mVoiceInput.flushLogs(); 945 mVoiceInput.cancel(); 946 } 947 if (mKeyboardSwitcher.getInputView() != null) { 948 mKeyboardSwitcher.getInputView().closing(); 949 } 950 if (mAutoDictionary != null) 951 mAutoDictionary.flushPendingWrites(); 952 if (mUserBigramDictionary != null) 953 mUserBigramDictionary.flushPendingWrites(); 954 } 955 956 @Override 957 public void onFinishInputView(boolean finishingInput) { 958 super.onFinishInputView(finishingInput); 959 // Remove pending messages related to update suggestions 960 mHandler.removeMessages(MSG_UPDATE_SUGGESTIONS); 961 mHandler.removeMessages(MSG_UPDATE_OLD_SUGGESTIONS); 962 963 // If auto-switch is on and we're in auto-switched mode, then restore 964 // the previous forced mode. 965 if (mAutoSwitch && mCachedForcedMode != FORCE_NONE) { 966 requestKeyboardMode(mCachedForcedMode); 967 } 968 } 969 970 @Override 971 public void onUpdateExtractedText(int token, ExtractedText text) { 972 super.onUpdateExtractedText(token, text); 973 InputConnection ic = getCurrentInputConnection(); 974 if (!mImmediatelyAfterVoiceInput && mAfterVoiceInput && ic != null) { 975 if (mHints.showPunctuationHintIfNecessary(ic)) { 976 mVoiceInput.logPunctuationHintDisplayed(); 977 } 978 } 979 mImmediatelyAfterVoiceInput = false; 980 } 981 982 @Override 983 public void onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, 984 int candidatesStart, int candidatesEnd) { 985 super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart, 986 candidatesEnd); 987 988 if (DEBUG) { 989 Log.i(TAG, "onUpdateSelection: oss=" + oldSelStart + ", ose=" + oldSelEnd + ", nss=" 990 + newSelStart + ", nse=" + newSelEnd + ", cs=" + candidatesStart + ", ce=" 991 + candidatesEnd); 992 } 993 994 if (mAfterVoiceInput) { 995 mVoiceInput.setCursorPos(newSelEnd); 996 mVoiceInput.setSelectionSpan(newSelEnd - newSelStart); 997 } 998 999 // If the current selection in the text view changes, we should 1000 // clear whatever candidate text we have. 1001 if ((((mComposing.length() > 0 && mPredicting) || mVoiceInputHighlighted) 1002 && (newSelStart != candidatesEnd || newSelEnd != candidatesEnd) && mLastSelectionStart != newSelStart)) { 1003 mComposing.setLength(0); 1004 mPredicting = false; 1005 postUpdateSuggestions(); 1006 TextEntryState.reset(); 1007 InputConnection ic = getCurrentInputConnection(); 1008 if (ic != null) { 1009 ic.finishComposingText(); 1010 } 1011 mVoiceInputHighlighted = false; 1012 } else if (!mPredicting && !mJustAccepted) { 1013 switch (TextEntryState.getState()) { 1014 case ACCEPTED_DEFAULT: 1015 TextEntryState.reset(); 1016 //$FALL-THROUGH$ 1017 case SPACE_AFTER_PICKED: 1018 mJustAddedAutoSpace = false; // The user moved the cursor. 1019 break; 1020 } 1021 } 1022 mJustAccepted = false; 1023 postUpdateShiftKeyState(); 1024 1025 // Make a note of the cursor position 1026 mLastSelectionStart = newSelStart; 1027 mLastSelectionEnd = newSelEnd; 1028 1029 if (mReCorrectionEnabled) { 1030 // Don't look for corrections if the keyboard is not visible 1031 if (mKeyboardSwitcher != null && mKeyboardSwitcher.getInputView() != null 1032 && mKeyboardSwitcher.getInputView().isShown()) { 1033 // Check if we should go in or out of correction mode. 1034 if (isPredictionOn() 1035 && mJustRevertedSeparator == null 1036 && (candidatesStart == candidatesEnd || newSelStart != oldSelStart || TextEntryState 1037 .isCorrecting()) && (newSelStart < newSelEnd - 1 || (!mPredicting)) 1038 && !mVoiceInputHighlighted) { 1039 if (isCursorTouchingWord() || mLastSelectionStart < mLastSelectionEnd) { 1040 postUpdateOldSuggestions(); 1041 } else { 1042 abortCorrection(false); 1043 // Show the punctuation suggestions list if the current 1044 // one is not 1045 // and if not showing "Touch again to save". 1046 if (mCandidateView != null 1047 && !mSuggestPuncList.equals(mCandidateView.getSuggestions()) 1048 && !mCandidateView.isShowingAddToDictionaryHint()) { 1049 setNextSuggestions(); 1050 } 1051 } 1052 } 1053 } 1054 } 1055 } 1056 1057 /** 1058 * This is called when the user has clicked on the extracted text view, when running in 1059 * fullscreen mode. The default implementation hides the candidates view when this happens, but 1060 * only if the extracted text editor has a vertical scroll bar because its text doesn't fit. 1061 * Here we override the behavior due to the possibility that a re-correction could cause the 1062 * candidate strip to disappear and re-appear. 1063 */ 1064 @Override 1065 public void onExtractedTextClicked() { 1066 if (mReCorrectionEnabled && isPredictionOn()) 1067 return; 1068 1069 super.onExtractedTextClicked(); 1070 } 1071 1072 /** 1073 * This is called when the user has performed a cursor movement in the extracted text view, when 1074 * it is running in fullscreen mode. The default implementation hides the candidates view when a 1075 * vertical movement happens, but only if the extracted text editor has a vertical scroll bar 1076 * because its text doesn't fit. Here we override the behavior due to the possibility that a 1077 * re-correction could cause the candidate strip to disappear and re-appear. 1078 */ 1079 @Override 1080 public void onExtractedCursorMovement(int dx, int dy) { 1081 if (mReCorrectionEnabled && isPredictionOn()) 1082 return; 1083 1084 super.onExtractedCursorMovement(dx, dy); 1085 } 1086 1087 @Override 1088 public void hideWindow() { 1089 LatinImeLogger.commit(); 1090 onAutoCompletionStateChanged(false); 1091 1092 if (TRACE) 1093 Debug.stopMethodTracing(); 1094 if (mOptionsDialog != null && mOptionsDialog.isShowing()) { 1095 mOptionsDialog.dismiss(); 1096 mOptionsDialog = null; 1097 } 1098 if (!mConfigurationChanging) { 1099 if (mAfterVoiceInput) 1100 mVoiceInput.logInputEnded(); 1101 if (mVoiceWarningDialog != null && mVoiceWarningDialog.isShowing()) { 1102 mVoiceInput.logKeyboardWarningDialogDismissed(); 1103 mVoiceWarningDialog.dismiss(); 1104 mVoiceWarningDialog = null; 1105 } 1106 if (VOICE_INSTALLED & mRecognizing) { 1107 mVoiceInput.cancel(); 1108 } 1109 } 1110 mWordToSuggestions.clear(); 1111 mWordHistory.clear(); 1112 super.hideWindow(); 1113 TextEntryState.endSession(); 1114 } 1115 1116 @Override 1117 public void onDisplayCompletions(CompletionInfo[] completions) { 1118 if (DEBUG) { 1119 Log.i("foo", "Received completions:"); 1120 for (int i = 0; i < (completions != null ? completions.length : 0); i++) { 1121 Log.i("foo", " #" + i + ": " + completions[i]); 1122 } 1123 } 1124 if (mCompletionOn) { 1125 mCompletions = completions; 1126 if (completions == null) { 1127 clearSuggestions(); 1128 return; 1129 } 1130 1131 List<CharSequence> stringList = new ArrayList<CharSequence>(); 1132 for (int i = 0; i < (completions != null ? completions.length : 0); i++) { 1133 CompletionInfo ci = completions[i]; 1134 if (ci != null) 1135 stringList.add(ci.getText()); 1136 } 1137 // When in fullscreen mode, show completions generated by the 1138 // application 1139 setSuggestions(stringList, true, true, true); 1140 mBestWord = null; 1141 setCandidatesViewShown(true); 1142 } 1143 } 1144 1145 private void setCandidatesViewShownInternal(boolean shown, boolean needsInputViewShown) { 1146 // TODO: Remove this if we support candidates with hard keyboard 1147 if (onEvaluateInputViewShown()) { 1148 super.setCandidatesViewShown(shown && mKeyboardSwitcher.getInputView() != null 1149 && (needsInputViewShown ? mKeyboardSwitcher.getInputView().isShown() : true)); 1150 } 1151 } 1152 1153 @Override 1154 public void setCandidatesViewShown(boolean shown) { 1155 setCandidatesViewShownInternal(shown, true /* needsInputViewShown */); 1156 } 1157 1158 @Override 1159 public void onComputeInsets(InputMethodService.Insets outInsets) { 1160 super.onComputeInsets(outInsets); 1161 if (!isFullscreenMode()) { 1162 outInsets.contentTopInsets = outInsets.visibleTopInsets; 1163 } 1164 } 1165 1166 @Override 1167 public boolean onEvaluateFullscreenMode() { 1168 DisplayMetrics dm = getResources().getDisplayMetrics(); 1169 float displayHeight = dm.heightPixels; 1170 // If the display is more than X inches high, don't go to fullscreen 1171 // mode 1172 float dimen = getResources().getDimension(R.dimen.max_height_for_fullscreen); 1173 if (displayHeight > dimen) { 1174 return false; 1175 } else { 1176 return super.onEvaluateFullscreenMode(); 1177 } 1178 } 1179 1180 @Override 1181 public boolean onKeyDown(int keyCode, KeyEvent event) { 1182 switch (keyCode) { 1183 case KeyEvent.KEYCODE_BACK: 1184 if (event.getRepeatCount() == 0 && mKeyboardSwitcher.getInputView() != null) { 1185 if (mKeyboardSwitcher.getInputView().handleBack()) { 1186 return true; 1187 } 1188 } 1189 break; 1190 } 1191 return super.onKeyDown(keyCode, event); 1192 } 1193 1194 @Override 1195 public boolean onKeyUp(int keyCode, KeyEvent event) { 1196 switch (keyCode) { 1197 case KeyEvent.KEYCODE_DPAD_DOWN: 1198 case KeyEvent.KEYCODE_DPAD_UP: 1199 case KeyEvent.KEYCODE_DPAD_LEFT: 1200 case KeyEvent.KEYCODE_DPAD_RIGHT: 1201 LatinKeyboardView view = mKeyboardSwitcher.getInputView(); 1202 if (view != null && view.isShifted()) { 1203 // Force this key to be shifted. 1204 event = new KeyEvent(event.getDownTime(), event.getEventTime(), 1205 event.getAction(), event.getKeyCode(), event.getRepeatCount(), 1206 event.getMetaState() | KeyEvent.META_SHIFT_ON 1207 | KeyEvent.META_SHIFT_LEFT_ON, event.getDeviceId(), 1208 event.getScanCode(), event.getFlags()); 1209 } 1210 break; 1211 } 1212 return super.onKeyUp(keyCode, event); 1213 } 1214 1215 @Override 1216 public boolean onKeyLongPress(int keyCode, KeyEvent event) { 1217 switch (keyCode) { 1218 case KeyEvent.KEYCODE_VOLUME_UP: 1219 if (event.getRepeatCount() == 1) { 1220 cycleForcedType(1); 1221 } 1222 return true; 1223 case KeyEvent.KEYCODE_VOLUME_DOWN: 1224 if (event.getRepeatCount() == 1) { 1225 cycleForcedType(-1); 1226 } 1227 return true; 1228 } 1229 1230 return super.onKeyLongPress(keyCode, event); 1231 } 1232 1233 private void cycleForcedType(int di…
Large files files are truncated, but you can click here to view the full file