/** @return current line number of the cursor, or -1 if no cursor */ public int getCurrentCursorLine() { int selectionStart = mMentionsEditText.getSelectionStart(); Layout layout = mMentionsEditText.getLayout(); if (layout != null && !(selectionStart == -1)) { return layout.getLineForOffset(selectionStart); } return -1; }
/** * Determine whether the internal {@link EditText} should match the full height of the {@link * RichEditorView} initially or if it should wrap its content in height and expand to fill it as * the user types. * * <p>Note: The {@link EditText} will always match the parent (i.e. the {@link RichEditorView} in * width. Additionally, the {@link ListView} containing mention suggestions will always fill the * rest of the height in the {@link RichEditorView}. * * @param enabled true if the {@link EditText} should wrap its content in height */ public void setEditTextShouldWrapContent(boolean enabled) { mEditTextShouldWrapContent = enabled; if (mMentionsEditText == null) { return; } mPrevEditTextParams = mMentionsEditText.getLayoutParams(); int wrap = (enabled) ? LayoutParams.WRAP_CONTENT : LayoutParams.MATCH_PARENT; if (mPrevEditTextParams != null && mPrevEditTextParams.height == wrap) { return; } ViewGroup.LayoutParams newParams = new LayoutParams(LayoutParams.MATCH_PARENT, wrap); mMentionsEditText.setLayoutParams(newParams); requestLayout(); invalidate(); }
/** * Sets the {@link * com.linkedin.android.spyglass.suggestions.interfaces.SuggestionsVisibilityManager} to use * (determines which and how the suggestions are displayed). * * @param suggestionsVisibilityManager the {@link * com.linkedin.android.spyglass.suggestions.interfaces.SuggestionsVisibilityManager} to use */ public void setSuggestionsManager( final @NonNull SuggestionsVisibilityManager suggestionsVisibilityManager) { if (mMentionsEditText != null && mSuggestionsAdapter != null) { mMentionsEditText.setSuggestionsVisibilityManager(suggestionsVisibilityManager); mSuggestionsAdapter.setSuggestionsManager(suggestionsVisibilityManager); } }
/** * Convenience method for {@link MentionsEditText#getCurrentKeywordsString()}. * * @return a String representing current keywords in the underlying {@link EditText} */ @NonNull public String getCurrentKeywordsString() { if (mMentionsEditText == null) { return ""; } return mMentionsEditText.getCurrentKeywordsString(); }
/** * Disables spelling suggestions from the user's keyboard. This is necessary because some * keyboards will replace the input text with spelling suggestions automatically, which changes * the suggestion results. This results in a confusing user experience. * * @param disable {@code true} if spelling suggestions should be disabled, otherwise {@code false} */ private void disableSpellingSuggestions(boolean disable) { // toggling suggestions often resets the cursor location, but we don't want that to happen int start = mMentionsEditText.getSelectionStart(); int end = mMentionsEditText.getSelectionEnd(); // -1 means there is no selection or cursor. if (start == -1 || end == -1) { return; } if (disable) { // store the previous input type mOriginalInputType = mMentionsEditText.getInputType(); } mMentionsEditText.setInputType( disable ? InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS : mOriginalInputType); mMentionsEditText.setSelection(start, end); }
/** * Updates the TextView counting the number of characters in the editor. Sets not only the content * of the TextView, but also the color of the text depending if the limit has been reached. */ private void updateEditorTextCount() { if (mMentionsEditText != null && mTextCounterView != null) { int textCount = mMentionsEditText.getMentionsText().length(); mTextCounterView.setText(String.valueOf(textCount)); if (mTextCountLimit > 0 && textCount > mTextCountLimit) { mTextCounterView.setTextColor(mBeyondCountLimitTextColor); } else { mTextCounterView.setTextColor(mWithinCountLimitTextColor); } } }
public void init(Context context, AttributeSet attrs) { // Inflate view from XML layout file LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater.inflate(R.layout.editor_view, this, true); // Get the views for the fragment mMentionsEditText = (MentionsEditText) findViewById(R.id.text_editor); mTextCounterView = (TextView) findViewById(R.id.text_counter); mSuggestionsList = (ListView) findViewById(R.id.suggestions_list); // Create the tokenizer to use for the editor // TODO: Allow customization of configuration via XML attributes WordTokenizerConfig config = new WordTokenizerConfig.Builder().build(); WordTokenizer tokenizer = new WordTokenizer(config); mMentionsEditText.setTokenizer(tokenizer); // Set various delegates on the MentionEditText to the RichEditorView mMentionsEditText.setSuggestionsVisibilityManager(this); mMentionsEditText.addTextChangedListener(this); mMentionsEditText.setQueryTokenReceiver(this); mMentionsEditText.setAvoidPrefixOnTap(true); // Set the suggestions adapter SuggestionsListBuilder listBuilder = new BasicSuggestionsListBuilder(); mSuggestionsAdapter = new SuggestionsAdapter(context, this, listBuilder); mSuggestionsList.setAdapter(mSuggestionsAdapter); // Set the item click listener mSuggestionsList.setOnItemClickListener( new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Mentionable mention = (Mentionable) mSuggestionsAdapter.getItem(position); if (mMentionsEditText != null) { mMentionsEditText.insertMention(mention); mSuggestionsAdapter.clear(); } } }); // Display and update the editor text counter (starts it at 0) updateEditorTextCount(); // Wrap the EditText content height if necessary (ideally, allow this to be controlled via // custom XML attribute) setEditTextShouldWrapContent(mEditTextShouldWrapContent); mPrevEditTextBottomPadding = mMentionsEditText.getPaddingBottom(); }
/** * Sets the input type of the embedded {@link MentionsEditText}. * * @param type the input type of the {@link MentionsEditText} */ public void setInputType(final int type) { if (mMentionsEditText != null) { mMentionsEditText.setInputType(type); } }
/** * Sets the text hint to use within the embedded {@link MentionsEditText}. * * @param hint the text hint to use */ public void setHint(final @NonNull CharSequence hint) { if (mMentionsEditText != null) { mMentionsEditText.setHint(hint); } }
/** * Sets the text being displayed within the {@link RichEditorView}. Note that this removes the * {@link TextWatcher} temporarily to avoid changing the text while listening to text changes * (which could result in an infinite loop). * * @param text the text to display */ public void setText(final @NonNull CharSequence text) { if (mMentionsEditText != null) { mMentionsEditText.setText(text); } }
/** @return the {@link Tokenizer} in use */ @Nullable public Tokenizer getTokenizer() { return (mMentionsEditText != null) ? mMentionsEditText.getTokenizer() : null; }
/** @return the {@link MentionsEditable} within the embedded {@link MentionsEditText} */ @NonNull public MentionsEditable getText() { return (mMentionsEditText != null) ? ((MentionsEditable) mMentionsEditText.getText()) : new MentionsEditable(""); }
/** * Resets the given {@link MentionSpan} in the editor, forcing it to redraw with its latest * drawable state. * * @param span the {@link MentionSpan} to update */ public void updateSpan(MentionSpan span) { if (mMentionsEditText != null) { mMentionsEditText.updateSpan(span); } }
/** {@inheritDoc} */ public void displaySuggestions(boolean display) { // If nothing to change, return early if (display == isDisplayingSuggestions() || mMentionsEditText == null) { return; } // Change view depending on whether suggestions are being shown or not if (display) { disableSpellingSuggestions(true); mTextCounterView.setVisibility(View.GONE); mSuggestionsList.setVisibility(View.VISIBLE); mPrevEditTextParams = mMentionsEditText.getLayoutParams(); mPrevEditTextBottomPadding = mMentionsEditText.getPaddingBottom(); mMentionsEditText.setPadding( mMentionsEditText.getPaddingLeft(), mMentionsEditText.getPaddingTop(), mMentionsEditText.getPaddingRight(), mMentionsEditText.getPaddingTop()); int height = mMentionsEditText.getPaddingTop() + mMentionsEditText.getLineHeight() + mMentionsEditText.getPaddingBottom(); mMentionsEditText.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, height)); mMentionsEditText.setVerticalScrollBarEnabled(false); int cursorLine = getCurrentCursorLine(); Layout layout = mMentionsEditText.getLayout(); if (layout != null) { int lineTop = layout.getLineTop(cursorLine); mMentionsEditText.scrollTo(0, lineTop); } // Notify action listener that list was shown if (mActionListener != null) { mActionListener.onSuggestionsDisplayed(); } } else { disableSpellingSuggestions(false); mTextCounterView.setVisibility(View.VISIBLE); mSuggestionsList.setVisibility(View.GONE); mMentionsEditText.setPadding( mMentionsEditText.getPaddingLeft(), mMentionsEditText.getPaddingTop(), mMentionsEditText.getPaddingRight(), mPrevEditTextBottomPadding); if (mPrevEditTextParams == null) { mPrevEditTextParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); } mMentionsEditText.setLayoutParams(mPrevEditTextParams); mMentionsEditText.setVerticalScrollBarEnabled(true); // Notify action listener that list was hidden if (mActionListener != null) { mActionListener.onSuggestionsHidden(); } } requestLayout(); invalidate(); }
/** * Sets the selection within the embedded {@link MentionsEditText}. * * @param index the index of the selection within the embedded {@link MentionsEditText} */ public void setSelection(final int index) { if (mMentionsEditText != null) { mMentionsEditText.setSelection(index); } }
/** * Sets the {@link Tokenizer} for the {@link MentionsEditText} to use. * * @param tokenizer the {@link Tokenizer} to use */ public void setTokenizer(final @NonNull Tokenizer tokenizer) { if (mMentionsEditText != null) { mMentionsEditText.setTokenizer(tokenizer); } }
/** Deselects any spans in the editor that are currently selected. */ public void deselectAllSpans() { if (mMentionsEditText != null) { mMentionsEditText.deselectAllSpans(); } }
/** * Adds an {@link TextWatcher} to the internal {@link MentionsEditText}. * * @param hostTextWatcher the {TextWatcher} to add */ public void addTextChangedListener(final @Nullable TextWatcher hostTextWatcher) { if (mMentionsEditText != null) { mMentionsEditText.addTextChangedListener(hostTextWatcher); } }
/** @return a list of {@link MentionSpan} objects currently in the editor */ @NonNull public List<MentionSpan> getMentionSpans() { return (mMentionsEditText != null) ? mMentionsEditText.getMentionsText().getMentionSpans() : new ArrayList<MentionSpan>(); }