diff options
author | Dianne Hackborn <hackbod@google.com> | 2015-07-07 14:29:36 -0700 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2015-07-07 16:47:37 -0700 |
commit | 6f0fdc4167ad906fc7409856e86cd0cc601c3b53 (patch) | |
tree | b3097ee875c68282d9758f74654a61a9705f433e | |
parent | 79d9219c9f9734025837a01a6f8e490d1e0ab57e (diff) | |
download | frameworks_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.txt | 3 | ||||
-rw-r--r-- | api/system-current.txt | 3 | ||||
-rw-r--r-- | core/java/android/app/assist/AssistStructure.java | 89 | ||||
-rw-r--r-- | core/java/android/view/ViewStructure.java | 11 | ||||
-rw-r--r-- | core/java/android/widget/TextView.java | 109 | ||||
-rw-r--r-- | tests/VoiceInteraction/res/layout/main.xml | 9 | ||||
-rw-r--r-- | tests/VoiceInteraction/res/values/strings.xml | 229 | ||||
-rw-r--r-- | tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java | 26 |
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(); } } |