mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-06-19 15:40:52 +00:00
simplify the way of setting suggestions
remove the excursion to latinIME have Suggest return suggestions instead of calling a callback now the relevant code can be mostly found in inputLogic (except for handling batch input)
This commit is contained in:
parent
0787a79de4
commit
867438fdc0
4 changed files with 63 additions and 133 deletions
|
@ -60,7 +60,6 @@ import helium314.keyboard.keyboard.KeyboardId;
|
|||
import helium314.keyboard.keyboard.KeyboardLayoutSet;
|
||||
import helium314.keyboard.keyboard.KeyboardSwitcher;
|
||||
import helium314.keyboard.keyboard.MainKeyboardView;
|
||||
import helium314.keyboard.latin.Suggest.OnGetSuggestedWordsCallback;
|
||||
import helium314.keyboard.latin.SuggestedWords.SuggestedWordInfo;
|
||||
import helium314.keyboard.latin.common.ColorType;
|
||||
import helium314.keyboard.latin.common.Constants;
|
||||
|
@ -129,6 +128,9 @@ public class LatinIME extends InputMethodService implements
|
|||
public final KeyboardActionListener mKeyboardActionListener;
|
||||
private int mOriginalNavBarColor = 0;
|
||||
private int mOriginalNavBarFlags = 0;
|
||||
|
||||
// UIHandler is needed when creating InputLogic
|
||||
public final UIHandler mHandler = new UIHandler(this);
|
||||
private final DictionaryFacilitator mDictionaryFacilitator =
|
||||
DictionaryFacilitatorProvider.getDictionaryFacilitator(false);
|
||||
final InputLogic mInputLogic = new InputLogic(this, this, mDictionaryFacilitator);
|
||||
|
@ -187,8 +189,6 @@ public class LatinIME extends InputMethodService implements
|
|||
|
||||
private final ClipboardHistoryManager mClipboardHistoryManager = new ClipboardHistoryManager(this);
|
||||
|
||||
public final UIHandler mHandler = new UIHandler(this);
|
||||
|
||||
public static final class UIHandler extends LeakGuardHandlerWrapper<LatinIME> {
|
||||
private static final int MSG_UPDATE_SHIFT_STATE = 0;
|
||||
private static final int MSG_PENDING_IMS_CALLBACK = 1;
|
||||
|
@ -796,14 +796,6 @@ public class LatinIME extends InputMethodService implements
|
|||
deallocateMemory();
|
||||
}
|
||||
|
||||
public void recycle() {
|
||||
unregisterReceiver(mDictionaryPackInstallReceiver);
|
||||
unregisterReceiver(mDictionaryDumpBroadcastReceiver);
|
||||
unregisterReceiver(mRingerModeChangeReceiver);
|
||||
unregisterReceiver(mRestartAfterDeviceUnlockReceiver);
|
||||
mInputLogic.recycle();
|
||||
}
|
||||
|
||||
private boolean isImeSuppressedByHardwareKeyboard() {
|
||||
final KeyboardSwitcher switcher = KeyboardSwitcher.getInstance();
|
||||
return !onEvaluateInputViewShown() && switcher.isImeSuppressedByHardwareKeyboard(
|
||||
|
@ -1683,18 +1675,6 @@ public class LatinIME extends InputMethodService implements
|
|||
}
|
||||
}
|
||||
|
||||
// TODO[IL]: Move this out of LatinIME.
|
||||
public void getSuggestedWords(final int inputStyle, final int sequenceNumber,
|
||||
final OnGetSuggestedWordsCallback callback) {
|
||||
final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
|
||||
if (keyboard == null) {
|
||||
callback.onGetSuggestedWords(SuggestedWords.getEmptyInstance());
|
||||
return;
|
||||
}
|
||||
mInputLogic.getSuggestedWords(mSettings.getCurrent(), keyboard,
|
||||
mKeyboardSwitcher.getKeyboardShiftMode(), inputStyle, sequenceNumber, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showSuggestionStrip(final SuggestedWords suggestedWords) {
|
||||
if (suggestedWords.isEmpty()) {
|
||||
|
|
|
@ -45,19 +45,20 @@ class Suggest(private val mDictionaryFacilitator: DictionaryFacilitator) {
|
|||
mAutoCorrectionThreshold = threshold
|
||||
}
|
||||
|
||||
// todo: remove when InputLogic is ready
|
||||
interface OnGetSuggestedWordsCallback {
|
||||
fun onGetSuggestedWords(suggestedWords: SuggestedWords?)
|
||||
}
|
||||
|
||||
fun getSuggestedWords(wordComposer: WordComposer, ngramContext: NgramContext, keyboard: Keyboard,
|
||||
settingsValuesForSuggestion: SettingsValuesForSuggestion, isCorrectionEnabled: Boolean,
|
||||
inputStyle: Int, sequenceNumber: Int, callback: OnGetSuggestedWordsCallback) {
|
||||
if (wordComposer.isBatchMode) {
|
||||
inputStyle: Int, sequenceNumber: Int): SuggestedWords {
|
||||
return if (wordComposer.isBatchMode) {
|
||||
getSuggestedWordsForBatchInput(wordComposer, ngramContext, keyboard, settingsValuesForSuggestion,
|
||||
inputStyle, sequenceNumber, callback)
|
||||
inputStyle, sequenceNumber)
|
||||
} else {
|
||||
getSuggestedWordsForNonBatchInput(wordComposer, ngramContext, keyboard, settingsValuesForSuggestion,
|
||||
inputStyle, isCorrectionEnabled, sequenceNumber, callback)
|
||||
inputStyle, isCorrectionEnabled, sequenceNumber)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,7 +66,7 @@ class Suggest(private val mDictionaryFacilitator: DictionaryFacilitator) {
|
|||
// and calls the callback function with the suggestions.
|
||||
private fun getSuggestedWordsForNonBatchInput(wordComposer: WordComposer, ngramContext: NgramContext, keyboard: Keyboard,
|
||||
settingsValuesForSuggestion: SettingsValuesForSuggestion, inputStyleIfNotPrediction: Int,
|
||||
isCorrectionEnabled: Boolean, sequenceNumber: Int, callback: OnGetSuggestedWordsCallback) {
|
||||
isCorrectionEnabled: Boolean, sequenceNumber: Int): SuggestedWords {
|
||||
val typedWordString = wordComposer.typedWord
|
||||
val resultsArePredictions = !wordComposer.isComposingWord
|
||||
val suggestionResults = if (typedWordString.isEmpty())
|
||||
|
@ -131,8 +132,8 @@ class Suggest(private val mDictionaryFacilitator: DictionaryFacilitator) {
|
|||
}
|
||||
}
|
||||
val isTypedWordValid = firstOccurrenceOfTypedWordInSuggestions > -1 || (!resultsArePredictions && !allowsToBeAutoCorrected)
|
||||
callback.onGetSuggestedWords(SuggestedWords(suggestionsList, suggestionResults.mRawSuggestions,
|
||||
typedWordInfo, isTypedWordValid, hasAutoCorrection, false, inputStyle, sequenceNumber))
|
||||
return SuggestedWords(suggestionsList, suggestionResults.mRawSuggestions,
|
||||
typedWordInfo, isTypedWordValid, hasAutoCorrection, false, inputStyle, sequenceNumber)
|
||||
}
|
||||
|
||||
// returns [allowsToBeAutoCorrected, hasAutoCorrection]
|
||||
|
@ -252,9 +253,8 @@ class Suggest(private val mDictionaryFacilitator: DictionaryFacilitator) {
|
|||
wordComposer: WordComposer,
|
||||
ngramContext: NgramContext, keyboard: Keyboard,
|
||||
settingsValuesForSuggestion: SettingsValuesForSuggestion,
|
||||
inputStyle: Int, sequenceNumber: Int,
|
||||
callback: OnGetSuggestedWordsCallback
|
||||
) {
|
||||
inputStyle: Int, sequenceNumber: Int
|
||||
): SuggestedWords {
|
||||
val suggestionResults = mDictionaryFacilitator.getSuggestionResults(
|
||||
wordComposer.composedDataSnapshot, ngramContext, keyboard,
|
||||
settingsValuesForSuggestion, SESSION_ID_GESTURE, inputStyle
|
||||
|
@ -312,10 +312,8 @@ class Suggest(private val mDictionaryFacilitator: DictionaryFacilitator) {
|
|||
} else {
|
||||
suggestionsContainer
|
||||
}
|
||||
callback.onGetSuggestedWords(
|
||||
SuggestedWords(suggestionsList, suggestionResults.mRawSuggestions, pseudoTypedWordInfo, true,
|
||||
return SuggestedWords(suggestionsList, suggestionResults.mRawSuggestions, pseudoTypedWordInfo, true,
|
||||
false, false, inputStyle, sequenceNumber)
|
||||
)
|
||||
}
|
||||
|
||||
/** reduces score of the first suggestion if next one is close and has more than a single letter */
|
||||
|
|
|
@ -44,6 +44,7 @@ import helium314.keyboard.latin.common.StringUtils;
|
|||
import helium314.keyboard.latin.common.StringUtilsKt;
|
||||
import helium314.keyboard.latin.common.SuggestionSpanUtilsKt;
|
||||
import helium314.keyboard.latin.define.DebugFlags;
|
||||
import helium314.keyboard.latin.settings.Settings;
|
||||
import helium314.keyboard.latin.settings.SettingsValues;
|
||||
import helium314.keyboard.latin.settings.SpacingAndPunctuations;
|
||||
import helium314.keyboard.latin.suggestions.SuggestionStripViewAccessor;
|
||||
|
@ -71,7 +72,7 @@ public final class InputLogic {
|
|||
final LatinIME mLatinIME;
|
||||
private final SuggestionStripViewAccessor mSuggestionStripViewAccessor;
|
||||
|
||||
@NonNull private InputLogicHandler mInputLogicHandler;
|
||||
@NonNull private final InputLogicHandler mInputLogicHandler;
|
||||
|
||||
// TODO : make all these fields private as soon as possible.
|
||||
// Current space state of the input method. This can be any of the above constants.
|
||||
|
@ -119,7 +120,7 @@ public final class InputLogic {
|
|||
mSuggestionStripViewAccessor = suggestionStripViewAccessor;
|
||||
mWordComposer = new WordComposer();
|
||||
mConnection = new RichInputConnection(latinIME);
|
||||
mInputLogicHandler = InputLogicHandler.NULL_HANDLER;
|
||||
mInputLogicHandler = new InputLogicHandler(mLatinIME.mHandler, this);
|
||||
mSuggest = new Suggest(dictionaryFacilitator);
|
||||
mDictionaryFacilitator = dictionaryFacilitator;
|
||||
}
|
||||
|
@ -153,11 +154,7 @@ public final class InputLogic {
|
|||
// editorInfo.initialSelStart is not the actual cursor position, so we try using some heuristics to find the correct position.
|
||||
mConnection.tryFixIncorrectCursorPosition();
|
||||
cancelDoubleSpacePeriodCountdown();
|
||||
if (InputLogicHandler.NULL_HANDLER == mInputLogicHandler) {
|
||||
mInputLogicHandler = new InputLogicHandler(mLatinIME, this);
|
||||
} else {
|
||||
mInputLogicHandler.reset();
|
||||
}
|
||||
mConnection.requestCursorUpdates(true, true);
|
||||
}
|
||||
|
||||
|
@ -200,17 +197,6 @@ public final class InputLogic {
|
|||
mInputLogicHandler.reset();
|
||||
}
|
||||
|
||||
// Normally this class just gets out of scope after the process ends, but in unit tests, we
|
||||
// create several instances of LatinIME in the same process, which results in several
|
||||
// instances of InputLogic. This cleans up the associated handler so that tests don't leak
|
||||
// handlers.
|
||||
public void recycle() {
|
||||
final InputLogicHandler inputLogicHandler = mInputLogicHandler;
|
||||
mInputLogicHandler = InputLogicHandler.NULL_HANDLER;
|
||||
inputLogicHandler.destroy();
|
||||
mDictionaryFacilitator.closeDictionaries();
|
||||
}
|
||||
|
||||
/**
|
||||
* React to a string input.
|
||||
* <p>
|
||||
|
@ -1680,15 +1666,14 @@ public final class InputLogic {
|
|||
}
|
||||
|
||||
final AsyncResultHolder<SuggestedWords> holder = new AsyncResultHolder<>("Suggest");
|
||||
mInputLogicHandler.getSuggestedWords(inputStyle, SuggestedWords.NOT_A_SEQUENCE_NUMBER,
|
||||
mInputLogicHandler.getSuggestedWords(() -> getSuggestedWords(
|
||||
inputStyle, SuggestedWords.NOT_A_SEQUENCE_NUMBER,
|
||||
suggestedWords -> {
|
||||
final String typedWordString = mWordComposer.getTypedWord();
|
||||
final SuggestedWordInfo typedWordInfo = new SuggestedWordInfo(
|
||||
typedWordString, "" /* prevWordsContext */,
|
||||
SuggestedWordInfo.MAX_SCORE,
|
||||
SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED,
|
||||
SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
|
||||
SuggestedWordInfo.NOT_A_CONFIDENCE);
|
||||
typedWordString, "", SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_TYPED,
|
||||
Dictionary.DICTIONARY_USER_TYPED, SuggestedWordInfo.NOT_AN_INDEX, SuggestedWordInfo.NOT_A_CONFIDENCE
|
||||
);
|
||||
// Show new suggestions if we have at least one. Otherwise keep the old
|
||||
// suggestions with the new typed word. Exception: if the length of the
|
||||
// typed word is <= 1 (after a deletion typically) we clear old suggestions.
|
||||
|
@ -1698,8 +1683,7 @@ public final class InputLogic {
|
|||
holder.set(retrieveOlderSuggestions(typedWordInfo, mSuggestedWords));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
));
|
||||
// This line may cause the current thread to wait.
|
||||
final SuggestedWords suggestedWords = holder.get(null,
|
||||
Constants.GET_SUGGESTED_WORDS_TIMEOUT);
|
||||
|
@ -1809,8 +1793,8 @@ public final class InputLogic {
|
|||
// If there weren't any suggestion spans on this word, suggestions#size() will be 1
|
||||
// if shouldIncludeResumedWordInSuggestions is true, 0 otherwise. In this case, we
|
||||
// have no useful suggestions, so we will try to compute some for it instead.
|
||||
mInputLogicHandler.getSuggestedWords(Suggest.SESSION_ID_TYPING,
|
||||
SuggestedWords.NOT_A_SEQUENCE_NUMBER, this::doShowSuggestionsAndClearAutoCorrectionIndicator);
|
||||
mInputLogicHandler.getSuggestedWords(() -> getSuggestedWords(Suggest.SESSION_ID_TYPING,
|
||||
SuggestedWords.NOT_A_SEQUENCE_NUMBER, this::doShowSuggestionsAndClearAutoCorrectionIndicator));
|
||||
} else {
|
||||
// We found suggestion spans in the word. We'll create the SuggestedWords out of
|
||||
// them, and make willAutoCorrect false. We make typedWordValid false, because the
|
||||
|
@ -2434,12 +2418,17 @@ public final class InputLogic {
|
|||
return true;
|
||||
}
|
||||
|
||||
public void getSuggestedWords(final SettingsValues settingsValues,
|
||||
final Keyboard keyboard, final int keyboardShiftMode, final int inputStyle,
|
||||
final int sequenceNumber, final OnGetSuggestedWordsCallback callback) {
|
||||
// we used to provide keyboard, settingsValues and keyboardShiftMode, but every time read it from current instance anyway
|
||||
public void getSuggestedWords(final int inputStyle, final int sequenceNumber, final OnGetSuggestedWordsCallback callback) {
|
||||
final Keyboard keyboard = KeyboardSwitcher.getInstance().getKeyboard();
|
||||
if (keyboard == null) {
|
||||
callback.onGetSuggestedWords(SuggestedWords.getEmptyInstance());
|
||||
return;
|
||||
}
|
||||
final SettingsValues settingsValues = Settings.getValues();
|
||||
mWordComposer.adviseCapitalizedModeBeforeFetchingSuggestions(
|
||||
getActualCapsMode(settingsValues, keyboardShiftMode));
|
||||
mSuggest.getSuggestedWords(mWordComposer,
|
||||
getActualCapsMode(settingsValues, KeyboardSwitcher.getInstance().getKeyboardShiftMode()));
|
||||
final SuggestedWords suggestedWords = mSuggest.getSuggestedWords(mWordComposer,
|
||||
getNgramContextFromNthPreviousWordForSuggestion(
|
||||
settingsValues.mSpacingAndPunctuations,
|
||||
// Get the word on which we should search the bigrams. If we are composing
|
||||
|
@ -2449,7 +2438,8 @@ public final class InputLogic {
|
|||
keyboard,
|
||||
settingsValues.mSettingsValuesForSuggestion,
|
||||
settingsValues.mAutoCorrectEnabled,
|
||||
inputStyle, sequenceNumber, callback);
|
||||
inputStyle, sequenceNumber);
|
||||
callback.onGetSuggestedWords(suggestedWords);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -11,7 +11,6 @@ import android.os.HandlerThread;
|
|||
import android.os.Message;
|
||||
|
||||
import helium314.keyboard.latin.LatinIME;
|
||||
import helium314.keyboard.latin.Suggest.OnGetSuggestedWordsCallback;
|
||||
import helium314.keyboard.latin.SuggestedWords;
|
||||
import helium314.keyboard.latin.common.InputPointers;
|
||||
|
||||
|
@ -20,48 +19,19 @@ import helium314.keyboard.latin.common.InputPointers;
|
|||
*/
|
||||
class InputLogicHandler implements Handler.Callback {
|
||||
final Handler mNonUIThreadHandler;
|
||||
// TODO: remove this reference.
|
||||
final LatinIME mLatinIME;
|
||||
final LatinIME.UIHandler mLatinIMEHandler;
|
||||
final InputLogic mInputLogic;
|
||||
private final Object mLock = new Object();
|
||||
private boolean mInBatchInput; // synchronized using {@link #mLock}.
|
||||
|
||||
private static final int MSG_GET_SUGGESTED_WORDS = 1;
|
||||
|
||||
// A handler that never does anything. This is used for cases where events come before anything
|
||||
// is initialized, though probably only the monkey can actually do this.
|
||||
public static final InputLogicHandler NULL_HANDLER = new InputLogicHandler() {
|
||||
@Override
|
||||
public void reset() {}
|
||||
@Override
|
||||
public boolean handleMessage(final Message msg) { return true; }
|
||||
@Override
|
||||
public void onStartBatchInput() {}
|
||||
@Override
|
||||
public void onUpdateBatchInput(final InputPointers batchPointers,
|
||||
final int sequenceNumber) {}
|
||||
@Override
|
||||
public void onCancelBatchInput() {}
|
||||
@Override
|
||||
public void updateTailBatchInput(final InputPointers batchPointers,
|
||||
final int sequenceNumber) {}
|
||||
@Override
|
||||
public void getSuggestedWords(final int sessionId, final int sequenceNumber,
|
||||
final OnGetSuggestedWordsCallback callback) {}
|
||||
};
|
||||
|
||||
InputLogicHandler() {
|
||||
mNonUIThreadHandler = null;
|
||||
mLatinIME = null;
|
||||
mInputLogic = null;
|
||||
}
|
||||
|
||||
public InputLogicHandler(final LatinIME latinIME, final InputLogic inputLogic) {
|
||||
public InputLogicHandler(final LatinIME.UIHandler latinIMEHandler, final InputLogic inputLogic) {
|
||||
final HandlerThread handlerThread = new HandlerThread(
|
||||
InputLogicHandler.class.getSimpleName());
|
||||
handlerThread.start();
|
||||
mNonUIThreadHandler = new Handler(handlerThread.getLooper(), this);
|
||||
mLatinIME = latinIME;
|
||||
mLatinIMEHandler = latinIMEHandler;
|
||||
mInputLogic = inputLogic;
|
||||
}
|
||||
|
||||
|
@ -69,12 +39,6 @@ class InputLogicHandler implements Handler.Callback {
|
|||
mNonUIThreadHandler.removeCallbacksAndMessages(null);
|
||||
}
|
||||
|
||||
// In unit tests, we create several instances of LatinIME, which results in several instances
|
||||
// of InputLogicHandler. To avoid these handlers lingering, we call this.
|
||||
public void destroy() {
|
||||
mNonUIThreadHandler.getLooper().quitSafely();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a message.
|
||||
* @see android.os.Handler.Callback#handleMessage(android.os.Message)
|
||||
|
@ -83,7 +47,7 @@ class InputLogicHandler implements Handler.Callback {
|
|||
@Override
|
||||
public boolean handleMessage(final Message msg) {
|
||||
if (msg.what == MSG_GET_SUGGESTED_WORDS)
|
||||
mLatinIME.getSuggestedWords(msg.arg1, msg.arg2, (OnGetSuggestedWordsCallback) msg.obj);
|
||||
((Runnable)msg.obj).run();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -118,13 +82,14 @@ class InputLogicHandler implements Handler.Callback {
|
|||
return;
|
||||
}
|
||||
mInputLogic.mWordComposer.setBatchInputPointers(batchPointers);
|
||||
final OnGetSuggestedWordsCallback callback = suggestedWords -> showGestureSuggestionsWithPreviewVisuals(suggestedWords, isTailBatchInput);
|
||||
getSuggestedWords(isTailBatchInput ? SuggestedWords.INPUT_STYLE_TAIL_BATCH
|
||||
: SuggestedWords.INPUT_STYLE_UPDATE_BATCH, sequenceNumber, callback);
|
||||
getSuggestedWords(() -> mInputLogic.getSuggestedWords(
|
||||
isTailBatchInput ? SuggestedWords.INPUT_STYLE_TAIL_BATCH : SuggestedWords.INPUT_STYLE_UPDATE_BATCH, sequenceNumber,
|
||||
suggestedWords -> showGestureSuggestionsWithPreviewVisuals(suggestedWords, isTailBatchInput))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void showGestureSuggestionsWithPreviewVisuals(final SuggestedWords suggestedWordsForBatchInput,
|
||||
private void showGestureSuggestionsWithPreviewVisuals(final SuggestedWords suggestedWordsForBatchInput,
|
||||
final boolean isTailBatchInput) {
|
||||
final SuggestedWords suggestedWordsToShowSuggestions;
|
||||
// We're now inside the callback. This always runs on the Non-UI thread,
|
||||
|
@ -138,13 +103,12 @@ class InputLogicHandler implements Handler.Callback {
|
|||
} else {
|
||||
suggestedWordsToShowSuggestions = suggestedWordsForBatchInput;
|
||||
}
|
||||
mLatinIME.mHandler.showGesturePreviewAndSuggestionStrip(suggestedWordsToShowSuggestions,
|
||||
isTailBatchInput /* dismissGestureFloatingPreviewText */);
|
||||
mLatinIMEHandler.showGesturePreviewAndSuggestionStrip(suggestedWordsToShowSuggestions, isTailBatchInput);
|
||||
if (isTailBatchInput) {
|
||||
mInBatchInput = false;
|
||||
// The following call schedules onEndBatchInputInternal
|
||||
// to be called on the UI thread.
|
||||
mLatinIME.mHandler.showTailBatchInputResult(suggestedWordsToShowSuggestions);
|
||||
mLatinIMEHandler.showTailBatchInputResult(suggestedWordsToShowSuggestions);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,9 +157,7 @@ class InputLogicHandler implements Handler.Callback {
|
|||
updateBatchInput(batchPointers, sequenceNumber, true);
|
||||
}
|
||||
|
||||
public void getSuggestedWords(final int inputStyle, final int sequenceNumber,
|
||||
final OnGetSuggestedWordsCallback callback) {
|
||||
mNonUIThreadHandler.obtainMessage(
|
||||
MSG_GET_SUGGESTED_WORDS, inputStyle, sequenceNumber, callback).sendToTarget();
|
||||
public void getSuggestedWords(final Runnable callback) {
|
||||
mNonUIThreadHandler.obtainMessage(MSG_GET_SUGGESTED_WORDS, callback).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue