Loading core/java/android/widget/Editor.java +137 −122 Original line number Diff line number Diff line Loading @@ -2634,8 +2634,11 @@ public class Editor { @VisibleForTesting public class SuggestionsPopupWindow extends PinnedPopupWindow implements OnItemClickListener { private static final int MAX_NUMBER_SUGGESTIONS = SuggestionSpan.SUGGESTIONS_MAX_SIZE; private static final int ADD_TO_DICTIONARY = -1; private static final int DELETE_TEXT = -2; // Key of intent extras for inserting new word into user dictionary. private static final String USER_DICTIONARY_EXTRA_WORD = "word"; private static final String USER_DICTIONARY_EXTRA_LOCALE = "locale"; private SuggestionInfo[] mSuggestionInfos; private int mNumberOfSuggestions; private boolean mCursorWasVisibleBeforeSuggestions; Loading @@ -2644,7 +2647,10 @@ public class Editor { private final Comparator<SuggestionSpan> mSuggestionSpanComparator; private final HashMap<SuggestionSpan, Integer> mSpansLengths; private final TextAppearanceSpan mHighlightSpan = new TextAppearanceSpan( mTextView.getContext(), android.R.style.TextAppearance_SuggestionHighlight); mTextView.getContext(), mTextView.mTextEditSuggestionHighlightStyle); private TextView mAddToDictionaryButton; private TextView mDeleteButton; private SuggestionSpan mMisspelledSpan; private class CustomPopupWindow extends PopupWindow { public CustomPopupWindow(Context context, int defStyleAttr) { Loading Loading @@ -2684,17 +2690,73 @@ public class Editor { @Override protected void initContentView() { ListView listView = new ListView(mTextView.getContext()); final LayoutInflater inflater = (LayoutInflater) mTextView.getContext(). getSystemService(Context.LAYOUT_INFLATER_SERVICE); final LinearLayout linearLayout = (LinearLayout) inflater.inflate( mTextView.mTextEditSuggestionContainerLayout, null); final ListView suggestionListView = (ListView) linearLayout.findViewById( com.android.internal.R.id.suggestionContainer); mSuggestionsAdapter = new SuggestionAdapter(); listView.setAdapter(mSuggestionsAdapter); listView.setOnItemClickListener(this); mContentView = listView; suggestionListView.setAdapter(mSuggestionsAdapter); suggestionListView.setOnItemClickListener(this); // Inflate the suggestion items once and for all. + 2 for add to dictionary and delete mSuggestionInfos = new SuggestionInfo[MAX_NUMBER_SUGGESTIONS + 2]; // Inflate the suggestion items once and for all. mSuggestionInfos = new SuggestionInfo[MAX_NUMBER_SUGGESTIONS]; for (int i = 0; i < mSuggestionInfos.length; i++) { mSuggestionInfos[i] = new SuggestionInfo(); } mContentView = linearLayout; mAddToDictionaryButton = (TextView) linearLayout.findViewById( com.android.internal.R.id.addToDictionaryButton); mAddToDictionaryButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { final Editable editable = (Editable) mTextView.getText(); final int spanStart = editable.getSpanStart(mMisspelledSpan); final int spanEnd = editable.getSpanEnd(mMisspelledSpan); final String originalText = TextUtils.substring(editable, spanStart, spanEnd); final Intent intent = new Intent(Settings.ACTION_USER_DICTIONARY_INSERT); intent.putExtra(USER_DICTIONARY_EXTRA_WORD, originalText); intent.putExtra(USER_DICTIONARY_EXTRA_LOCALE, mTextView.getTextServicesLocale().toString()); intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); mTextView.getContext().startActivity(intent); // There is no way to know if the word was indeed added. Re-check. // TODO The ExtractEditText should remove the span in the original text instead editable.removeSpan(mMisspelledSpan); Selection.setSelection(editable, spanEnd); updateSpellCheckSpans(spanStart, spanEnd, false); hideWithCleanUp(); } }); mDeleteButton = (TextView) linearLayout.findViewById( com.android.internal.R.id.deleteButton); mDeleteButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { final Editable editable = (Editable) mTextView.getText(); final int spanUnionStart = editable.getSpanStart(mSuggestionRangeSpan); int spanUnionEnd = editable.getSpanEnd(mSuggestionRangeSpan); if (spanUnionStart >= 0 && spanUnionEnd > spanUnionStart) { // Do not leave two adjacent spaces after deletion, or one at beginning of // text if (spanUnionEnd < editable.length() && Character.isSpaceChar(editable.charAt(spanUnionEnd)) && (spanUnionStart == 0 || Character.isSpaceChar(editable.charAt(spanUnionStart - 1)))) { spanUnionEnd = spanUnionEnd + 1; } mTextView.deleteText_internal(spanUnionStart, spanUnionEnd); } hideWithCleanUp(); } }); } public boolean isShowingUp() { Loading Loading @@ -2753,14 +2815,6 @@ public class Editor { final SuggestionInfo suggestionInfo = mSuggestionInfos[position]; textView.setText(suggestionInfo.text); if (suggestionInfo.suggestionIndex == ADD_TO_DICTIONARY || suggestionInfo.suggestionIndex == DELETE_TEXT) { textView.setBackgroundColor(Color.TRANSPARENT); } else { textView.setBackgroundColor(Color.WHITE); } return textView; } } Loading Loading @@ -2843,6 +2897,14 @@ public class Editor { width = Math.max(width, view.getMeasuredWidth()); } if (mAddToDictionaryButton.getVisibility() != View.GONE) { mAddToDictionaryButton.measure(horizontalMeasure, verticalMeasure); width = Math.max(width, mAddToDictionaryButton.getMeasuredWidth()); } mDeleteButton.measure(horizontalMeasure, verticalMeasure); width = Math.max(width, mDeleteButton.getMeasuredWidth()); // Enforce the width based on actual text widths mContentView.measure( View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), Loading Loading @@ -2878,6 +2940,7 @@ public class Editor { for (final SuggestionInfo info : mSuggestionInfos) { info.clear(); } mMisspelledSpan = null; hide(); } Loading @@ -2893,7 +2956,7 @@ public class Editor { int spanUnionStart = mTextView.getText().length(); int spanUnionEnd = 0; SuggestionSpan misspelledSpan = null; mMisspelledSpan = null; int underlineColor = 0; for (int spanIndex = 0; spanIndex < nbSpans; spanIndex++) { Loading @@ -2904,7 +2967,7 @@ public class Editor { spanUnionEnd = Math.max(spanEnd, spanUnionEnd); if ((suggestionSpan.getFlags() & SuggestionSpan.FLAG_MISSPELLED) != 0) { misspelledSpan = suggestionSpan; mMisspelledSpan = suggestionSpan; } // The first span dictates the background color of the highlighted text Loading Loading @@ -2949,31 +3012,16 @@ public class Editor { highlightTextDifferences(mSuggestionInfos[i], spanUnionStart, spanUnionEnd); } // Add "Add to dictionary" item if there is a span with the misspelled flag if (misspelledSpan != null) { final int misspelledStart = spannable.getSpanStart(misspelledSpan); final int misspelledEnd = spannable.getSpanEnd(misspelledSpan); // Make "Add to dictionary" item visible if there is a span with the misspelled flag int addToDictionaryButtonVisibility = View.GONE; if (mMisspelledSpan != null) { final int misspelledStart = spannable.getSpanStart(mMisspelledSpan); final int misspelledEnd = spannable.getSpanEnd(mMisspelledSpan); if (misspelledStart >= 0 && misspelledEnd > misspelledStart) { SuggestionInfo suggestionInfo = mSuggestionInfos[mNumberOfSuggestions]; suggestionInfo.suggestionSpan = misspelledSpan; suggestionInfo.suggestionIndex = ADD_TO_DICTIONARY; suggestionInfo.text.replace(0, suggestionInfo.text.length(), mTextView. getContext().getString(com.android.internal.R.string.addToDictionary)); suggestionInfo.text.setSpan(mHighlightSpan, 0, 0, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); mNumberOfSuggestions++; addToDictionaryButtonVisibility = View.VISIBLE; } } // Delete item SuggestionInfo suggestionInfo = mSuggestionInfos[mNumberOfSuggestions]; suggestionInfo.suggestionSpan = null; suggestionInfo.suggestionIndex = DELETE_TEXT; suggestionInfo.text.replace(0, suggestionInfo.text.length(), mTextView.getContext().getString(com.android.internal.R.string.deleteText)); suggestionInfo.text.setSpan(mHighlightSpan, 0, 0, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); mNumberOfSuggestions++; mAddToDictionaryButton.setVisibility(addToDictionaryButtonVisibility); if (mSuggestionRangeSpan == null) mSuggestionRangeSpan = new SuggestionRangeSpan(); if (underlineColor == 0) { Loading Loading @@ -3017,23 +3065,6 @@ public class Editor { Editable editable = (Editable) mTextView.getText(); SuggestionInfo suggestionInfo = mSuggestionInfos[position]; if (suggestionInfo.suggestionIndex == DELETE_TEXT) { final int spanUnionStart = editable.getSpanStart(mSuggestionRangeSpan); int spanUnionEnd = editable.getSpanEnd(mSuggestionRangeSpan); if (spanUnionStart >= 0 && spanUnionEnd > spanUnionStart) { // Do not leave two adjacent spaces after deletion, or one at beginning of text if (spanUnionEnd < editable.length() && Character.isSpaceChar(editable.charAt(spanUnionEnd)) && (spanUnionStart == 0 || Character.isSpaceChar(editable.charAt(spanUnionStart - 1)))) { spanUnionEnd = spanUnionEnd + 1; } mTextView.deleteText_internal(spanUnionStart, spanUnionEnd); } hideWithCleanUp(); return; } final int spanStart = editable.getSpanStart(suggestionInfo.suggestionSpan); final int spanEnd = editable.getSpanEnd(suggestionInfo.suggestionSpan); if (spanStart < 0 || spanEnd <= spanStart) { Loading @@ -3044,27 +3075,13 @@ public class Editor { final String originalText = TextUtils.substring(editable, spanStart, spanEnd); if (suggestionInfo.suggestionIndex == ADD_TO_DICTIONARY) { Intent intent = new Intent(Settings.ACTION_USER_DICTIONARY_INSERT); intent.putExtra("word", originalText); intent.putExtra("locale", mTextView.getTextServicesLocale().toString()); // Put a listener to replace the original text with a word which the user // modified in a user dictionary dialog. intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); mTextView.getContext().startActivity(intent); // There is no way to know if the word was indeed added. Re-check. // TODO The ExtractEditText should remove the span in the original text instead editable.removeSpan(suggestionInfo.suggestionSpan); Selection.setSelection(editable, spanEnd); updateSpellCheckSpans(spanStart, spanEnd, false); } else { // SuggestionSpans are removed by replace: save them before SuggestionSpan[] suggestionSpans = editable.getSpans(spanStart, spanEnd, final SuggestionSpan[] suggestionSpans = editable.getSpans(spanStart, spanEnd, SuggestionSpan.class); final int length = suggestionSpans.length; int[] suggestionSpansStarts = new int[length]; int[] suggestionSpansEnds = new int[length]; int[] suggestionSpansFlags = new int[length]; final int[] suggestionSpansStarts = new int[length]; final int[] suggestionSpansEnds = new int[length]; final int[] suggestionSpansFlags = new int[length]; for (int i = 0; i < length; i++) { final SuggestionSpan suggestionSpan = suggestionSpans[i]; suggestionSpansStarts[i] = editable.getSpanStart(suggestionSpan); Loading Loading @@ -3092,7 +3109,7 @@ public class Editor { mTextView.getContext(), originalText, suggestionInfo.suggestionIndex); // Swap text content between actual text and Suggestion span String[] suggestions = suggestionInfo.suggestionSpan.getSuggestions(); final String[] suggestions = suggestionInfo.suggestionSpan.getSuggestions(); suggestions[suggestionInfo.suggestionIndex] = originalText; // Restore previous SuggestionSpans Loading @@ -3111,8 +3128,6 @@ public class Editor { // Move cursor at the end of the replaced word final int newCursorPosition = spanEnd + lengthDifference; mTextView.setCursorPosition_internal(newCursorPosition, newCursorPosition); } hideWithCleanUp(); } } Loading core/java/android/widget/TextView.java +11 −1 Original line number Diff line number Diff line Loading @@ -624,7 +624,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Although these fields are specific to editable text, they are not added to Editor because // they are defined by the TextView's style and are theme-dependent. int mCursorDrawableRes; // These four fields, could be moved to Editor, since we know their default values and we // These six fields, could be moved to Editor, since we know their default values and we // could condition the creation of the Editor to a non standard value. This is however // brittle since the hardcoded values here (such as // com.android.internal.R.drawable.text_select_handle_left) would have to be updated if the Loading @@ -633,6 +633,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int mTextSelectHandleRightRes; int mTextSelectHandleRes; int mTextEditSuggestionItemLayout; int mTextEditSuggestionContainerLayout; int mTextEditSuggestionHighlightStyle; /** * EditText specific data, created on demand when one of the Editor fields is used. Loading Loading @@ -1155,6 +1157,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mTextEditSuggestionItemLayout = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.TextView_textEditSuggestionContainerLayout: mTextEditSuggestionContainerLayout = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.TextView_textEditSuggestionHighlightStyle: mTextEditSuggestionHighlightStyle = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.TextView_textIsSelectable: setTextIsSelectable(a.getBoolean(attr, false)); break; Loading core/res/res/layout/text_edit_suggestion_container.xml 0 → 100644 +43 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2015 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. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:divider="@null"> <ListView android:id="@+id/suggestionContainer" android:layout_width="match_parent" android:layout_height="wrap_content" android:divider="?android:attr/dividerHorizontal"> <!-- Suggestions will be added here. --> </ListView> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:divider="?android:attr/dividerHorizontal" android:showDividers="middle"> <TextView style="@android:style/Widget.Holo.SuggestionButton" android:id="@+id/addToDictionaryButton" android:text="@string/addToDictionary" /> <TextView style="@android:style/Widget.Holo.SuggestionButton" android:id="@+id/deleteButton" android:text="@string/deleteText" /> </LinearLayout> </LinearLayout> core/res/res/layout/text_edit_suggestion_container_material.xml 0 → 100644 +42 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2015 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. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:divider="?android:attr/dividerHorizontal" android:showDividers="middle" > <ListView android:id="@+id/suggestionContainer" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white" android:paddingTop="8dip" android:paddingBottom="8dip" android:divider="@null" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView style="@android:style/Widget.Material.SuggestionButton" android:id="@+id/addToDictionaryButton" android:text="@string/addToDictionary" /> <TextView style="@android:style/Widget.Material.SuggestionButton" android:id="@+id/deleteButton" android:text="@string/deleteText" /> </LinearLayout> </LinearLayout> core/res/res/layout/text_edit_suggestion_item.xml +3 −14 Original line number Diff line number Diff line Loading @@ -15,16 +15,5 @@ --> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingStart="16dip" android:paddingEnd="16dip" android:paddingTop="8dip" android:paddingBottom="8dip" android:layout_gravity="start|center_vertical" android:singleLine="true" android:drawablePadding="8dip" android:ellipsize="marquee" android:textAppearance="?android:attr/textAppearanceMedium" android:textColor="@android:color/dim_foreground_light" /> style="@android:style/Widget.Holo.SuggestionItem" /> Loading
core/java/android/widget/Editor.java +137 −122 Original line number Diff line number Diff line Loading @@ -2634,8 +2634,11 @@ public class Editor { @VisibleForTesting public class SuggestionsPopupWindow extends PinnedPopupWindow implements OnItemClickListener { private static final int MAX_NUMBER_SUGGESTIONS = SuggestionSpan.SUGGESTIONS_MAX_SIZE; private static final int ADD_TO_DICTIONARY = -1; private static final int DELETE_TEXT = -2; // Key of intent extras for inserting new word into user dictionary. private static final String USER_DICTIONARY_EXTRA_WORD = "word"; private static final String USER_DICTIONARY_EXTRA_LOCALE = "locale"; private SuggestionInfo[] mSuggestionInfos; private int mNumberOfSuggestions; private boolean mCursorWasVisibleBeforeSuggestions; Loading @@ -2644,7 +2647,10 @@ public class Editor { private final Comparator<SuggestionSpan> mSuggestionSpanComparator; private final HashMap<SuggestionSpan, Integer> mSpansLengths; private final TextAppearanceSpan mHighlightSpan = new TextAppearanceSpan( mTextView.getContext(), android.R.style.TextAppearance_SuggestionHighlight); mTextView.getContext(), mTextView.mTextEditSuggestionHighlightStyle); private TextView mAddToDictionaryButton; private TextView mDeleteButton; private SuggestionSpan mMisspelledSpan; private class CustomPopupWindow extends PopupWindow { public CustomPopupWindow(Context context, int defStyleAttr) { Loading Loading @@ -2684,17 +2690,73 @@ public class Editor { @Override protected void initContentView() { ListView listView = new ListView(mTextView.getContext()); final LayoutInflater inflater = (LayoutInflater) mTextView.getContext(). getSystemService(Context.LAYOUT_INFLATER_SERVICE); final LinearLayout linearLayout = (LinearLayout) inflater.inflate( mTextView.mTextEditSuggestionContainerLayout, null); final ListView suggestionListView = (ListView) linearLayout.findViewById( com.android.internal.R.id.suggestionContainer); mSuggestionsAdapter = new SuggestionAdapter(); listView.setAdapter(mSuggestionsAdapter); listView.setOnItemClickListener(this); mContentView = listView; suggestionListView.setAdapter(mSuggestionsAdapter); suggestionListView.setOnItemClickListener(this); // Inflate the suggestion items once and for all. + 2 for add to dictionary and delete mSuggestionInfos = new SuggestionInfo[MAX_NUMBER_SUGGESTIONS + 2]; // Inflate the suggestion items once and for all. mSuggestionInfos = new SuggestionInfo[MAX_NUMBER_SUGGESTIONS]; for (int i = 0; i < mSuggestionInfos.length; i++) { mSuggestionInfos[i] = new SuggestionInfo(); } mContentView = linearLayout; mAddToDictionaryButton = (TextView) linearLayout.findViewById( com.android.internal.R.id.addToDictionaryButton); mAddToDictionaryButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { final Editable editable = (Editable) mTextView.getText(); final int spanStart = editable.getSpanStart(mMisspelledSpan); final int spanEnd = editable.getSpanEnd(mMisspelledSpan); final String originalText = TextUtils.substring(editable, spanStart, spanEnd); final Intent intent = new Intent(Settings.ACTION_USER_DICTIONARY_INSERT); intent.putExtra(USER_DICTIONARY_EXTRA_WORD, originalText); intent.putExtra(USER_DICTIONARY_EXTRA_LOCALE, mTextView.getTextServicesLocale().toString()); intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); mTextView.getContext().startActivity(intent); // There is no way to know if the word was indeed added. Re-check. // TODO The ExtractEditText should remove the span in the original text instead editable.removeSpan(mMisspelledSpan); Selection.setSelection(editable, spanEnd); updateSpellCheckSpans(spanStart, spanEnd, false); hideWithCleanUp(); } }); mDeleteButton = (TextView) linearLayout.findViewById( com.android.internal.R.id.deleteButton); mDeleteButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { final Editable editable = (Editable) mTextView.getText(); final int spanUnionStart = editable.getSpanStart(mSuggestionRangeSpan); int spanUnionEnd = editable.getSpanEnd(mSuggestionRangeSpan); if (spanUnionStart >= 0 && spanUnionEnd > spanUnionStart) { // Do not leave two adjacent spaces after deletion, or one at beginning of // text if (spanUnionEnd < editable.length() && Character.isSpaceChar(editable.charAt(spanUnionEnd)) && (spanUnionStart == 0 || Character.isSpaceChar(editable.charAt(spanUnionStart - 1)))) { spanUnionEnd = spanUnionEnd + 1; } mTextView.deleteText_internal(spanUnionStart, spanUnionEnd); } hideWithCleanUp(); } }); } public boolean isShowingUp() { Loading Loading @@ -2753,14 +2815,6 @@ public class Editor { final SuggestionInfo suggestionInfo = mSuggestionInfos[position]; textView.setText(suggestionInfo.text); if (suggestionInfo.suggestionIndex == ADD_TO_DICTIONARY || suggestionInfo.suggestionIndex == DELETE_TEXT) { textView.setBackgroundColor(Color.TRANSPARENT); } else { textView.setBackgroundColor(Color.WHITE); } return textView; } } Loading Loading @@ -2843,6 +2897,14 @@ public class Editor { width = Math.max(width, view.getMeasuredWidth()); } if (mAddToDictionaryButton.getVisibility() != View.GONE) { mAddToDictionaryButton.measure(horizontalMeasure, verticalMeasure); width = Math.max(width, mAddToDictionaryButton.getMeasuredWidth()); } mDeleteButton.measure(horizontalMeasure, verticalMeasure); width = Math.max(width, mDeleteButton.getMeasuredWidth()); // Enforce the width based on actual text widths mContentView.measure( View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), Loading Loading @@ -2878,6 +2940,7 @@ public class Editor { for (final SuggestionInfo info : mSuggestionInfos) { info.clear(); } mMisspelledSpan = null; hide(); } Loading @@ -2893,7 +2956,7 @@ public class Editor { int spanUnionStart = mTextView.getText().length(); int spanUnionEnd = 0; SuggestionSpan misspelledSpan = null; mMisspelledSpan = null; int underlineColor = 0; for (int spanIndex = 0; spanIndex < nbSpans; spanIndex++) { Loading @@ -2904,7 +2967,7 @@ public class Editor { spanUnionEnd = Math.max(spanEnd, spanUnionEnd); if ((suggestionSpan.getFlags() & SuggestionSpan.FLAG_MISSPELLED) != 0) { misspelledSpan = suggestionSpan; mMisspelledSpan = suggestionSpan; } // The first span dictates the background color of the highlighted text Loading Loading @@ -2949,31 +3012,16 @@ public class Editor { highlightTextDifferences(mSuggestionInfos[i], spanUnionStart, spanUnionEnd); } // Add "Add to dictionary" item if there is a span with the misspelled flag if (misspelledSpan != null) { final int misspelledStart = spannable.getSpanStart(misspelledSpan); final int misspelledEnd = spannable.getSpanEnd(misspelledSpan); // Make "Add to dictionary" item visible if there is a span with the misspelled flag int addToDictionaryButtonVisibility = View.GONE; if (mMisspelledSpan != null) { final int misspelledStart = spannable.getSpanStart(mMisspelledSpan); final int misspelledEnd = spannable.getSpanEnd(mMisspelledSpan); if (misspelledStart >= 0 && misspelledEnd > misspelledStart) { SuggestionInfo suggestionInfo = mSuggestionInfos[mNumberOfSuggestions]; suggestionInfo.suggestionSpan = misspelledSpan; suggestionInfo.suggestionIndex = ADD_TO_DICTIONARY; suggestionInfo.text.replace(0, suggestionInfo.text.length(), mTextView. getContext().getString(com.android.internal.R.string.addToDictionary)); suggestionInfo.text.setSpan(mHighlightSpan, 0, 0, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); mNumberOfSuggestions++; addToDictionaryButtonVisibility = View.VISIBLE; } } // Delete item SuggestionInfo suggestionInfo = mSuggestionInfos[mNumberOfSuggestions]; suggestionInfo.suggestionSpan = null; suggestionInfo.suggestionIndex = DELETE_TEXT; suggestionInfo.text.replace(0, suggestionInfo.text.length(), mTextView.getContext().getString(com.android.internal.R.string.deleteText)); suggestionInfo.text.setSpan(mHighlightSpan, 0, 0, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); mNumberOfSuggestions++; mAddToDictionaryButton.setVisibility(addToDictionaryButtonVisibility); if (mSuggestionRangeSpan == null) mSuggestionRangeSpan = new SuggestionRangeSpan(); if (underlineColor == 0) { Loading Loading @@ -3017,23 +3065,6 @@ public class Editor { Editable editable = (Editable) mTextView.getText(); SuggestionInfo suggestionInfo = mSuggestionInfos[position]; if (suggestionInfo.suggestionIndex == DELETE_TEXT) { final int spanUnionStart = editable.getSpanStart(mSuggestionRangeSpan); int spanUnionEnd = editable.getSpanEnd(mSuggestionRangeSpan); if (spanUnionStart >= 0 && spanUnionEnd > spanUnionStart) { // Do not leave two adjacent spaces after deletion, or one at beginning of text if (spanUnionEnd < editable.length() && Character.isSpaceChar(editable.charAt(spanUnionEnd)) && (spanUnionStart == 0 || Character.isSpaceChar(editable.charAt(spanUnionStart - 1)))) { spanUnionEnd = spanUnionEnd + 1; } mTextView.deleteText_internal(spanUnionStart, spanUnionEnd); } hideWithCleanUp(); return; } final int spanStart = editable.getSpanStart(suggestionInfo.suggestionSpan); final int spanEnd = editable.getSpanEnd(suggestionInfo.suggestionSpan); if (spanStart < 0 || spanEnd <= spanStart) { Loading @@ -3044,27 +3075,13 @@ public class Editor { final String originalText = TextUtils.substring(editable, spanStart, spanEnd); if (suggestionInfo.suggestionIndex == ADD_TO_DICTIONARY) { Intent intent = new Intent(Settings.ACTION_USER_DICTIONARY_INSERT); intent.putExtra("word", originalText); intent.putExtra("locale", mTextView.getTextServicesLocale().toString()); // Put a listener to replace the original text with a word which the user // modified in a user dictionary dialog. intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); mTextView.getContext().startActivity(intent); // There is no way to know if the word was indeed added. Re-check. // TODO The ExtractEditText should remove the span in the original text instead editable.removeSpan(suggestionInfo.suggestionSpan); Selection.setSelection(editable, spanEnd); updateSpellCheckSpans(spanStart, spanEnd, false); } else { // SuggestionSpans are removed by replace: save them before SuggestionSpan[] suggestionSpans = editable.getSpans(spanStart, spanEnd, final SuggestionSpan[] suggestionSpans = editable.getSpans(spanStart, spanEnd, SuggestionSpan.class); final int length = suggestionSpans.length; int[] suggestionSpansStarts = new int[length]; int[] suggestionSpansEnds = new int[length]; int[] suggestionSpansFlags = new int[length]; final int[] suggestionSpansStarts = new int[length]; final int[] suggestionSpansEnds = new int[length]; final int[] suggestionSpansFlags = new int[length]; for (int i = 0; i < length; i++) { final SuggestionSpan suggestionSpan = suggestionSpans[i]; suggestionSpansStarts[i] = editable.getSpanStart(suggestionSpan); Loading Loading @@ -3092,7 +3109,7 @@ public class Editor { mTextView.getContext(), originalText, suggestionInfo.suggestionIndex); // Swap text content between actual text and Suggestion span String[] suggestions = suggestionInfo.suggestionSpan.getSuggestions(); final String[] suggestions = suggestionInfo.suggestionSpan.getSuggestions(); suggestions[suggestionInfo.suggestionIndex] = originalText; // Restore previous SuggestionSpans Loading @@ -3111,8 +3128,6 @@ public class Editor { // Move cursor at the end of the replaced word final int newCursorPosition = spanEnd + lengthDifference; mTextView.setCursorPosition_internal(newCursorPosition, newCursorPosition); } hideWithCleanUp(); } } Loading
core/java/android/widget/TextView.java +11 −1 Original line number Diff line number Diff line Loading @@ -624,7 +624,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Although these fields are specific to editable text, they are not added to Editor because // they are defined by the TextView's style and are theme-dependent. int mCursorDrawableRes; // These four fields, could be moved to Editor, since we know their default values and we // These six fields, could be moved to Editor, since we know their default values and we // could condition the creation of the Editor to a non standard value. This is however // brittle since the hardcoded values here (such as // com.android.internal.R.drawable.text_select_handle_left) would have to be updated if the Loading @@ -633,6 +633,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int mTextSelectHandleRightRes; int mTextSelectHandleRes; int mTextEditSuggestionItemLayout; int mTextEditSuggestionContainerLayout; int mTextEditSuggestionHighlightStyle; /** * EditText specific data, created on demand when one of the Editor fields is used. Loading Loading @@ -1155,6 +1157,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mTextEditSuggestionItemLayout = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.TextView_textEditSuggestionContainerLayout: mTextEditSuggestionContainerLayout = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.TextView_textEditSuggestionHighlightStyle: mTextEditSuggestionHighlightStyle = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.TextView_textIsSelectable: setTextIsSelectable(a.getBoolean(attr, false)); break; Loading
core/res/res/layout/text_edit_suggestion_container.xml 0 → 100644 +43 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2015 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. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:divider="@null"> <ListView android:id="@+id/suggestionContainer" android:layout_width="match_parent" android:layout_height="wrap_content" android:divider="?android:attr/dividerHorizontal"> <!-- Suggestions will be added here. --> </ListView> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:divider="?android:attr/dividerHorizontal" android:showDividers="middle"> <TextView style="@android:style/Widget.Holo.SuggestionButton" android:id="@+id/addToDictionaryButton" android:text="@string/addToDictionary" /> <TextView style="@android:style/Widget.Holo.SuggestionButton" android:id="@+id/deleteButton" android:text="@string/deleteText" /> </LinearLayout> </LinearLayout>
core/res/res/layout/text_edit_suggestion_container_material.xml 0 → 100644 +42 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2015 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. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:divider="?android:attr/dividerHorizontal" android:showDividers="middle" > <ListView android:id="@+id/suggestionContainer" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white" android:paddingTop="8dip" android:paddingBottom="8dip" android:divider="@null" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView style="@android:style/Widget.Material.SuggestionButton" android:id="@+id/addToDictionaryButton" android:text="@string/addToDictionary" /> <TextView style="@android:style/Widget.Material.SuggestionButton" android:id="@+id/deleteButton" android:text="@string/deleteText" /> </LinearLayout> </LinearLayout>
core/res/res/layout/text_edit_suggestion_item.xml +3 −14 Original line number Diff line number Diff line Loading @@ -15,16 +15,5 @@ --> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingStart="16dip" android:paddingEnd="16dip" android:paddingTop="8dip" android:paddingBottom="8dip" android:layout_gravity="start|center_vertical" android:singleLine="true" android:drawablePadding="8dip" android:ellipsize="marquee" android:textAppearance="?android:attr/textAppearanceMedium" android:textColor="@android:color/dim_foreground_light" /> style="@android:style/Widget.Holo.SuggestionItem" />