Fix the available input locales and moved Recorrection

Bug: 4409091

Change-Id: I6efd23ebb9528bf1bd35320057a0ea264c187451
main
satok 2011-05-11 15:19:24 +09:00
parent 4f3b59711f
commit f733074aae
14 changed files with 225 additions and 91 deletions

View File

@ -285,6 +285,8 @@
<string name="subtype_mode_nl_keyboard">Dutch Keyboard</string>
<!-- Description for Polish keyboard subtype [CHAR LIMIT=35] -->
<string name="subtype_mode_pl_keyboard">Polish Keyboard</string>
<!-- Description for Portuguese keyboard subtype [CHAR LIMIT=35] -->
<string name="subtype_mode_pt_keyboard">Portuguese Keyboard</string>
<!-- Description for Russian keyboard subtype [CHAR LIMIT=35] -->
<string name="subtype_mode_ru_keyboard">Russian Keyboard</string>
<!-- Description for Serbian keyboard subtype [CHAR LIMIT=35] -->

View File

@ -28,7 +28,7 @@
latin:verticalGap="@dimen/key_bottom_gap"
latin:popupKeyboardTemplate="@xml/kbd_popup_template"
latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
latin:keyboardLocale="de_DE"
latin:keyboardLocale="de"
>
<include
latin:keyboardLayout="@xml/kbd_qwertz_rows" />

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2011, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyboardHeight="@dimen/keyboardHeight"
latin:maxKeyboardHeight="50%p"
latin:rowHeight="25%p"
latin:keyWidth="10%p"
latin:horizontalGap="@dimen/key_horizontal_gap"
latin:verticalGap="@dimen/key_bottom_gap"
latin:popupKeyboardTemplate="@xml/kbd_popup_template"
latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
latin:keyboardLocale="es,es_US"
>
<include
latin:keyboardLayout="@xml/kbd_qwerty_rows" />
</Keyboard>

View File

@ -28,7 +28,7 @@
latin:verticalGap="@dimen/key_bottom_gap"
latin:popupKeyboardTemplate="@xml/kbd_popup_template"
latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
latin:keyboardLocale="fr_FR"
latin:keyboardLocale="fr"
>
<include
latin:keyboardLayout="@xml/kbd_azerty_rows" />

View File

@ -28,7 +28,7 @@
latin:verticalGap="@dimen/key_bottom_gap"
latin:popupKeyboardTemplate="@xml/kbd_popup_template"
latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
latin:keyboardLocale="pl_PL"
latin:keyboardLocale="pl"
>
<include
latin:keyboardLayout="@xml/kbd_qwerty_rows" />

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2011, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyboardHeight="@dimen/keyboardHeight"
latin:maxKeyboardHeight="50%p"
latin:rowHeight="25%p"
latin:keyWidth="10%p"
latin:horizontalGap="@dimen/key_horizontal_gap"
latin:verticalGap="@dimen/key_bottom_gap"
latin:popupKeyboardTemplate="@xml/kbd_popup_template"
latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
latin:keyboardLocale="pt"
>
<include
latin:keyboardLayout="@xml/kbd_qwerty_rows" />
</Keyboard>

View File

@ -27,7 +27,7 @@
latin:verticalGap="@dimen/key_bottom_gap"
latin:popupKeyboardTemplate="@xml/kbd_popup_template"
latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
latin:keyboardLocale="ru_RU"
latin:keyboardLocale="ru"
>
<include
latin:keyboardLayout="@xml/kbd_ru_rows" />

View File

@ -150,6 +150,11 @@
android:imeSubtypeLocale="pl"
android:imeSubtypeMode="keyboard"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_mode_pt_keyboard"
android:imeSubtypeLocale="pt"
android:imeSubtypeMode="keyboard"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_mode_ru_keyboard"
android:imeSubtypeLocale="ru"

View File

