summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2015-07-07 14:29:36 -0700
committerDianne Hackborn <hackbod@google.com>2015-07-07 16:47:37 -0700
commit6f0fdc4167ad906fc7409856e86cd0cc601c3b53 (patch)
treeb3097ee875c68282d9758f74654a61a9705f433e
parent79d9219c9f9734025837a01a6f8e490d1e0ab57e (diff)
downloadframeworks_base-6f0fdc4167ad906fc7409856e86cd0cc601c3b53.zip
frameworks_base-6f0fdc4167ad906fc7409856e86cd0cc601c3b53.tar.gz
frameworks_base-6f0fdc4167ad906fc7409856e86cd0cc601c3b53.tar.bz2
Implement better handling of text in assist.
TextView is now much smarter about the text it reports, limiting it to what is visible (plus a bit more). Also add a facility for it to report where the lines of text are, both as offsets in the text string and their baselines on screen. Part of fixing issue #22328792: Fix scalability issues in AssistStructure Change-Id: Idddb8c3a3331355f381e2d4af06d520fe7c7ce8e
-rw-r--r--api/current.txt3
-rw-r--r--api/system-current.txt3
-rw-r--r--core/java/android/app/assist/AssistStructure.java89
-rw-r--r--core/java/android/view/ViewStructure.java11
-rw-r--r--core/java/android/widget/TextView.java109
-rw-r--r--tests/VoiceInteraction/res/layout/main.xml9
-rw-r--r--tests/VoiceInteraction/res/values/strings.xml229
-rw-r--r--tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java26
8 files changed, 443 insertions, 36 deletions
diff --git a/api/current.txt b/api/current.txt
index bb1070d..1152256 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5848,6 +5848,8 @@ package android.app.assist {
method public java.lang.CharSequence getText();
method public int getTextBackgroundColor();
method public int getTextColor();
+ method public int[] getTextLineBaselines();
+ method public int[] getTextLineCharOffsets();
method public int getTextSelectionEnd();
method public int getTextSelectionStart();
method public float getTextSize();
@@ -36969,6 +36971,7 @@ package android.view {
method public abstract void setSelected(boolean);
method public abstract void setText(java.lang.CharSequence);
method public abstract void setText(java.lang.CharSequence, int, int);
+ method public abstract void setTextLines(int[], int[]);
method public abstract void setTextStyle(float, int, int, int);
method public abstract void setTransformation(android.graphics.Matrix);
method public abstract void setVisibility(int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 5861560..b09af08 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5980,6 +5980,8 @@ package android.app.assist {
method public java.lang.CharSequence getText();
method public int getTextBackgroundColor();
method public int getTextColor();
+ method public int[] getTextLineBaselines();
+ method public int[] getTextLineCharOffsets();
method public int getTextSelectionEnd();
method public int getTextSelectionStart();
method public float getTextSize();
@@ -39259,6 +39261,7 @@ package android.view {
method public abstract void setSelected(boolean);
method public abstract void setText(java.lang.CharSequence);
method public abstract void setText(java.lang.CharSequence, int, int);
+ method public abstract void setTextLines(int[], int[]);
method public abstract void setTextStyle(float, int, int, int);
method public abstract void setTransformation(android.graphics.Matrix);
method public abstract void setVisibility(int);
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 3429b6e..73c551f 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -61,37 +61,53 @@ public class AssistStructure implements Parcelable {
final static class ViewNodeText {
CharSequence mText;
- int mTextSelectionStart;
- int mTextSelectionEnd;
- int mTextColor;
- int mTextBackgroundColor;
float mTextSize;
int mTextStyle;
+ int mTextColor = ViewNode.TEXT_COLOR_UNDEFINED;
+ int mTextBackgroundColor = ViewNode.TEXT_COLOR_UNDEFINED;
+ int mTextSelectionStart;
+ int mTextSelectionEnd;
+ int[] mLineCharOffsets;
+ int[] mLineBaselines;
String mHint;
ViewNodeText() {
}
- ViewNodeText(Parcel in) {
+ boolean isSimple() {
+ return mTextBackgroundColor == ViewNode.TEXT_COLOR_UNDEFINED
+ && mTextSelectionStart == 0 && mTextSelectionEnd == 0
+ && mLineCharOffsets == null && mLineBaselines == null && mHint == null;
+ }
+
+ ViewNodeText(Parcel in, boolean simple) {
mText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
- mTextSelectionStart = in.readInt();
- mTextSelectionEnd = in.readInt();
- mTextColor = in.readInt();
- mTextBackgroundColor = in.readInt();
mTextSize = in.readFloat();
mTextStyle = in.readInt();
- mHint = in.readString();
+ mTextColor = in.readInt();
+ if (!simple) {
+ mTextBackgroundColor = in.readInt();
+ mTextSelectionStart = in.readInt();
+ mTextSelectionEnd = in.readInt();
+ mLineCharOffsets = in.createIntArray();
+ mLineBaselines = in.createIntArray();
+ mHint = in.readString();
+ }
}
- void writeToParcel(Parcel out) {
+ void writeToParcel(Parcel out, boolean simple) {
TextUtils.writeToParcel(mText, out, 0);
- out.writeInt(mTextSelectionStart);
- out.writeInt(mTextSelectionEnd);
- out.writeInt(mTextColor);
- out.writeInt(mTextBackgroundColor);
out.writeFloat(mTextSize);
out.writeInt(mTextStyle);
- out.writeString(mHint);
+ out.writeInt(mTextColor);
+ if (!simple) {
+ out.writeInt(mTextBackgroundColor);
+ out.writeInt(mTextSelectionStart);
+ out.writeInt(mTextSelectionEnd);
+ out.writeIntArray(mLineCharOffsets);
+ out.writeIntArray(mLineBaselines);
+ out.writeString(mHint);
+ }
}
}
@@ -252,9 +268,10 @@ public class AssistStructure implements Parcelable {
static final int FLAGS_HAS_LARGE_COORDS = 0x04000000;
static final int FLAGS_HAS_CONTENT_DESCRIPTION = 0x02000000;
static final int FLAGS_HAS_TEXT = 0x01000000;
- static final int FLAGS_HAS_EXTRAS = 0x00800000;
- static final int FLAGS_HAS_ID = 0x00400000;
- static final int FLAGS_HAS_CHILDREN = 0x00200000;
+ static final int FLAGS_HAS_COMPLEX_TEXT = 0x00800000;
+ static final int FLAGS_HAS_EXTRAS = 0x00400000;
+ static final int FLAGS_HAS_ID = 0x00200000;
+ static final int FLAGS_HAS_CHILDREN = 0x00100000;
static final int FLAGS_ALL_CONTROL = 0xfff00000;
int mFlags;
@@ -316,7 +333,7 @@ public class AssistStructure implements Parcelable {
mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
}
if ((flags&FLAGS_HAS_TEXT) != 0) {
- mText = new ViewNodeText(in);
+ mText = new ViewNodeText(in, (flags&FLAGS_HAS_COMPLEX_TEXT) == 0);
}
if ((flags&FLAGS_HAS_EXTRAS) != 0) {
mExtras = in.readBundle();
@@ -356,6 +373,9 @@ public class AssistStructure implements Parcelable {
}
if (mText != null) {
flags |= FLAGS_HAS_TEXT;
+ if (!mText.isSimple()) {
+ flags |= FLAGS_HAS_COMPLEX_TEXT;
+ }
}
if (mExtras != null) {
flags |= FLAGS_HAS_EXTRAS;
@@ -403,7 +423,7 @@ public class AssistStructure implements Parcelable {
TextUtils.writeToParcel(mContentDescription, out, 0);
}
if ((flags&FLAGS_HAS_TEXT) != 0) {
- mText.writeToParcel(out);
+ mText.writeToParcel(out, (flags&FLAGS_HAS_COMPLEX_TEXT) == 0);
}
if ((flags&FLAGS_HAS_EXTRAS) != 0) {
out.writeBundle(mExtras);
@@ -703,6 +723,26 @@ public class AssistStructure implements Parcelable {
}
/**
+ * Return per-line offsets into the text returned by {@link #getText()}. Each entry
+ * in the array is a formatted line of text, and the value it contains is the offset
+ * into the text string where that line starts. May return null if there is no line
+ * information.
+ */
+ public int[] getTextLineCharOffsets() {
+ return mText != null ? mText.mLineCharOffsets : null;
+ }
+
+ /**
+ * Return per-line baselines into the text returned by {@link #getText()}. Each entry
+ * in the array is a formatted line of text, and the value it contains is the baseline
+ * where that text appears in the view. May return null if there is no line
+ * information.
+ */
+ public int[] getTextLineBaselines() {
+ return mText != null ? mText.mLineBaselines : null;
+ }
+
+ /**
* Return additional hint text associated with the node; this is typically used with
* a node that takes user input, describing to the user what the input means.
*/
@@ -901,6 +941,13 @@ public class AssistStructure implements Parcelable {
}
@Override
+ public void setTextLines(int[] charOffsets, int[] baselines) {
+ ViewNodeText t = getNodeText();
+ t.mLineCharOffsets = charOffsets;
+ t.mLineBaselines = baselines;
+ }
+
+ @Override
public void setHint(CharSequence hint) {
getNodeText().mHint = hint != null ? hint.toString() : null;
}
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 794622a..2e4ba74 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -178,6 +178,17 @@ public abstract class ViewStructure {
public abstract void setTextStyle(float size, int fgColor, int bgColor, int style);
/**
+ * Set line information for test that was previously supplied through
+ * {@link #setText(CharSequence)}. This provides the line breaking of the text as it
+ * is shown on screen. This function takes ownership of the provided arrays; you should
+ * not make further modification to them.
+ *
+ * @param charOffsets The offset in to {@link #setText} where a line starts.
+ * @param baselines The baseline where the line is drawn on screen.
+ */
+ public abstract void setTextLines(int[] charOffsets, int[] baselines);
+
+ /**
* Set optional hint text associated with this view; this is for example the text that is
* shown by an EditText when it is empty to indicate to the user the kind of text to input.
*/
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 6b8abab..7da901e 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -46,7 +46,6 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
-import android.inputmethodservice.ExtractEditText;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Parcel;
@@ -116,6 +115,7 @@ import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewParent;
import android.view.ViewStructure;
import android.view.ViewConfiguration;
import android.view.ViewDebug;
@@ -5819,6 +5819,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return super.getBaseline();
}
+ return getBaselineOffset() + mLayout.getLineBaseline(0);
+ }
+
+ int getBaselineOffset() {
int voffset = 0;
if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) {
voffset = getVerticalOffset(true);
@@ -5828,7 +5832,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
voffset -= getOpticalInsets().top;
}
- return getExtendedPaddingTop() + voffset + mLayout.getLineBaseline(0);
+ return getExtendedPaddingTop() + voffset;
}
/**
@@ -8763,7 +8767,79 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final boolean isPassword = hasPasswordTransformationMethod()
|| isPasswordInputType(getInputType());
if (!isPassword) {
- structure.setText(getText(), getSelectionStart(), getSelectionEnd());
+ if (mLayout == null) {
+ assumeLayout();
+ }
+ Layout layout = mLayout;
+ final int lineCount = layout.getLineCount();
+ if (lineCount <= 1) {
+ // Simple case: this is a single line.
+ structure.setText(getText(), getSelectionStart(), getSelectionEnd());
+ } else {
+ // Complex case: multi-line, could be scrolled or within a scroll container
+ // so some lines are not visible.
+ final int[] tmpCords = new int[2];
+ getLocationInWindow(tmpCords);
+ final int topWindowLocation = tmpCords[1];
+ View root = this;
+ ViewParent viewParent = getParent();
+ while (viewParent instanceof View) {
+ root = (View) viewParent;
+ viewParent = root.getParent();
+ }
+ final int windowHeight = root.getHeight();
+ final int topLine;
+ final int bottomLine;
+ if (topWindowLocation >= 0) {
+ // The top of the view is fully within its window; start text at line 0.
+ topLine = getLineAtCoordinateUnclamped(0);
+ bottomLine = getLineAtCoordinateUnclamped(windowHeight-1);
+ } else {
+ // The top of hte window has scrolled off the top of the window; figure out
+ // the starting line for this.
+ topLine = getLineAtCoordinateUnclamped(-topWindowLocation);
+ bottomLine = getLineAtCoordinateUnclamped(windowHeight-1-topWindowLocation);
+ }
+ // We want to return some contextual lines above/below the lines that are
+ // actually visible.
+ int expandedTopLine = topLine - (bottomLine-topLine)/2;
+ if (expandedTopLine < 0) {
+ expandedTopLine = 0;
+ }
+ int expandedBottomLine = bottomLine + (bottomLine-topLine)/2;
+ if (expandedBottomLine >= lineCount) {
+ expandedBottomLine = lineCount-1;
+ }
+ // Convert lines into character offsets.
+ int expandedTopChar = layout.getLineStart(expandedTopLine);
+ int expandedBottomChar = layout.getLineEnd(expandedBottomLine);
+ // Take into account selection -- if there is a selection, we need to expand
+ // the text we are returning to include that selection.
+ final int selStart = getSelectionStart();
+ final int selEnd = getSelectionEnd();
+ if (selStart < selEnd) {
+ if (selStart < expandedTopChar) {
+ expandedTopChar = selStart;
+ }
+ if (selEnd > expandedBottomChar) {
+ expandedBottomChar = selEnd;
+ }
+ }
+ // Get the text and trim it to the range we are reporting.
+ CharSequence text = getText();
+ if (expandedTopChar > 0 || expandedBottomChar < text.length()) {
+ text = text.subSequence(expandedTopChar, expandedBottomChar);
+ }
+ structure.setText(text, selStart-expandedTopChar, selEnd-expandedTopChar);
+ final int[] lineOffsets = new int[bottomLine-topLine+1];
+ final int[] lineBaselines = new int[bottomLine-topLine+1];
+ final int baselineOffset = getBaselineOffset();
+ for (int i=topLine; i<=bottomLine; i++) {
+ lineOffsets[i-topLine] = layout.getLineStart(i);
+ lineBaselines[i-topLine] = layout.getLineBaseline(i) + baselineOffset;
+ }
+ structure.setTextLines(lineOffsets, lineBaselines);
+ }
// Extract style information that applies to the TextView as a whole.
int style = 0;
@@ -9196,24 +9272,25 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* If provided, this ActionMode.Callback will be used to create the ActionMode when text
* selection is initiated in this View.
*
- * The standard implementation populates the menu with a subset of Select All, Cut, Copy,
+ * <p>The standard implementation populates the menu with a subset of Select All, Cut, Copy,
* Paste, Replace and Share actions, depending on what this View supports.
*
- * A custom implementation can add new entries in the default menu in its
- * {@link android.view.ActionMode.Callback#onPrepareActionMode(ActionMode, Menu)} method. The
- * default actions can also be removed from the menu using
+ * <p>A custom implementation can add new entries in the default menu in its
+ * {@link android.view.ActionMode.Callback#onPrepareActionMode(ActionMode, android.view.Menu)}
+ * method. The default actions can also be removed from the menu using
* {@link android.view.Menu#removeItem(int)} and passing {@link android.R.id#selectAll},
* {@link android.R.id#cut}, {@link android.R.id#copy}, {@link android.R.id#paste},
* {@link android.R.id#replaceText} or {@link android.R.id#shareText} ids as parameters.
*
- * Returning false from
- * {@link android.view.ActionMode.Callback#onCreateActionMode(ActionMode, Menu)} will prevent
- * the action mode from being started.
+ * <p>Returning false from
+ * {@link android.view.ActionMode.Callback#onCreateActionMode(ActionMode, android.view.Menu)}
+ * will prevent the action mode from being started.
*
- * Action click events should be handled by the custom implementation of
- * {@link android.view.ActionMode.Callback#onActionItemClicked(ActionMode, MenuItem)}.
+ * <p>Action click events should be handled by the custom implementation of
+ * {@link android.view.ActionMode.Callback#onActionItemClicked(ActionMode,
+ * android.view.MenuItem)}.
*
- * Note that text selection mode is not started when a TextView receives focus and the
+ * <p>Note that text selection mode is not started when a TextView receives focus and the
* {@link android.R.attr#selectAllOnFocus} flag has been set. The content is highlighted in
* that case, to allow for quick replacement.
*/
@@ -9444,6 +9521,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return getLayout().getLineForVertical((int) y);
}
+ int getLineAtCoordinateUnclamped(float y) {
+ y -= getTotalPaddingTop();
+ y += getScrollY();
+ return getLayout().getLineForVertical((int) y);
+ }
+
int getOffsetAtCoordinate(int line, float x) {
x = convertToLocalHorizontalCoordinate(x);
return getLayout().getOffsetForHorizontal(line, x);
diff --git a/tests/VoiceInteraction/res/layout/main.xml b/tests/VoiceInteraction/res/layout/main.xml
index 092d37d..0f968eb 100644
--- a/tests/VoiceInteraction/res/layout/main.xml
+++ b/tests/VoiceInteraction/res/layout/main.xml
@@ -30,10 +30,17 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="64dp"
- android:paddingBottom="64dp"
android:text="@string/asyncStructure"
/>
+ <EditText android:id="@+id/text"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:paddingTop="64dp"
+ android:paddingBottom="64dp"
+ android:text="@string/largetext" />
+
</LinearLayout>
diff --git a/tests/VoiceInteraction/res/values/strings.xml b/tests/VoiceInteraction/res/values/strings.xml
index cf660e6..ab39f99 100644
--- a/tests/VoiceInteraction/res/values/strings.xml
+++ b/tests/VoiceInteraction/res/values/strings.xml
@@ -30,4 +30,233 @@
<string name="cancelVoice">Cancel</string>
<string name="jumpOut">Jump out</string>
+ <string name="largetext">This is a bunch of text that we will use to show how we handle it
+when reporting it for assist data. We need many many lines of text, like\n
+this\n
+and\n
+this other\n
+one\n
+two\n
+three\n
+four\n
+five\n
+six\n
+seven\n
+eight\n
+nine\n
+ten\n
+eleven\n
+twelve\n
+thirteen\n
+fourteen\n
+fifteen\n
+sixteen\n
+seventeen\n
+eighteen\n
+nineteen\n
+twenty\n
+<big><big><big>So shaken as we are, so wan with care,\n
+Find we a time for frighted peace to pant,\n</big>
+And breathe short-winded accents of new broils\n
+To be commenced in strands afar remote.\n</big>
+No more the thirsty entrance of this soil\n
+Shall daub her lips with her own children\'s blood;\n</big>
+<b>Nor more shall trenching war channel her fields,\n
+Nor bruise her flowerets with the armed hoofs\n</b>
+<i>Of hostile paces: those opposed eyes,\n
+Which, like the meteors of a troubled heaven,\n</i>
+All of one nature, of one substance bred,\n
+Did lately meet in the intestine shock\n
+And furious close of civil butchery\n
+Shall now, in mutual well-beseeming ranks,\n
+March all one way and be no more opposed\n
+Against acquaintance, kindred and allies:\n
+The edge of war, like an ill-sheathed knife,\n
+No more shall cut his master. Therefore, friends,\n
+As far as to the sepulchre of Christ,\n
+Whose soldier now, under whose blessed cross\n
+We are impressed and engaged to fight,\n
+Forthwith a power of English shall we levy;\n
+Whose arms were moulded in their mothers\' womb\n
+To chase these pagans in those holy fields\n
+Over whose acres walk\'d those blessed feet\n
+Which fourteen hundred years ago were nail\'d\n
+For our advantage on the bitter cross.\n
+But this our purpose now is twelve month old,\n
+And bootless \'tis to tell you we will go:\n
+Therefore we meet not now. Then let me hear\n
+Of you, my gentle cousin Westmoreland,\n
+What yesternight our council did decree\n
+In forwarding this dear expedience.\n
+\n
+Hear him but reason in divinity,\n
+And all-admiring with an inward wish\n
+You would desire the king were made a prelate:\n
+Hear him debate of commonwealth affairs,\n
+You would say it hath been all in all his study:\n
+List his discourse of war, and you shall hear\n
+A fearful battle render\'d you in music:\n
+Turn him to any cause of policy,\n
+The Gordian knot of it he will unloose,\n
+Familiar as his garter: that, when he speaks,\n
+The air, a charter\'d libertine, is still,\n
+And the mute wonder lurketh in men\'s ears,\n
+To steal his sweet and honey\'d sentences;\n
+So that the art and practic part of life\n
+Must be the mistress to this theoric:\n
+Which is a wonder how his grace should glean it,\n
+Since his addiction was to courses vain,\n
+His companies unletter\'d, rude and shallow,\n
+His hours fill\'d up with riots, banquets, sports,\n
+And never noted in him any study,\n
+Any retirement, any sequestration\n
+From open haunts and popularity.\n
+\n
+I come no more to make you laugh: things now,\n
+That bear a weighty and a serious brow,\n
+Sad, high, and working, full of state and woe,\n
+Such noble scenes as draw the eye to flow,\n
+e now present. Those that can pity, here\n
+May, if they think it well, let fall a tear;\n
+The subject will deserve it. Such as give\n
+Their money out of hope they may believe,\n
+May here find truth too. Those that come to see\n
+Only a show or two, and so agree\n
+The play may pass, if they be still and willing,\n
+I\'ll undertake may see away their shilling\n
+Richly in two short hours. Only they\n
+That come to hear a merry bawdy play,\n
+A noise of targets, or to see a fellow\n
+In a long motley coat guarded with yellow,\n
+Will be deceived; for, gentle hearers, know,\n
+To rank our chosen truth with such a show\n
+As fool and fight is, beside forfeiting\n
+Our own brains, and the opinion that we bring,\n
+To make that only true we now intend,\n
+Will leave us never an understanding friend.\n
+Therefore, for goodness\' sake, and as you are known\n
+The first and happiest hearers of the town,\n
+Be sad, as we would make ye: think ye see\n
+The very persons of our noble story\n
+As they were living; think you see them great,\n
+And follow\'d with the general throng and sweat\n
+Of thousand friends; then in a moment, see\n
+How soon this mightiness meets misery:\n
+And, if you can be merry then, I\'ll say\n
+A man may weep upon his wedding-day.\n
+\n
+<big>First, heaven be the record to my speech!\n
+In the devotion of a subject\'s love,\n</big>
+<b>Tendering the precious safety of my prince,\n
+And free from other misbegotten hate,\n</b>
+Come I appellant to this princely presence.\n
+Now, Thomas Mowbray, do I turn to thee,\n
+And mark my greeting well; for what I speak\n
+My body shall make good upon this earth,\n
+Or my divine soul answer it in heaven.\n
+Thou art a traitor and a miscreant,\n
+Too good to be so and too bad to live,\n
+Since the more fair and crystal is the sky,\n
+The uglier seem the clouds that in it fly.\n
+Once more, the more to aggravate the note,\n
+With a foul traitor\'s name stuff I thy throat;\n
+And wish, so please my sovereign, ere I move,\n
+What my tongue speaks my right drawn sword may prove.\n
+\n
+Now is the winter of our discontent\n
+Made glorious summer by this sun of York;\n
+And all the clouds that lour\'d upon our house\n
+In the deep bosom of the ocean buried.\n
+Now are our brows bound with victorious wreaths;\n
+Our bruised arms hung up for monuments;\n
+Our stern alarums changed to merry meetings,\n
+Our dreadful marches to delightful measures.\n
+Grim-visaged war hath smooth\'d his wrinkled front;\n
+And now, instead of mounting barded steeds\n
+To fright the souls of fearful adversaries,\n
+He capers nimbly in a lady\'s chamber\n
+To the lascivious pleasing of a lute.\n
+But I, that am not shaped for sportive tricks,\n
+Nor made to court an amorous looking-glass;\n
+I, that am rudely stamp\'d, and want love\'s majesty\n
+To strut before a wanton ambling nymph;\n
+I, that am curtail\'d of this fair proportion,\n
+Cheated of feature by dissembling nature,\n
+Deformed, unfinish\'d, sent before my time\n
+Into this breathing world, scarce half made up,\n
+And that so lamely and unfashionable\n
+That dogs bark at me as I halt by them;\n
+Why, I, in this weak piping time of peace,\n
+Have no delight to pass away the time,\n
+Unless to spy my shadow in the sun\n
+And descant on mine own deformity:\n
+And therefore, since I cannot prove a lover,\n
+To entertain these fair well-spoken days,\n
+I am determined to prove a villain\n
+And hate the idle pleasures of these days.\n
+Plots have I laid, inductions dangerous,\n
+By drunken prophecies, libels and dreams,\n
+To set my brother Clarence and the king\n
+In deadly hate the one against the other:\n
+And if King Edward be as true and just\n
+As I am subtle, false and treacherous,\n
+This day should Clarence closely be mew\'d up,\n
+About a prophecy, which says that \'G\'\n
+Of Edward\'s heirs the murderer shall be.\n
+Dive, thoughts, down to my soul: here\n
+Clarence comes.\n
+\n
+To bait fish withal: if it will feed nothing else,\n
+it will feed my revenge. He hath disgraced me, and\n
+hindered me half a million; laughed at my losses,\n
+mocked at my gains, scorned my nation, thwarted my\n
+bargains, cooled my friends, heated mine\n
+enemies; and what\'s his reason? I am a Jew. Hath\n
+not a Jew eyes? hath not a Jew hands, organs,\n
+dimensions, senses, affections, passions? fed with\n
+the same food, hurt with the same weapons, subject\n
+to the same diseases, healed by the same means,\n
+warmed and cooled by the same winter and summer, as\n
+a Christian is? If you prick us, do we not bleed?\n
+if you tickle us, do we not laugh? if you poison\n
+us, do we not die? and if you wrong us, shall we not\n
+revenge? If we are like you in the rest, we will\n
+resemble you in that. If a Jew wrong a Christian,\n
+what is his humility? Revenge. If a Christian\n
+wrong a Jew, what should his sufferance be by\n
+Christian example? Why, revenge. The villany you\n
+teach me, I will execute, and it shall go hard but I\n
+will better the instruction.\n
+\n
+Virtue! a fig! \'tis in ourselves that we are thus\n
+or thus. Our bodies are our gardens, to the which\n
+our wills are gardeners: so that if we will plant\n
+nettles, or sow lettuce, set hyssop and weed up\n
+thyme, supply it with one gender of herbs, or\n
+distract it with many, either to have it sterile\n
+with idleness, or manured with industry, why, the\n
+power and corrigible authority of this lies in our\n
+wills. If the balance of our lives had not one\n
+scale of reason to poise another of sensuality, the\n
+blood and baseness of our natures would conduct us\n
+to most preposterous conclusions: but we have\n
+reason to cool our raging motions, our carnal\n
+stings, our unbitted lusts, whereof I take this that\n
+you call love to be a sect or scion.\n
+\n
+Blow, winds, and crack your cheeks! rage! blow!\n
+You cataracts and hurricanoes, spout\n
+Till you have drench\'d our steeples, drown\'d the cocks!\n
+You sulphurous and thought-executing fires,\n
+Vaunt-couriers to oak-cleaving thunderbolts,\n
+Singe my white head! And thou, all-shaking thunder,\n
+Smite flat the thick rotundity o\' the world!\n
+Crack nature\'s moulds, an germens spill at once,\n
+That make ingrateful man!
+5...\n
+4...\n
+3...\n
+2...\n
+1...\n
+BEEEEEEEEEEEEEEEEEP!</string>
</resources>
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java
index 339755f..005a483 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java
@@ -38,6 +38,9 @@ public class AssistVisualizer extends View {
final Matrix matrix;
final String className;
final CharSequence text;
+ final int scrollY;
+ final int[] lineCharOffsets;
+ final int[] lineBaselines;
TextEntry(AssistStructure.ViewNode node, int parentLeft, int parentTop, Matrix matrix) {
int left = parentLeft+node.getLeft();
@@ -48,16 +51,19 @@ public class AssistVisualizer extends View {
this.matrix = new Matrix(matrix);
this.className = node.getClassName();
this.text = node.getText() != null ? node.getText() : node.getContentDescription();
+ this.scrollY = node.getScrollY();
+ this.lineCharOffsets = node.getTextLineCharOffsets();
+ this.lineBaselines = node.getTextLineBaselines();
}
}
AssistStructure mAssistStructure;
final Paint mFramePaint = new Paint();
+ final Paint mFrameBaselinePaint = new Paint();
final Paint mFrameNoTransformPaint = new Paint();
final ArrayList<Matrix> mMatrixStack = new ArrayList<>();
final ArrayList<TextEntry> mTextRects = new ArrayList<>();
final int[] mTmpLocation = new int[2];
- final float[] mTmpMatrixPoint = new float[2];
public AssistVisualizer(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
@@ -65,6 +71,9 @@ public class AssistVisualizer extends View {
mFramePaint.setColor(0xffff0000);
mFramePaint.setStyle(Paint.Style.STROKE);
mFramePaint.setStrokeWidth(0);
+ mFrameBaselinePaint.setColor(0xa0b0b000);
+ mFrameBaselinePaint.setStyle(Paint.Style.STROKE);
+ mFrameBaselinePaint.setStrokeWidth(0);
float density = getResources().getDisplayMetrics().density;
mFramePaint.setShadowLayer(density, density, density, 0xff000000);
mFrameNoTransformPaint.setColor(0xff0000ff);
@@ -106,6 +115,14 @@ public class AssistVisualizer extends View {
+ " in " + te.parentLeft + "," + te.parentTop
+ " matrix=" + te.matrix.toShortString() + ": "
+ te.text);
+ if (te.lineCharOffsets != null && te.lineBaselines != null) {
+ final int num = te.lineCharOffsets.length < te.lineBaselines.length
+ ? te.lineCharOffsets.length : te.lineBaselines.length;
+ for (int j=0; j<num; j++) {
+ Log.d(TAG, " Line #" + j + ": offset=" + te.lineCharOffsets[j]
+ + " baseline=" + te.lineBaselines[j]);
+ }
+ }
}
}
@@ -171,6 +188,13 @@ public class AssistVisualizer extends View {
canvas.concat(te.matrix);
canvas.drawRect(0, 0, te.bounds.right - te.bounds.left, te.bounds.bottom - te.bounds.top,
mFramePaint);
+ if (te.lineBaselines != null) {
+ for (int j=0; j<te.lineBaselines.length; j++) {
+ canvas.drawLine(0, te.lineBaselines[j] - te.scrollY,
+ te.bounds.right - te.bounds.left, te.lineBaselines[j] - te.scrollY,
+ mFrameBaselinePaint);
+ }
+ }
canvas.restore();
}
}