diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin/LatinIME.java')
-rw-r--r-- | java/src/com/android/inputmethod/latin/LatinIME.java | 288 |
1 files changed, 164 insertions, 124 deletions
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 743118eed..5d90e10a0 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -25,6 +25,9 @@ import android.app.AlertDialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.DialogInterface.OnDismissListener; +import android.content.DialogInterface.OnShowListener; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; @@ -171,10 +174,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public void onCreate() { final Resources res = getOwnerInstance().getResources(); - mDelayUpdateSuggestions = - res.getInteger(R.integer.config_delay_update_suggestions); - mDelayUpdateShiftState = - res.getInteger(R.integer.config_delay_update_shift_state); + mDelayUpdateSuggestions = res.getInteger(R.integer.config_delay_update_suggestions); + mDelayUpdateShiftState = res.getInteger(R.integer.config_delay_update_shift_state); mDoubleSpacePeriodTimeout = res.getInteger(R.integer.config_double_space_period_timeout); } @@ -339,12 +340,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void executePendingImsCallback(final LatinIME latinIme, final EditorInfo editorInfo, boolean restarting) { - if (mHasPendingFinishInputView) + if (mHasPendingFinishInputView) { latinIme.onFinishInputViewInternal(mHasPendingFinishInput); - if (mHasPendingFinishInput) + } + if (mHasPendingFinishInput) { latinIme.onFinishInputInternal(); - if (mHasPendingStartInput) + } + if (mHasPendingStartInput) { latinIme.onStartInputInternal(editorInfo, restarting); + } resetPendingImsCallback(); } @@ -530,18 +534,33 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private void refreshPersonalizationDictionarySession() { + final Suggest suggest = mInputLogic.mSuggest; + final boolean shouldKeepUserHistoryDictionaries; + final boolean shouldKeepPersonalizationDictionaries; if (mSettings.getCurrent().mUsePersonalizedDicts) { - if (mSubtypeSwitcher.isSystemLocaleSameAsLocaleOfAllEnabledSubtypes()) { - final DictionaryFacilitatorForSuggest dictionaryFacilitator = - (mInputLogic.mSuggest == null) ? - null : mInputLogic.mSuggest.mDictionaryFacilitator; - PersonalizationDictionarySessionRegistrar.init(this, dictionaryFacilitator); - } else { - PersonalizationDictionarySessionRegistrar.close(this); - } + shouldKeepUserHistoryDictionaries = true; + // TODO: Eliminate this restriction + shouldKeepPersonalizationDictionaries = + mSubtypeSwitcher.isSystemLocaleSameAsLocaleOfAllEnabledSubtypes(); } else { - PersonalizationHelper.removeAllPersonalizedDictionaries(this); + shouldKeepUserHistoryDictionaries = false; + shouldKeepPersonalizationDictionaries = false; + } + if (!shouldKeepUserHistoryDictionaries) { + // Remove user history dictionaries. + PersonalizationHelper.removeAllUserHistoryDictionaries(this); + if (suggest != null) { + suggest.mDictionaryFacilitator.clearUserHistoryDictionary(); + } + } + if (!shouldKeepPersonalizationDictionaries) { + // Remove personalization dictionaries. + PersonalizationHelper.removeAllPersonalizationDictionaries(this); PersonalizationDictionarySessionRegistrar.resetAll(this); + } else { + final DictionaryFacilitatorForSuggest dictionaryFacilitator = + (suggest == null) ? null : suggest.mDictionaryFacilitator; + PersonalizationDictionarySessionRegistrar.init(this, dictionaryFacilitator); } } @@ -579,9 +598,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen (mInputLogic.mSuggest == null) ? null : mInputLogic.mSuggest.mDictionaryFacilitator; // Creates new dictionary facilitator for the new locale. final DictionaryFacilitatorForSuggest dictionaryFacilitator = - new DictionaryFacilitatorForSuggest(this /* context */, locale, - settingsValues, this /* DictionaryInitializationListener */, - oldDictionaryFacilitator); + new DictionaryFacilitatorForSuggest(this /* context */, locale, settingsValues, + this /* DictionaryInitializationListener */, oldDictionaryFacilitator); final Suggest newSuggest = new Suggest(locale, dictionaryFacilitator); if (settingsValues.mCorrectionEnabled) { newSuggest.setAutoCorrectionThreshold(settingsValues.mAutoCorrectionThreshold); @@ -658,7 +676,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen .findViewById(android.R.id.extractArea); mKeyPreviewBackingView = view.findViewById(R.id.key_preview_backing); mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view); - if (mSuggestionStripView != null) { + if (hasSuggestionStripView()) { mSuggestionStripView.setListener(this, view); } if (LatinImeLogger.sVISUALDEBUG) { @@ -738,13 +756,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen ResearchLogger.latinIME_onStartInputViewInternal(editorInfo, prefs); } if (InputAttributes.inPrivateImeOptions(null, NO_MICROPHONE_COMPAT, editorInfo)) { - Log.w(TAG, "Deprecated private IME option specified: " - + editorInfo.privateImeOptions); + Log.w(TAG, "Deprecated private IME option specified: " + editorInfo.privateImeOptions); Log.w(TAG, "Use " + getPackageName() + "." + NO_MICROPHONE + " instead"); } if (InputAttributes.inPrivateImeOptions(getPackageName(), FORCE_ASCII, editorInfo)) { - Log.w(TAG, "Deprecated private IME option specified: " - + editorInfo.privateImeOptions); + Log.w(TAG, "Deprecated private IME option specified: " + editorInfo.privateImeOptions); Log.w(TAG, "Use EditorInfo.IME_FLAG_FORCE_ASCII flag instead"); } @@ -777,14 +793,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Note: the following does a round-trip IPC on the main thread: be careful final Locale currentLocale = mSubtypeSwitcher.getCurrentSubtypeLocale(); - final Suggest suggest = mInputLogic.mSuggest; + Suggest suggest = mInputLogic.mSuggest; if (null != suggest && null != currentLocale && !currentLocale.equals(suggest.mLocale)) { initSuggest(); - } - if (mSuggestionStripView != null) { - // This will set the punctuation suggestions if next word suggestion is off; - // otherwise it will clear the suggestion strip. - setNeutralSuggestionStrip(); + suggest = mInputLogic.mSuggest; } // Sometimes, while rotating, for some reason the framework tells the app we are not @@ -809,6 +821,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (isDifferentTextField || !currentSettingsValues.hasSameOrientation(getResources().getConfiguration())) { loadSettings(); + suggest = mInputLogic.mSuggest; } if (isDifferentTextField) { mainKeyboardView.closing(); @@ -834,8 +847,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Space state must be updated before calling updateShiftState switcher.updateShiftState(); } - setSuggestionStripShownInternal( - isSuggestionsStripVisible(), /* needsInputViewShown */ false); + // This will set the punctuation suggestions if next word suggestion is off; + // otherwise it will clear the suggestion strip. + setNeutralSuggestionStripInternal(false /* needsInputViewShown */); mHandler.cancelUpdateSuggestionStrip(); mHandler.cancelDoubleSpacePeriodTimer(); @@ -894,12 +908,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, composingSpanStart, composingSpanEnd); if (DEBUG) { - Log.i(TAG, "onUpdateSelection: oss=" + oldSelStart - + ", ose=" + oldSelEnd - + ", nss=" + newSelStart - + ", nse=" + newSelEnd - + ", cs=" + composingSpanStart - + ", ce=" + composingSpanEnd); + Log.i(TAG, "onUpdateSelection: oss=" + oldSelStart + ", ose=" + oldSelEnd + + ", nss=" + newSelStart + ", nse=" + newSelEnd + + ", cs=" + composingSpanStart + ", ce=" + composingSpanEnd); } if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { ResearchLogger.latinIME_onUpdateSelection(oldSelStart, oldSelEnd, @@ -983,7 +994,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } } - if (!mSettings.getCurrent().isApplicationSpecifiedCompletionsOn()) return; + if (!mSettings.getCurrent().isApplicationSpecifiedCompletionsOn()) { + return; + } if (applicationSpecifiedCompletions == null) { setNeutralSuggestionStrip(); if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { @@ -997,27 +1010,25 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final ArrayList<SuggestedWords.SuggestedWordInfo> applicationSuggestedWords = SuggestedWords.getFromApplicationSpecifiedCompletions( applicationSpecifiedCompletions); - final SuggestedWords suggestedWords = new SuggestedWords( - applicationSuggestedWords, null /* rawSuggestions */, - false /* typedWordValid */, - false /* willAutoCorrect */, - false /* isObsoleteSuggestions */, - false /* isPrediction */); - // When in fullscreen mode, show completions generated by the application - setSuggestedWords(suggestedWords, true /* shouldShow */); + final SuggestedWords suggestedWords = new SuggestedWords(applicationSuggestedWords, + null /* rawSuggestions */, false /* typedWordValid */, false /* willAutoCorrect */, + false /* isObsoleteSuggestions */, false /* isPrediction */); + // When in fullscreen mode, show completions generated by the application forcibly + setSuggestedWords(suggestedWords, true /* isSuggestionStripVisible */, + true /* needsInputViewShown */); if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions); } } - private void setSuggestionStripShownInternal(final boolean shown, + private void setSuggestionStripShownInternal(final boolean isSuggestionStripVisible, final boolean needsInputViewShown) { // TODO: Modify this if we support suggestions with hard keyboard - if (!onEvaluateInputViewShown() || null == mSuggestionStripView) { + if (!onEvaluateInputViewShown() || !hasSuggestionStripView()) { return; } final boolean inputViewShown = mKeyboardSwitcher.isShowingMainKeyboardOrEmojiPalettes(); - final boolean shouldShowSuggestions = shown + final boolean shouldShowSuggestions = isSuggestionStripVisible && (needsInputViewShown ? inputViewShown : true); if (shouldShowSuggestions) { mSuggestionStripView.setVisibility(View.VISIBLE); @@ -1057,7 +1068,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public void onComputeInsets(final InputMethodService.Insets outInsets) { super.onComputeInsets(outInsets); final View visibleKeyboardView = mKeyboardSwitcher.getVisibleKeyboardView(); - if (visibleKeyboardView == null || mSuggestionStripView == null) { + if (visibleKeyboardView == null || !hasSuggestionStripView()) { return; } final int adjustedBackingHeight = getAdjustedBackingViewHeight(); @@ -1172,31 +1183,54 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void showImportantNoticeContents() { final Context context = this; - final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { + final OnShowListener onShowListener = new OnShowListener() { @Override - public void onClick(final DialogInterface di, final int position) { - di.dismiss(); + public void onShow(final DialogInterface dialog) { ImportantNoticeUtils.updateLastImportantNoticeVersion(context); - if (position == DialogInterface.BUTTON_POSITIVE) { - setNeutralSuggestionStrip(); - return; - } + onShowImportantNoticeDialog( + ImportantNoticeUtils.getCurrentImportantNoticeVersion(context)); + } + }; + final OnClickListener onClickListener = new OnClickListener() { + @Override + public void onClick(final DialogInterface dialog, final int position) { if (position == DialogInterface.BUTTON_NEGATIVE) { launchSettings(); - return; } } }; - final AlertDialog.Builder builder = - new AlertDialog.Builder(context, AlertDialog.THEME_HOLO_DARK); - builder.setMessage(R.string.important_notice_contents) - .setPositiveButton(android.R.string.ok, listener) - .setNegativeButton(R.string.go_to_settings, listener); - showOptionDialog(builder.create(), true /* cancelable */); + final OnDismissListener onDismissListener = new OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + setNeutralSuggestionStrip(); + } + }; + final String importantNoticeContents = ImportantNoticeUtils.getImportantNoticeContents( + context); + final AlertDialog.Builder builder = new AlertDialog.Builder( + context, AlertDialog.THEME_HOLO_DARK); + builder.setMessage(importantNoticeContents) + .setPositiveButton(android.R.string.ok, null /* listener */) + .setNegativeButton(R.string.go_to_settings, onClickListener); + final AlertDialog importantNoticeDialog = builder.create(); + importantNoticeDialog.setOnShowListener(onShowListener); + importantNoticeDialog.setOnDismissListener(onDismissListener); + showOptionDialog(importantNoticeDialog); + } + + private void onShowImportantNoticeDialog(final int importantNoticeVersion) { + if (importantNoticeVersion == + ImportantNoticeUtils.VERSION_TO_ENABLE_PERSONALIZED_SUGGESTIONS) { + mSettings.writeUsePersonalizationDictionary(true /* enabled */); + loadSettings(); + initSuggest(); + } } public void displaySettingsDialog() { - if (isShowingOptionDialog()) return; + if (isShowingOptionDialog()) { + return; + } showSubtypeSelectorAndSettings(); } @@ -1256,8 +1290,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSubtypeSwitcher.switchToShortcutIME(this); // Still call the *#onCodeInput methods for readability. } - mInputLogic.onCodeInput(codeToSend, keyX, keyY, mSettings.getCurrent(), - mHandler, mKeyboardSwitcher); + mInputLogic.onCodeInput(codeToSend, keyX, keyY, mSettings.getCurrent(), mHandler, + mKeyboardSwitcher); mKeyboardSwitcher.onCodeInput(codePoint); } @@ -1314,27 +1348,28 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Nothing to do so far. } - // TODO: remove this, read this directly from mInputLogic or something in the tests - @UsedForTesting - public boolean isShowingPunctuationList() { - return mInputLogic.isShowingPunctuationList(mSettings.getCurrent()); - } - // TODO[IL]: Define a clear interface for this - public boolean isSuggestionsStripVisible() { - final SettingsValues currentSettings = mSettings.getCurrent(); - if (mSuggestionStripView == null) + public boolean isSuggestionStripVisible() { + if (!hasSuggestionStripView()) { return false; - if (mSuggestionStripView.isShowingAddToDictionaryHint()) + } + if (mSuggestionStripView.isShowingAddToDictionaryHint()) { return true; - if (null == currentSettings) + } + final SettingsValues currentSettings = mSettings.getCurrent(); + if (null == currentSettings) { return false; - if (ImportantNoticeUtils.shouldShowImportantNotice(this, currentSettings.mInputAttributes)) + } + if (ImportantNoticeUtils.shouldShowImportantNotice(this, + currentSettings.mInputAttributes)) { return true; - if (!currentSettings.isSuggestionStripVisible()) + } + if (!currentSettings.isSuggestionStripVisible()) { return false; - if (currentSettings.isApplicationSpecifiedCompletionsOn()) + } + if (currentSettings.isApplicationSpecifiedCompletionsOn()) { return true; + } return currentSettings.isSuggestionsRequested(); } @@ -1350,32 +1385,35 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void dismissAddToDictionaryHint() { - if (null != mSuggestionStripView) { - mSuggestionStripView.dismissAddToDictionaryHint(); + if (!hasSuggestionStripView()) { + return; } + mSuggestionStripView.dismissAddToDictionaryHint(); } // TODO[IL]: Define a clear interface for this - public void setSuggestedWords(final SuggestedWords suggestedWords, final boolean shouldShow) { + public void setSuggestedWords(final SuggestedWords suggestedWords, + final boolean isSuggestionStripVisible, final boolean needsInputViewShown) { mInputLogic.setSuggestedWords(suggestedWords); - if (mSuggestionStripView != null) { - final SettingsValues currentSettings = mSettings.getCurrent(); - final boolean showSuggestions; - if (SuggestedWords.EMPTY == suggestedWords - || suggestedWords.isPunctuationSuggestions() - || !currentSettings.isSuggestionsRequested()) { - showSuggestions = !mSuggestionStripView.maybeShowImportantNoticeTitle( - currentSettings.mInputAttributes); - } else { - showSuggestions = true; - } - if (showSuggestions) { - mSuggestionStripView.setSuggestions(suggestedWords, - SubtypeLocaleUtils.isRtlLanguage(mSubtypeSwitcher.getCurrentSubtype())); - } - mKeyboardSwitcher.onAutoCorrectionStateChanged(suggestedWords.mWillAutoCorrect); - setSuggestionStripShownInternal(shouldShow, true /* needsInputViewShown */); + if (!hasSuggestionStripView()) { + return; + } + final SettingsValues currentSettings = mSettings.getCurrent(); + final boolean showSuggestions; + if (SuggestedWords.EMPTY == suggestedWords + || suggestedWords.isPunctuationSuggestions() + || !currentSettings.isSuggestionsRequested()) { + showSuggestions = !mSuggestionStripView.maybeShowImportantNoticeTitle( + currentSettings.mInputAttributes); + } else { + showSuggestions = true; } + if (showSuggestions) { + mSuggestionStripView.setSuggestions(suggestedWords, + SubtypeLocaleUtils.isRtlLanguage(mSubtypeSwitcher.getCurrentSubtype())); + } + mKeyboardSwitcher.onAutoCorrectionStateChanged(suggestedWords.mWillAutoCorrect); + setSuggestionStripShownInternal(isSuggestionStripVisible, needsInputViewShown); } // TODO[IL]: Move this out of LatinIME. @@ -1414,9 +1452,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } suggest.getSuggestedWords(mInputLogic.mWordComposer, mInputLogic.mWordComposer.getPreviousWordForSuggestion(), - keyboard.getProximityInfo(), - currentSettings.mBlockPotentiallyOffensive, currentSettings.mCorrectionEnabled, - additionalFeaturesOptions, sessionId, sequenceNumber, callback); + keyboard.getProximityInfo(), currentSettings.mBlockPotentiallyOffensive, + currentSettings.mCorrectionEnabled, additionalFeaturesOptions, sessionId, + sequenceNumber, callback); } // TODO[IL]: Move this to InputLogic @@ -1430,7 +1468,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // the "add to dictionary" hint, we need to revert to suggestions - although it is unclear // how we can come here if it's displayed. if (suggestedWords.size() > 1 || typedWord.length() <= 1 - || null == mSuggestionStripView || isShowingAddToDictionaryHint()) { + || !hasSuggestionStripView() || isShowingAddToDictionaryHint()) { return suggestedWords; } else { final SuggestedWords punctuationList = @@ -1440,10 +1478,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final ArrayList<SuggestedWords.SuggestedWordInfo> typedWordAndPreviousSuggestions = SuggestedWords.getTypedWordAndPreviousSuggestions(typedWord, oldSuggestedWords); return new SuggestedWords(typedWordAndPreviousSuggestions, null /* rawSuggestions */, - false /* typedWordValid */, - false /* hasAutoCorrectionCandidate */, - true /* isObsoleteSuggestions */, - false /* isPrediction */); + false /* typedWordValid */, false /* hasAutoCorrectionCandidate */, + true /* isObsoleteSuggestions */, false /* isPrediction */); } } @@ -1463,7 +1499,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen setNeutralSuggestionStrip(); } else { mInputLogic.mWordComposer.setAutoCorrection(autoCorrection); - setSuggestedWords(suggestedWords, isSuggestionsStripVisible()); + setSuggestedWords( + suggestedWords, isSuggestionStripVisible(), true /* needsInputViewShown */); } // Cache the auto-correction in accessibility code so we can speak it if the user // touches a key that will insert it. @@ -1481,7 +1518,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void showAddToDictionaryHint(final String word) { - if (null == mSuggestionStripView) return; + if (!hasSuggestionStripView()) { + return; + } mSuggestionStripView.showAddToDictionaryHint(word); } @@ -1490,13 +1529,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // punctuation suggestions (if it's disabled). @Override public void setNeutralSuggestionStrip() { + setNeutralSuggestionStripInternal(true /* needsInputViewShown */); + } + + private void setNeutralSuggestionStripInternal(final boolean needsInputViewShown) { final SettingsValues currentSettings = mSettings.getCurrent(); - if (currentSettings.mBigramPredictionEnabled) { - setSuggestedWords(SuggestedWords.EMPTY, isSuggestionsStripVisible()); - } else { - setSuggestedWords(currentSettings.mSpacingAndPunctuations.mSuggestPuncList, - isSuggestionsStripVisible()); - } + final SuggestedWords neutralSuggestions = currentSettings.mBigramPredictionEnabled + ? SuggestedWords.EMPTY : currentSettings.mSpacingAndPunctuations.mSuggestPuncList; + setSuggestedWords(neutralSuggestions, isSuggestionStripVisible(), needsInputViewShown); } // TODO: Make this private @@ -1638,7 +1678,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen getString(R.string.language_selection_title), getString(ApplicationUtils.getActivityTitleResId(this, SettingsActivity.class)), }; - final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { + final OnClickListener listener = new OnClickListener() { @Override public void onClick(DialogInterface di, int position) { di.dismiss(); @@ -1659,18 +1699,18 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen }; final AlertDialog.Builder builder = new AlertDialog.Builder(this).setItems(items, listener).setTitle(title); - showOptionDialog(builder.create(), true /*cancelable */); + showOptionDialog(builder.create()); } // TODO: Move this method out of {@link LatinIME}. - private void showOptionDialog(final AlertDialog dialog, final boolean cancelable) { + private void showOptionDialog(final AlertDialog dialog) { final IBinder windowToken = mKeyboardSwitcher.getMainKeyboardView().getWindowToken(); if (windowToken == null) { return; } - dialog.setCancelable(cancelable); - dialog.setCanceledOnTouchOutside(cancelable); + dialog.setCancelable(true /* cancelable */); + dialog.setCanceledOnTouchOutside(true /* cancelable */); final Window window = dialog.getWindow(); final WindowManager.LayoutParams lp = window.getAttributes(); @@ -1685,7 +1725,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // TODO: can this be removed somehow without breaking the tests? @UsedForTesting - /* package for test */ SuggestedWords getSuggestedWords() { + /* package for test */ SuggestedWords getSuggestedWordsForTest() { // You may not use this method for anything else than debug return DEBUG ? mInputLogic.mSuggestedWords : null; } |