@ -43,6 +43,8 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map.Entry;
import java.util.TreeMap;
public class InputLanguageSelection extends PreferenceActivity {
@ -51,21 +53,17 @@ public class InputLanguageSelection extends PreferenceActivity {
private HashMap<CheckBoxPreference, Locale> mLocaleMap =
new HashMap<CheckBoxPreference, Locale>();
private static class Loc implements Comparable<Object> {
private static class LocaleEntry implements Comparable<Object> {
private static Collator sCollator = Collator.getInstance();
private String mLabel;
public final Locale mLocale;
public Loc(String label, Locale locale) {
public LocaleEntry(String label, Locale locale) {
this.mLabel = label;
this.mLocale = locale;
}
public void setLabel(String label) {
this.mLabel = label;
}
@Override
public String toString() {
return this.mLabel;
@ -73,7 +71,7 @@ public class InputLanguageSelection extends PreferenceActivity {
@Override
public int compareTo(Object o) {
return sCollator.compare(this.mLabel, ((Loc) o).mLabel);
return sCollator.compare(this.mLabel, ((LocaleEntry) o).mLabel);
}
}
@ -85,21 +83,58 @@ public class InputLanguageSelection extends PreferenceActivity {
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
mSelectedLanguages = mPrefs.getString(Settings.PREF_SELECTED_LANGUAGES, "");
String[] languageList = mSelectedLanguages.split(",");
ArrayList<Loc> availableLanguages = getUniqueLocales();
ArrayList<LocaleEntry> availableLanguages = getUniqueLocales();
PreferenceGroup parent = getPreferenceScreen();
final HashMap<Long, LocaleEntry> dictionaryIdLocaleMap = new HashMap<Long, LocaleEntry>();
final TreeMap<LocaleEntry, Boolean> localeHasDictionaryMap =
new TreeMap<LocaleEntry, Boolean>();
for (int i = 0; i < availableLanguages.size(); i++) {
Locale locale = availableLanguages.get(i).mLocale;
final Pair<Boolean, Boolean> hasDictionaryOrLayout = hasDictionaryOrLayout(locale);
final boolean hasDictionary = hasDictionaryOrLayout.first;
LocaleEntry loc = availableLanguages.get(i);
Locale locale = loc.mLocale;
final Pair<Long, Boolean> hasDictionaryOrLayout = hasDictionaryOrLayout(locale);
final Long dictionaryId = hasDictionaryOrLayout.first;
final boolean hasLayout = hasDictionaryOrLayout.second;
final boolean hasDictionary = dictionaryId != null;
// Add this locale to the supported list if:
// 1) this locale has a layout/ 2) this locale has a dictionary and the length
// of the locale is equal to or larger than 5.
if (!hasLayout && !(hasDictionary && locale.toString().length() >= 5)) {
// 1) this locale has a layout/ 2) this locale has a dictionary
// If some locales have no layout but have a same dictionary, the shortest locale
// will be added to the supported list.
if (!hasLayout && !hasDictionary) {
continue;
}
if (hasLayout) {
localeHasDictionaryMap.put(loc, hasDictionary);
}
if (!hasDictionary) {
continue;
}
if (dictionaryIdLocaleMap.containsKey(dictionaryId)) {
final String newLocale = locale.toString();
final String oldLocale =
dictionaryIdLocaleMap.get(dictionaryId).mLocale.toString();
// Check if this locale is more appropriate to be the candidate of the input locale.
if (oldLocale.length() <= newLocale.length() && !hasLayout) {
// Don't add this new locale to the map<dictionary id, locale> if:
// 1) the new locale's name is longer than the existing one, and
// 2) the new locale doesn't have its layout
continue;
}
}
dictionaryIdLocaleMap.put(dictionaryId, loc);
}
for (LocaleEntry localeEntry : dictionaryIdLocaleMap.values()) {
if (!localeHasDictionaryMap.containsKey(localeEntry)) {
localeHasDictionaryMap.put(localeEntry, true);
}
}
for (Entry<LocaleEntry, Boolean> entry : localeHasDictionaryMap.entrySet()) {
final LocaleEntry localeEntry = entry.getKey();
final Locale locale = localeEntry.mLocale;
final Boolean hasDictionary = entry.getValue();
CheckBoxPreference pref = new CheckBoxPreference(this);
pref.setTitle(SubtypeSwitcher.getFullDisplayName(locale, true));
pref.setTitle(localeEntry.mLabel);
boolean checked = isLocaleIn(locale, languageList);
pref.setChecked(checked);
if (hasDictionary) {
@ -118,11 +153,11 @@ public class InputLanguageSelection extends PreferenceActivity {
return false;
}
private Pair<Boolean, Boolean> hasDictionaryOrLayout(Locale locale) {
if (locale == null) return new Pair<Boolean, Boolean>(false, false);
private Pair<Long, Boolean> hasDictionaryOrLayout(Locale locale) {
if (locale == null) return new Pair<Long, Boolean>(null, false);
final Resources res = getResources();
final Locale saveLocale = Utils.setSystemLocale(res, locale);
final boolean hasDictionary = DictionaryFactory.isDictionaryAvailable(this, locale);
final Long dictionaryId = DictionaryFactory.getDictionaryId(this, locale);
boolean hasLayout = false;
try {
@ -141,7 +176,7 @@ public class InputLanguageSelection extends PreferenceActivity {
} catch (IOException e) {
}
Utils.setSystemLocale(res, saveLocale);
return new Pair<Boolean, Boolean>(hasDictionary, hasLayout);
return new Pair<Long, Boolean>(dictionaryId, hasLayout);
}
private String get5Code(Locale locale) {
@ -174,13 +209,13 @@ public class InputLanguageSelection extends PreferenceActivity {
SharedPreferencesCompat.apply(editor);
}
public ArrayList<Loc> getUniqueLocales() {
public ArrayList<LocaleEntry> getUniqueLocales() {
String[] locales = getAssets().getLocales();
Arrays.sort(locales);
ArrayList<Loc> uniqueLocales = new ArrayList<Loc>();
ArrayList<LocaleEntry> uniqueLocales = new ArrayList<LocaleEntry>();
final int origSize = locales.length;
Loc[] preprocess = new Loc[origSize];
LocaleEntry[] preprocess = new LocaleEntry[origSize];
int finalSize = 0;
for (int i = 0 ; i < origSize; i++ ) {
String s = locales[i];
@ -202,26 +237,13 @@ public class InputLanguageSelection extends PreferenceActivity {
if (finalSize == 0) {
preprocess[finalSize++] =
new Loc(SubtypeSwitcher.getFullDisplayName(l, true), l);
new LocaleEntry(SubtypeSwitcher.getFullDisplayName(l, false), l);
} else {
// check previous entry:
// same lang and a country -> upgrade to full name and
// insert ours with full name
// diff lang -> insert ours with lang-only name
if (preprocess[finalSize-1].mLocale.getLanguage().equals(
language)) {
preprocess[finalSize-1].setLabel(SubtypeSwitcher.getFullDisplayName(
preprocess[finalSize-1].mLocale, false));
preprocess[finalSize++] =
new Loc(SubtypeSwitcher.getFullDisplayName(l, false), l);
if (s.equals("zz_ZZ")) {
// ignore this locale
} else {
String displayName;
if (s.equals("zz_ZZ")) {
// ignore this locale
} else {
displayName = SubtypeSwitcher.getFullDisplayName(l, true);
preprocess[finalSize++] = new Loc(displayName, l);
}
final String displayName = SubtypeSwitcher.getFullDisplayName(l, false);
preprocess[finalSize++] = new LocaleEntry(displayName, l);
}
}
}

View File

@ -14,11 +14,21 @@
* the License.
*/
package com.android.inputmethod.latin;
package com.android.inputmethod.deprecated.recorrection;
import com.android.inputmethod.compat.InputConnectionCompatUtils;
import com.android.inputmethod.deprecated.VoiceProxy;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.latin.AutoCorrection;
import com.android.inputmethod.latin.CandidateView;
import com.android.inputmethod.latin.EditingUtils;
import com.android.inputmethod.latin.LatinIME;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.Settings;
import com.android.inputmethod.latin.Suggest;
import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.TextEntryState;
import com.android.inputmethod.latin.WordComposer;
import android.content.SharedPreferences;
import android.content.res.Resources;
@ -37,7 +47,8 @@ public class Recorrection {
private LatinIME mService;
private boolean mRecorrectionEnabled = false;
private final ArrayList<WordAlternatives> mWordHistory = new ArrayList<WordAlternatives>();
private final ArrayList<RecorrectionSuggestionEntries> mRecorrectionSuggestionsList =
new ArrayList<RecorrectionSuggestionEntries>();
public static Recorrection getInstance() {
return sInstance;
@ -98,42 +109,42 @@ public class Recorrection {
}
public void updateRecorrectionSelection(KeyboardSwitcher keyboardSwitcher,
CandidateView candidateView, int candidatesStart, int candidatesEnd, int newSelStart,
int newSelEnd, int oldSelStart, int lastSelectionStart,
CandidateView candidateView, int candidatesStart, int candidatesEnd,
int newSelStart, int newSelEnd, int oldSelStart, int lastSelectionStart,
int lastSelectionEnd, boolean hasUncommittedTypedChars) {
if (mRecorrectionEnabled && mService.isShowingSuggestionsStrip()) {
// Don't look for corrections if the keyboard is not visible
if (keyboardSwitcher.isInputViewShown()) {
// Check if we should go in or out of correction mode.
if (mService.isSuggestionsRequested()
&& (candidatesStart == candidatesEnd || newSelStart != oldSelStart
|| TextEntryState.isRecorrecting())
&& (newSelStart < newSelEnd - 1 || !hasUncommittedTypedChars)) {
if (mService.isCursorTouchingWord() || lastSelectionStart < lastSelectionEnd) {
mService.mHandler.cancelUpdateBigramPredictions();
mService.mHandler.postUpdateOldSuggestions();
} else {
abortRecorrection(false);
// If showing the "touch again to save" hint, do not replace it. Else,
// show the bigrams if we are at the end of the text, punctuation otherwise.
if (candidateView != null
&& !candidateView.isShowingAddToDictionaryHint()) {
InputConnection ic = mService.getCurrentInputConnection();
if (null == ic || !TextUtils.isEmpty(ic.getTextAfterCursor(1, 0))) {
if (!mService.isShowingPunctuationList()) {
mService.setPunctuationSuggestions();
}
} else {
mService.mHandler.postUpdateBigramPredictions();
}
if (!mRecorrectionEnabled) return;
if (!mService.isShowingSuggestionsStrip()) return;
if (!keyboardSwitcher.isInputViewShown()) return;
if (!mService.isSuggestionsRequested()) return;
// Don't look for corrections if the keyboard is not visible
// Check if we should go in or out of correction mode.
if ((candidatesStart == candidatesEnd || newSelStart != oldSelStart || TextEntryState
.isRecorrecting())
&& (newSelStart < newSelEnd - 1 || !hasUncommittedTypedChars)) {
if (mService.isCursorTouchingWord() || lastSelectionStart < lastSelectionEnd) {
mService.mHandler.cancelUpdateBigramPredictions();
mService.mHandler.postUpdateOldSuggestions();
} else {
abortRecorrection(false);
// If showing the "touch again to save" hint, do not replace it. Else,
// show the bigrams if we are at the end of the text, punctuation
// otherwise.
if (candidateView != null && !candidateView.isShowingAddToDictionaryHint()) {
InputConnection ic = mService.getCurrentInputConnection();
if (null == ic || !TextUtils.isEmpty(ic.getTextAfterCursor(1, 0))) {
if (!mService.isShowingPunctuationList()) {
mService.setPunctuationSuggestions();
}
} else {
mService.mHandler.postUpdateBigramPredictions();
}
}
}
}
}
public void saveWordInHistory(WordComposer word, CharSequence result) {
public void saveRecorrectionSuggestion(WordComposer word, CharSequence result) {
if (!mRecorrectionEnabled) return;
if (word.size() <= 1) {
return;
}
@ -144,12 +155,13 @@ public class Recorrection {
// Make a copy of the CharSequence, since it is/could be a mutable CharSequence
final String resultCopy = result.toString();
WordAlternatives entry = new WordAlternatives(resultCopy, new WordComposer(word));
mWordHistory.add(entry);
RecorrectionSuggestionEntries entry = new RecorrectionSuggestionEntries(
resultCopy, new WordComposer(word));
mRecorrectionSuggestionsList.add(entry);
}
public void clearWordsInHistory() {
mWordHistory.clear();
mRecorrectionSuggestionsList.clear();
}
/**
@ -162,9 +174,9 @@ public class Recorrection {
KeyboardSwitcher keyboardSwitcher, EditingUtils.SelectedWord touching) {
// If we didn't find a match, search for result in typed word history
WordComposer foundWord = null;
WordAlternatives alternatives = null;
RecorrectionSuggestionEntries alternatives = null;
// Search old suggestions to suggest re-corrected suggestions.
for (WordAlternatives entry : mWordHistory) {
for (RecorrectionSuggestionEntries entry : mRecorrectionSuggestionsList) {
if (TextUtils.equals(entry.getChosenWord(), touching.mWord)) {
foundWord = entry.mWordComposer;
alternatives = entry;
@ -186,7 +198,7 @@ public class Recorrection {
// Found a match, show suggestions
if (foundWord != null || alternatives != null) {
if (alternatives == null) {
alternatives = new WordAlternatives(touching.mWord, foundWord);
alternatives = new RecorrectionSuggestionEntries(touching.mWord, foundWord);
}
showRecorrections(suggest, keyboardSwitcher, alternatives);
if (foundWord != null) {
@ -201,10 +213,10 @@ public class Recorrection {
private void showRecorrections(Suggest suggest, KeyboardSwitcher keyboardSwitcher,
WordAlternatives alternatives) {
SuggestedWords.Builder builder = alternatives.getAlternatives(suggest, keyboardSwitcher);
RecorrectionSuggestionEntries entries) {
SuggestedWords.Builder builder = entries.getAlternatives(suggest, keyboardSwitcher);
builder.setTypedWordValid(false).setHasMinimalSuggestion(false);
mService.showSuggestions(builder.build(), alternatives.getOriginalWord());
mService.showSuggestions(builder.build(), entries.getOriginalWord());
}
public void setRecorrectionSuggestions(VoiceProxy voiceProxy, CandidateView candidateView,

View File

@ -14,17 +14,20 @@
* the License.
*/
package com.android.inputmethod.latin;
package com.android.inputmethod.deprecated.recorrection;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.latin.Suggest;
import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.WordComposer;
import android.text.TextUtils;
public class WordAlternatives {
public class RecorrectionSuggestionEntries {
public final CharSequence mChosenWord;
public final WordComposer mWordComposer;
public WordAlternatives(CharSequence chosenWord, WordComposer wordComposer) {
public RecorrectionSuggestionEntries(CharSequence chosenWord, WordComposer wordComposer) {
mChosenWord = chosenWord;
mWordComposer = wordComposer;
}
@ -56,4 +59,4 @@ public class WordAlternatives {
Suggest suggest, KeyboardSwitcher keyboardSwitcher, WordComposer word) {
return suggest.getSuggestedWordBuilder(keyboardSwitcher.getInputView(), word, null);
}
}
}

View File

@ -142,6 +142,25 @@ public class DictionaryFactory {
return hasDictionary;
}
// TODO: Do not use the size of the dictionary as an unique dictionary ID.
public static Long getDictionaryId(Context context, Locale locale) {
final Resources res = context.getResources();
final Locale saveLocale = Utils.setSystemLocale(res, locale);
final int resourceId = Utils.getMainDictionaryResourceId(res);
final AssetFileDescriptor afd = res.openRawResourceFd(resourceId);
final Long size = (afd != null && afd.getLength() > PLACEHOLDER_LENGTH)
? afd.getLength()
: null;
try {
if (null != afd) afd.close();
} catch (java.io.IOException e) {
}
Utils.setSystemLocale(res, saveLocale);
return size;
}
// TODO: Find the Right Way to find out whether the resource is a placeholder or not.
// Suggestion : strip the locale, open the placeholder file and store its offset.
// Upon opening the file, if it's the same offset, then it's the placeholder.

View File

@ -25,6 +25,7 @@ import com.android.inputmethod.compat.InputTypeCompatUtils;
import com.android.inputmethod.compat.VibratorCompatWrapper;
import com.android.inputmethod.deprecated.LanguageSwitcherProxy;
import com.android.inputmethod.deprecated.VoiceProxy;
import com.android.inputmethod.deprecated.recorrection.Recorrection;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardActionListener;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
@ -717,7 +718,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// If the composing span has been cleared, save the typed word in the history for
// recorrection before we reset the candidate strip. Then, we'll be able to show
// suggestions for recorrection right away.
mRecorrection.saveWordInHistory(mWord, mComposing);
mRecorrection.saveRecorrectionSuggestion(mWord, mComposing);
}
mComposing.setLength(0);
mHasUncommittedTypedChars = false;
@ -1254,7 +1255,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
if (!mHasUncommittedTypedChars) {
mHasUncommittedTypedChars = true;
mComposing.setLength(0);
mRecorrection.saveWordInHistory(mWord, mBestWord);
mRecorrection.saveRecorrectionSuggestion(mWord, mBestWord);
mWord.reset();
clearSuggestions();
}
@ -1662,7 +1663,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
mVoiceProxy.rememberReplacedWord(suggestion, mWordSeparators);
ic.commitText(suggestion, 1);
}
mRecorrection.saveWordInHistory(mWord, suggestion);
mRecorrection.saveRecorrectionSuggestion(mWord, suggestion);
mHasUncommittedTypedChars = false;
mCommittedLength = suggestion.length();
}

View File

@ -62,7 +62,7 @@ public class WordComposer {
mYCoordinates = new int[N];
}
WordComposer(WordComposer source) {
public WordComposer(WordComposer source) {
init(source);
}