Make suggestion window style material.
To make suggestion window style Material, this CL does following things:
1. Introduce LinearLayout to be able to split suggestion item and menu.
Currently suggestion menus, "Add to Dictionary" and "Delete" buttons
are children of ListView. It is necessary to introduce LinearLayout
and move these two menus from ListView to this LinearLayout to have a
divider between suggestion items and menus.
2. Extract suggestion window layout definition from Java.
Currently almost all layout of suggestion popup window is done by
Editor.java. By extracting this logic from Java and move it to XML
files, it becomes easy to support both Holo and Material theme.
3. Introduce Material Design.
Suggestion window should respect the running application's theme since
suggestion window is shown as the part of the application.
This patch introduces Material themed suggestion window, and at the
same time, the old window is also kept as the Holo themed suggestion
window.
Bug: 15347319
Change-Id: Ieccea12db95c0a040b38680ae794b1cf6971736f
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 2983053..9a911bc 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2634,8 +2634,11 @@
@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;
@@ -2644,7 +2647,10 @@
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) {
@@ -2684,17 +2690,73 @@
@Override
protected void initContentView() {
- ListView listView = new ListView(mTextView.getContext());
- mSuggestionsAdapter = new SuggestionAdapter();
- listView.setAdapter(mSuggestionsAdapter);
- listView.setOnItemClickListener(this);
- mContentView = listView;
+ final LayoutInflater inflater = (LayoutInflater) mTextView.getContext().
+ getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ final LinearLayout linearLayout = (LinearLayout) inflater.inflate(
+ mTextView.mTextEditSuggestionContainerLayout, null);
- // Inflate the suggestion items once and for all. + 2 for add to dictionary and delete
- mSuggestionInfos = new SuggestionInfo[MAX_NUMBER_SUGGESTIONS + 2];
+ final ListView suggestionListView = (ListView) linearLayout.findViewById(
+ com.android.internal.R.id.suggestionContainer);
+
+ mSuggestionsAdapter = new SuggestionAdapter();
+ suggestionListView.setAdapter(mSuggestionsAdapter);
+ suggestionListView.setOnItemClickListener(this);
+
+ // 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() {
@@ -2753,14 +2815,6 @@
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;
}
}
@@ -2843,6 +2897,14 @@
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),
@@ -2878,6 +2940,7 @@
for (final SuggestionInfo info : mSuggestionInfos) {
info.clear();
}
+ mMisspelledSpan = null;
hide();
}
@@ -2893,7 +2956,7 @@
int spanUnionStart = mTextView.getText().length();
int spanUnionEnd = 0;
- SuggestionSpan misspelledSpan = null;
+ mMisspelledSpan = null;
int underlineColor = 0;
for (int spanIndex = 0; spanIndex < nbSpans; spanIndex++) {
@@ -2904,7 +2967,7 @@
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
@@ -2949,31 +3012,16 @@
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) {
@@ -3017,23 +3065,6 @@
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) {
@@ -3044,75 +3075,59 @@
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,
- SuggestionSpan.class);
- final int length = suggestionSpans.length;
- int[] suggestionSpansStarts = new int[length];
- int[] suggestionSpansEnds = new int[length];
- int[] suggestionSpansFlags = new int[length];
- for (int i = 0; i < length; i++) {
- final SuggestionSpan suggestionSpan = suggestionSpans[i];
- suggestionSpansStarts[i] = editable.getSpanStart(suggestionSpan);
- suggestionSpansEnds[i] = editable.getSpanEnd(suggestionSpan);
- suggestionSpansFlags[i] = editable.getSpanFlags(suggestionSpan);
+ // SuggestionSpans are removed by replace: save them before
+ final SuggestionSpan[] suggestionSpans = editable.getSpans(spanStart, spanEnd,
+ SuggestionSpan.class);
+ final int length = suggestionSpans.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);
+ suggestionSpansEnds[i] = editable.getSpanEnd(suggestionSpan);
+ suggestionSpansFlags[i] = editable.getSpanFlags(suggestionSpan);
- // Remove potential misspelled flags
- int suggestionSpanFlags = suggestionSpan.getFlags();
- if ((suggestionSpanFlags & SuggestionSpan.FLAG_MISSPELLED) > 0) {
- suggestionSpanFlags &= ~SuggestionSpan.FLAG_MISSPELLED;
- suggestionSpanFlags &= ~SuggestionSpan.FLAG_EASY_CORRECT;
- suggestionSpan.setFlags(suggestionSpanFlags);
- }
+ // Remove potential misspelled flags
+ int suggestionSpanFlags = suggestionSpan.getFlags();
+ if ((suggestionSpanFlags & SuggestionSpan.FLAG_MISSPELLED) > 0) {
+ suggestionSpanFlags &= ~SuggestionSpan.FLAG_MISSPELLED;
+ suggestionSpanFlags &= ~SuggestionSpan.FLAG_EASY_CORRECT;
+ suggestionSpan.setFlags(suggestionSpanFlags);
}
-
- final int suggestionStart = suggestionInfo.suggestionStart;
- final int suggestionEnd = suggestionInfo.suggestionEnd;
- final String suggestion = suggestionInfo.text.subSequence(
- suggestionStart, suggestionEnd).toString();
- mTextView.replaceText_internal(spanStart, spanEnd, suggestion);
-
- // Notify source IME of the suggestion pick. Do this before
- // swaping texts.
- suggestionInfo.suggestionSpan.notifySelection(
- mTextView.getContext(), originalText, suggestionInfo.suggestionIndex);
-
- // Swap text content between actual text and Suggestion span
- String[] suggestions = suggestionInfo.suggestionSpan.getSuggestions();
- suggestions[suggestionInfo.suggestionIndex] = originalText;
-
- // Restore previous SuggestionSpans
- final int lengthDifference = suggestion.length() - (spanEnd - spanStart);
- for (int i = 0; i < length; i++) {
- // Only spans that include the modified region make sense after replacement
- // Spans partially included in the replaced region are removed, there is no
- // way to assign them a valid range after replacement
- if (suggestionSpansStarts[i] <= spanStart &&
- suggestionSpansEnds[i] >= spanEnd) {
- mTextView.setSpan_internal(suggestionSpans[i], suggestionSpansStarts[i],
- suggestionSpansEnds[i] + lengthDifference, suggestionSpansFlags[i]);
- }
- }
-
- // Move cursor at the end of the replaced word
- final int newCursorPosition = spanEnd + lengthDifference;
- mTextView.setCursorPosition_internal(newCursorPosition, newCursorPosition);
}
+ final int suggestionStart = suggestionInfo.suggestionStart;
+ final int suggestionEnd = suggestionInfo.suggestionEnd;
+ final String suggestion = suggestionInfo.text.subSequence(
+ suggestionStart, suggestionEnd).toString();
+ mTextView.replaceText_internal(spanStart, spanEnd, suggestion);
+
+ // Notify source IME of the suggestion pick. Do this before
+ // swaping texts.
+ suggestionInfo.suggestionSpan.notifySelection(
+ mTextView.getContext(), originalText, suggestionInfo.suggestionIndex);
+
+ // Swap text content between actual text and Suggestion span
+ final String[] suggestions = suggestionInfo.suggestionSpan.getSuggestions();
+ suggestions[suggestionInfo.suggestionIndex] = originalText;
+
+ // Restore previous SuggestionSpans
+ final int lengthDifference = suggestion.length() - (spanEnd - spanStart);
+ for (int i = 0; i < length; i++) {
+ // Only spans that include the modified region make sense after replacement
+ // Spans partially included in the replaced region are removed, there is no
+ // way to assign them a valid range after replacement
+ if (suggestionSpansStarts[i] <= spanStart &&
+ suggestionSpansEnds[i] >= spanEnd) {
+ mTextView.setSpan_internal(suggestionSpans[i], suggestionSpansStarts[i],
+ suggestionSpansEnds[i] + lengthDifference, suggestionSpansFlags[i]);
+ }
+ }
+
+ // Move cursor at the end of the replaced word
+ final int newCursorPosition = spanEnd + lengthDifference;
+ mTextView.setCursorPosition_internal(newCursorPosition, newCursorPosition);
hideWithCleanUp();
}
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index eaf4fe2..476c6a2 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -624,7 +624,7 @@
// 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
@@ -633,6 +633,8 @@
int mTextSelectHandleRightRes;
int mTextSelectHandleRes;
int mTextEditSuggestionItemLayout;
+ int mTextEditSuggestionContainerLayout;
+ int mTextEditSuggestionHighlightStyle;
/**
* EditText specific data, created on demand when one of the Editor fields is used.
@@ -1155,6 +1157,14 @@
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;
diff --git a/core/res/res/layout/text_edit_suggestion_container.xml b/core/res/res/layout/text_edit_suggestion_container.xml
new file mode 100644
index 0000000..04eca8f
--- /dev/null
+++ b/core/res/res/layout/text_edit_suggestion_container.xml
@@ -0,0 +1,43 @@
+<?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>
diff --git a/core/res/res/layout/text_edit_suggestion_container_material.xml b/core/res/res/layout/text_edit_suggestion_container_material.xml
new file mode 100644
index 0000000..d0e2467
--- /dev/null
+++ b/core/res/res/layout/text_edit_suggestion_container_material.xml
@@ -0,0 +1,42 @@
+<?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>
diff --git a/core/res/res/layout/text_edit_suggestion_item.xml b/core/res/res/layout/text_edit_suggestion_item.xml
index a965ddd..9dcbf2e 100644
--- a/core/res/res/layout/text_edit_suggestion_item.xml
+++ b/core/res/res/layout/text_edit_suggestion_item.xml
@@ -4,9 +4,9 @@
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.
@@ -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" />
diff --git a/core/res/res/layout/text_edit_suggestion_item_material.xml b/core/res/res/layout/text_edit_suggestion_item_material.xml
new file mode 100644
index 0000000..0443a97
--- /dev/null
+++ b/core/res/res/layout/text_edit_suggestion_item_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@android:style/Widget.Material.SuggestionItem" />
+
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 04f4fc2..f9f8162 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -942,6 +942,10 @@
<!-- Layout of the TextView item that will populate the suggestion popup window. -->
<attr name="textEditSuggestionItemLayout" format="reference" />
+ <!-- Layout of the container of the suggestion popup window. -->
+ <attr name="textEditSuggestionContainerLayout" format="reference" />
+ <!-- Text appearance of the focused words to be replaced by suggested word. -->
+ <attr name="textEditSuggestionHighlightStyle" format="reference" />
<!-- Theme to use for dialogs spawned from this theme. -->
<attr name="dialogTheme" format="reference" />
@@ -4411,6 +4415,10 @@
<!-- Layout of the TextView item that will populate the suggestion popup window. -->
<attr name="textEditSuggestionItemLayout" />
+ <!-- Layout of the container of the suggestion popup window. -->
+ <attr name="textEditSuggestionContainerLayout" />
+ <!-- Style of the highlighted string in the suggestion popup window. -->
+ <attr name="textEditSuggestionHighlightStyle" />
<!-- Reference to a drawable that will be drawn under the insertion cursor. -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index eb99077..e6f279d 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -496,6 +496,8 @@
<item name="textEditSidePasteWindowLayout">?attr/textEditSidePasteWindowLayout</item>
<item name="textEditSideNoPasteWindowLayout">?attr/textEditSideNoPasteWindowLayout</item>
<item name="textEditSuggestionItemLayout">?attr/textEditSuggestionItemLayout</item>
+ <item name="textEditSuggestionContainerLayout">?attr/textEditSuggestionContainerLayout</item>
+ <item name="textEditSuggestionHighlightStyle">?attr/textEditSuggestionHighlightStyle</item>
<item name="textCursorDrawable">?attr/textCursorDrawable</item>
<item name="breakStrategy">high_quality</item>
<item name="hyphenationFrequency">normal</item>
@@ -954,10 +956,10 @@
<item name="dividerPadding">6dip</item>
</style>
- <style name="TextAppearance.SuggestionHighlight">
- <item name="textSize">18sp</item>
- <item name="textColor">@color/suggestion_highlight_text</item>
- </style>
+ <style name="TextAppearance.SuggestionHighlight">
+ <item name="textSize">18sp</item>
+ <item name="textColor">@color/suggestion_highlight_text</item>
+ </style>
<!-- Preference Styles -->
diff --git a/core/res/res/values/styles_holo.xml b/core/res/res/values/styles_holo.xml
index 6861069..841afd8 100644
--- a/core/res/res/values/styles_holo.xml
+++ b/core/res/res/values/styles_holo.xml
@@ -1176,4 +1176,27 @@
<style name="Widget.Holo.Light.FastScroll" parent="Widget.Holo.FastScroll" />
+ <style name="Widget.Holo.SuggestionItem" parent="@android:attr/textAppearanceMedium">
+ <item name="background">@color/white</item>
+ <item name="drawablePadding">8dip</item>
+ <item name="ellipsize">marquee</item>
+ <item name="gravity">start|center_vertical</item>
+ <item name="layout_gravity">start|center_vertical</item>
+ <item name="layout_height">wrap_content</item>
+ <item name="layout_width">match_parent</item>
+ <item name="paddingBottom">8dip</item>
+ <item name="paddingEnd">16dip</item>
+ <item name="paddingStart">16dip</item>
+ <item name="paddingTop">8dip</item>
+ <item name="singleLine">true</item>
+ <item name="textSize">18sp</item>
+ <item name="textColor">@color/black</item>
+ </style>
+
+ <style name="TextAppearance.Holo.SuggestionHighlight" parent="TextAppearance.SuggestionHighlight" />
+
+ <style name="Widget.Holo.SuggestionButton" parent="Widget.Holo.SuggestionItem">
+ <item name="background">#E9E9E9</item>
+ <item name="textColor">@color/black</item>
+ </style>
</resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 4b2a451..adcb79b 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -988,6 +988,47 @@
<item name="contentDescription">@string/media_route_button_content_description</item>
</style>
+ <style name="Widget.Material.SuggestionItem" parent="@android:style/TextAppearance.Material.Body1">
+ <item name="background">@color/white</item>
+ <item name="alpha">.87</item>
+ <item name="textColor">@color/black</item>
+ <item name="drawablePadding">8dip</item>
+ <item name="ellipsize">marquee</item>
+ <item name="gravity">start|center_vertical</item>
+ <item name="layout_gravity">start|center_vertical</item>
+ <item name="layout_height">48dip</item>
+ <item name="layout_width">match_parent</item>
+ <item name="paddingBottom">8dip</item>
+ <item name="paddingEnd">16dip</item>
+ <item name="paddingStart">16dip</item>
+ <item name="paddingTop">8dip</item>
+ <item name="singleLine">true</item>
+ <item name="textSize">14sp</item>
+ </style>
+
+ <style name="TextAppearance.Material.TextSuggestionHighlight" parent="Widget.Material.SuggestionItem">
+ <item name="textColor">#009688</item>
+ </style>
+
+ <style name="Widget.Material.SuggestionButton" parent="@android:style/TextAppearance.Material.Button">
+ <item name="background">@color/white</item>
+ <item name="alpha">.87</item>
+ <item name="textColor">#009688</item>
+ <item name="drawablePadding">8dip</item>
+ <item name="ellipsize">marquee</item>
+ <item name="gravity">start|center_vertical</item>
+ <item name="layout_gravity">start|center_vertical</item>
+ <item name="layout_height">48dip</item>
+ <item name="layout_width">match_parent</item>
+ <item name="paddingBottom">8dip</item>
+ <item name="paddingEnd">16dip</item>
+ <item name="paddingStart">16dip</item>
+ <item name="paddingTop">8dip</item>
+ <item name="singleLine">true</item>
+ <item name="textAllCaps">true</item>
+ <item name="textSize">14sp</item>
+ </style>
+
<!-- Light widget styles -->
<style name="Widget.Material.Light" parent="Widget.Material"/>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 476b879..5d2b5b3 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2341,4 +2341,9 @@
<java-symbol type="string" name="config_packagedKeyboardName" />
<java-symbol type="string" name="default_notification_topic_label" />
+
+ <!-- EditText suggestion popup. -->
+ <java-symbol type="id" name="suggestionContainer" />
+ <java-symbol type="id" name="addToDictionaryButton" />
+ <java-symbol type="id" name="deleteButton" />
</resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index c230645..d56674a 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -256,8 +256,6 @@
<item name="textEditNoPasteWindowLayout">@layout/text_edit_no_paste_window</item>
<item name="textEditSidePasteWindowLayout">@layout/text_edit_side_paste_window</item>
<item name="textEditSideNoPasteWindowLayout">@layout/text_edit_side_no_paste_window</item>
- <item name="textSuggestionsWindowStyle">@style/Widget.TextSuggestionsPopupWindow</item>
- <item name="textEditSuggestionItemLayout">@layout/text_edit_suggestion_item</item>
<item name="textCursorDrawable">@null</item>
<!-- Widget styles -->
diff --git a/core/res/res/values/themes_holo.xml b/core/res/res/values/themes_holo.xml
index 701d0ef..d9599b3 100644
--- a/core/res/res/values/themes_holo.xml
+++ b/core/res/res/values/themes_holo.xml
@@ -133,6 +133,11 @@
<item name="textAppearanceLargePopupMenu">@style/TextAppearance.Holo.Widget.PopupMenu.Large</item>
<item name="textAppearanceSmallPopupMenu">@style/TextAppearance.Holo.Widget.PopupMenu.Small</item>
+ <item name="textEditSuggestionItemLayout">@layout/text_edit_suggestion_item</item>
+ <item name="textEditSuggestionContainerLayout">@layout/text_edit_suggestion_container</item>
+ <item name="textEditSuggestionHighlightStyle">@style/TextAppearance.Holo.SuggestionHighlight</item>
+ <item name="textSuggestionsWindowStyle">@style/Widget.Holo.TextSuggestionsPopupWindow</item>
+
<!-- Button styles -->
<item name="buttonStyle">@style/Widget.Holo.Button</item>
@@ -247,7 +252,6 @@
<item name="textSelectHandleRight">@drawable/text_select_handle_right</item>
<item name="textSelectHandle">@drawable/text_select_handle_middle</item>
<item name="textSelectHandleWindowStyle">@style/Widget.Holo.TextSelectHandle</item>
- <item name="textSuggestionsWindowStyle">@style/Widget.Holo.TextSuggestionsPopupWindow</item>
<item name="textCursorDrawable">@drawable/text_cursor_holo_dark</item>
<!-- Widget styles -->
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 59dfc92..a5b8476 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -221,6 +221,8 @@
<item name="textSelectHandleRight">@drawable/text_select_handle_right_material</item>
<item name="textSelectHandle">@drawable/text_select_handle_middle_material</item>
<item name="textSelectHandleWindowStyle">@style/Widget.Material.TextSelectHandle</item>
+ <item name="textEditSuggestionItemLayout">@layout/text_edit_suggestion_item_material</item>
+ <item name="textEditSuggestionContainerLayout">@layout/text_edit_suggestion_container_material</item>
<item name="textSuggestionsWindowStyle">@style/Widget.Material.TextSuggestionsPopupWindow</item>
<item name="textCursorDrawable">@drawable/text_cursor_material</item>
@@ -580,9 +582,14 @@
<item name="textSelectHandleRight">@drawable/text_select_handle_right_material</item>
<item name="textSelectHandle">@drawable/text_select_handle_middle_material</item>
<item name="textSelectHandleWindowStyle">@style/Widget.Material.TextSelectHandle</item>
- <item name="textSuggestionsWindowStyle">@style/Widget.Material.Light.TextSuggestionsPopupWindow</item>
<item name="textCursorDrawable">@drawable/text_cursor_material</item>
+ <!-- Suggestion window attributes -->
+ <item name="textEditSuggestionItemLayout">@layout/text_edit_suggestion_item_material</item>
+ <item name="textEditSuggestionContainerLayout">@layout/text_edit_suggestion_container_material</item>
+ <item name="textEditSuggestionHighlightStyle">@style/TextAppearance.Material.TextSuggestionHighlight</item>
+ <item name="textSuggestionsWindowStyle">@style/Widget.Material.Light.TextSuggestionsPopupWindow</item>
+
<!-- Widget styles -->
<item name="absListViewStyle">@style/Widget.Material.Light.AbsListView</item>
<item name="autoCompleteTextViewStyle">@style/Widget.Material.Light.AutoCompleteTextView</item>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 6903b7b..5177836 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -138,7 +138,8 @@
<activity android:name="android.widget.TextViewActivity"
android:label="TextViewActivity"
- android:screenOrientation="portrait">
+ android:screenOrientation="portrait"
+ android:theme="@android:style/Theme.Material.Light">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
diff --git a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
index 3ce45e9..3d8fe69 100644
--- a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
+++ b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
@@ -17,6 +17,7 @@
package android.widget;
import android.app.Activity;
+import android.content.res.TypedArray;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.Selection;
@@ -25,6 +26,7 @@
import android.text.TextPaint;
import android.text.style.SuggestionSpan;
import android.text.style.TextAppearanceSpan;
+import android.view.View;
import com.android.frameworks.coretests.R;
@@ -54,8 +56,12 @@
final int multiWordSpanStart = 0;
final int multiWordSpanEnd = 11;
- TextAppearanceSpan expectedSpan = new TextAppearanceSpan(activity,
- android.R.style.TextAppearance_SuggestionHighlight);
+ TypedArray array = activity.obtainStyledAttributes(com.android.internal.R.styleable.Theme);
+ int id = array.getResourceId(
+ com.android.internal.R.styleable.Theme_textEditSuggestionHighlightStyle, 0);
+ array.recycle();
+
+ TextAppearanceSpan expectedSpan = new TextAppearanceSpan(activity, id);
TextPaint tmpTp = new TextPaint();
expectedSpan.updateDrawState(tmpTp);
final int expectedHighlightTextColor = tmpTp.getColor();
@@ -89,6 +95,7 @@
// | abc *Def* ghi |
// | *ABC DEF GHI* |
// | *Abc Def Ghi* |
+ // -----------------
// | DELETE |
// -----------------
// *XX* means that XX is highlighted.
@@ -99,13 +106,15 @@
editor.getSuggestionsPopupWindowForTesting();
assertNotNull(popupWindow);
- ListView listView = (ListView) popupWindow.getContentViewForTesting();
+ LinearLayout linearLayout = (LinearLayout) popupWindow.getContentViewForTesting();
+ assertNotNull(linearLayout);
+
+ ListView listView = (ListView)linearLayout.findViewById(
+ com.android.internal.R.id.suggestionContainer);
assertNotNull(listView);
int childNum = listView.getChildCount();
- // +1 for "DELETE" command.
- assertEquals(singleWordCandidates.length + multiWordCandidates.length + 1,
- childNum);
+ assertEquals(singleWordCandidates.length + multiWordCandidates.length, childNum);
for (int i = 0; i < singleWordCandidates.length; ++i) {
TextView textView = (TextView) listView.getChildAt(i);
@@ -156,6 +165,10 @@
assertEquals(multiWordSpanStart, spanned.getSpanStart(taSpan[0]));
assertEquals(multiWordSpanEnd, spanned.getSpanEnd(taSpan[0]));
}
+
+ TextView deleteButton = (TextView)linearLayout.findViewById(
+ com.android.internal.R.id.deleteButton);
+ assertEquals(View.VISIBLE, deleteButton.getWindowVisibility());
}
};