AssistVisualizer.java revision 6f0fdc4167ad906fc7409856e86cd0cc601c3b53
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.test.voiceinteraction;
18
19import android.annotation.Nullable;
20import android.app.assist.AssistStructure;
21import android.content.Context;
22import android.graphics.Canvas;
23import android.graphics.Matrix;
24import android.graphics.Paint;
25import android.graphics.Rect;
26import android.util.AttributeSet;
27import android.util.Log;
28import android.view.View;
29
30import java.util.ArrayList;
31
32public class AssistVisualizer extends View {
33    static final String TAG = "AssistVisualizer";
34
35    static class TextEntry {
36        final Rect bounds;
37        final int parentLeft, parentTop;
38        final Matrix matrix;
39        final String className;
40        final CharSequence text;
41        final int scrollY;
42        final int[] lineCharOffsets;
43        final int[] lineBaselines;
44
45        TextEntry(AssistStructure.ViewNode node, int parentLeft, int parentTop, Matrix matrix) {
46            int left = parentLeft+node.getLeft();
47            int top = parentTop+node.getTop();
48            bounds = new Rect(left, top, left+node.getWidth(), top+node.getHeight());
49            this.parentLeft = parentLeft;
50            this.parentTop = parentTop;
51            this.matrix = new Matrix(matrix);
52            this.className = node.getClassName();
53            this.text = node.getText() != null ? node.getText() : node.getContentDescription();
54            this.scrollY = node.getScrollY();
55            this.lineCharOffsets = node.getTextLineCharOffsets();
56            this.lineBaselines = node.getTextLineBaselines();
57        }
58    }
59
60    AssistStructure mAssistStructure;
61    final Paint mFramePaint = new Paint();
62    final Paint mFrameBaselinePaint = new Paint();
63    final Paint mFrameNoTransformPaint = new Paint();
64    final ArrayList<Matrix> mMatrixStack = new ArrayList<>();
65    final ArrayList<TextEntry> mTextRects = new ArrayList<>();
66    final int[] mTmpLocation = new int[2];
67
68    public AssistVisualizer(Context context, @Nullable AttributeSet attrs) {
69        super(context, attrs);
70        setWillNotDraw(false);
71        mFramePaint.setColor(0xffff0000);
72        mFramePaint.setStyle(Paint.Style.STROKE);
73        mFramePaint.setStrokeWidth(0);
74        mFrameBaselinePaint.setColor(0xa0b0b000);
75        mFrameBaselinePaint.setStyle(Paint.Style.STROKE);
76        mFrameBaselinePaint.setStrokeWidth(0);
77        float density = getResources().getDisplayMetrics().density;
78        mFramePaint.setShadowLayer(density, density, density, 0xff000000);
79        mFrameNoTransformPaint.setColor(0xff0000ff);
80        mFrameNoTransformPaint.setStyle(Paint.Style.STROKE);
81        mFrameNoTransformPaint.setStrokeWidth(0);
82        mFrameNoTransformPaint.setShadowLayer(density, density, density, 0xff000000);
83    }
84
85    public void setAssistStructure(AssistStructure as) {
86        mAssistStructure = as;
87        mTextRects.clear();
88        final int N = as.getWindowNodeCount();
89        if (N > 0) {
90            for (int i=0; i<N; i++) {
91                AssistStructure.WindowNode windowNode = as.getWindowNodeAt(i);
92                mMatrixStack.clear();
93                Matrix matrix = new Matrix();
94                matrix.setTranslate(windowNode.getLeft(), windowNode.getTop());
95                mMatrixStack.add(matrix);
96                buildTextRects(windowNode.getRootViewNode(), 0, windowNode.getLeft(),
97                        windowNode.getTop());
98            }
99        }
100        Log.d(TAG, "Building text rects in " + this + ": found " + mTextRects.size());
101        invalidate();
102    }
103
104    public void logTree() {
105        if (mAssistStructure != null) {
106            mAssistStructure.dump();
107        }
108    }
109
110    public void logText() {
111        final int N = mTextRects.size();
112        for (int i=0; i<N; i++) {
113            TextEntry te = mTextRects.get(i);
114            Log.d(TAG, "View " + te.className + " " + te.bounds.toShortString()
115                    + " in " + te.parentLeft + "," + te.parentTop
116                    + " matrix=" + te.matrix.toShortString() + ": "
117                    + te.text);
118            if (te.lineCharOffsets != null && te.lineBaselines != null) {
119                final int num = te.lineCharOffsets.length < te.lineBaselines.length
120                        ? te.lineCharOffsets.length : te.lineBaselines.length;
121                for (int j=0; j<num; j++) {
122                    Log.d(TAG, "  Line #" + j + ": offset=" + te.lineCharOffsets[j]
123                            + " baseline=" + te.lineBaselines[j]);
124                }
125            }
126        }
127    }
128
129    public void clearAssistData() {
130        mAssistStructure = null;
131        mTextRects.clear();
132    }
133
134    void buildTextRects(AssistStructure.ViewNode root, int matrixStackIndex,
135            int parentLeft, int parentTop) {
136        if (root.getVisibility() != View.VISIBLE) {
137            return;
138        }
139        Matrix parentMatrix = mMatrixStack.get(matrixStackIndex);
140        matrixStackIndex++;
141        Matrix matrix;
142        if (mMatrixStack.size() > matrixStackIndex) {
143            matrix = mMatrixStack.get(matrixStackIndex);
144            matrix.set(parentMatrix);
145        } else {
146            matrix = new Matrix(parentMatrix);
147            mMatrixStack.add(matrix);
148        }
149        matrix.preTranslate(root.getLeft(), root.getTop());
150        int left = parentLeft + root.getLeft();
151        int top = parentTop + root.getTop();
152        Matrix transform = root.getTransformation();
153        if (transform != null) {
154            matrix.preConcat(transform);
155        }
156        if (root.getText() != null || root.getContentDescription() != null) {
157            TextEntry te = new TextEntry(root, parentLeft, parentTop, matrix);
158            mTextRects.add(te);
159        }
160        final int N = root.getChildCount();
161        if (N > 0) {
162            left -= root.getScrollX();
163            top -= root.getScrollY();
164            matrix.preTranslate(-root.getScrollX(), -root.getScrollY());
165            for (int i=0; i<N; i++) {
166                AssistStructure.ViewNode child = root.getChildAt(i);
167                buildTextRects(child, matrixStackIndex, left, top);
168            }
169        }
170    }
171
172    @Override
173    protected void onDraw(Canvas canvas) {
174        super.onDraw(canvas);
175        getLocationOnScreen(mTmpLocation);
176        final int N = mTextRects.size();
177        Log.d(TAG, "Drawing text rects in " + this + ": found " + mTextRects.size());
178        for (int i=0; i<N; i++) {
179            TextEntry te = mTextRects.get(i);
180            canvas.drawRect(te.bounds.left - mTmpLocation[0], te.bounds.top - mTmpLocation[1],
181                    te.bounds.right - mTmpLocation[0], te.bounds.bottom - mTmpLocation[1],
182                    mFrameNoTransformPaint);
183        }
184        for (int i=0; i<N; i++) {
185            TextEntry te = mTextRects.get(i);
186            canvas.save();
187            canvas.translate(-mTmpLocation[0], -mTmpLocation[1]);
188            canvas.concat(te.matrix);
189            canvas.drawRect(0, 0, te.bounds.right - te.bounds.left, te.bounds.bottom - te.bounds.top,
190                    mFramePaint);
191            if (te.lineBaselines != null) {
192                for (int j=0; j<te.lineBaselines.length; j++) {
193                    canvas.drawLine(0, te.lineBaselines[j] - te.scrollY,
194                            te.bounds.right - te.bounds.left, te.lineBaselines[j] - te.scrollY,
195                            mFrameBaselinePaint);
196                }
197            }
198            canvas.restore();
199        }
200    }
201}
202