Home | History | Annotate | Download | only in voiceinteraction
      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 
     17 package com.android.test.voiceinteraction;
     18 
     19 import android.annotation.Nullable;
     20 import android.app.assist.AssistStructure;
     21 import android.content.Context;
     22 import android.graphics.Canvas;
     23 import android.graphics.Matrix;
     24 import android.graphics.Paint;
     25 import android.graphics.Rect;
     26 import android.util.AttributeSet;
     27 import android.util.Log;
     28 import android.view.View;
     29 
     30 import java.util.ArrayList;
     31 
     32 public 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 float textSize;
     41         final int textColor;
     42         final CharSequence text;
     43         final int scrollY;
     44         final int[] lineCharOffsets;
     45         final int[] lineBaselines;
     46 
     47         TextEntry(AssistStructure.ViewNode node, int parentLeft, int parentTop, Matrix matrix) {
     48             int left = parentLeft+node.getLeft();
     49             int top = parentTop+node.getTop();
     50             bounds = new Rect(left, top, left+node.getWidth(), top+node.getHeight());
     51             this.parentLeft = parentLeft;
     52             this.parentTop = parentTop;
     53             this.matrix = new Matrix(matrix);
     54             this.className = node.getClassName();
     55             this.textSize = node.getTextSize();
     56             this.textColor = node.getTextColor();
     57             this.text = node.getText() != null ? node.getText() : node.getContentDescription();
     58             this.scrollY = node.getScrollY();
     59             this.lineCharOffsets = node.getTextLineCharOffsets();
     60             this.lineBaselines = node.getTextLineBaselines();
     61         }
     62     }
     63 
     64     AssistStructure mAssistStructure;
     65     final Paint mFramePaint = new Paint();
     66     final Paint mFrameBaselinePaint = new Paint();
     67     final Paint mFrameNoTransformPaint = new Paint();
     68     final ArrayList<Matrix> mMatrixStack = new ArrayList<>();
     69     final ArrayList<TextEntry> mTextRects = new ArrayList<>();
     70     final int[] mTmpLocation = new int[2];
     71 
     72     public AssistVisualizer(Context context, @Nullable AttributeSet attrs) {
     73         super(context, attrs);
     74         setWillNotDraw(false);
     75         mFramePaint.setColor(0xffff0000);
     76         mFramePaint.setStyle(Paint.Style.STROKE);
     77         mFramePaint.setStrokeWidth(0);
     78         mFrameBaselinePaint.setColor(0xa0b0b000);
     79         mFrameBaselinePaint.setStyle(Paint.Style.STROKE);
     80         mFrameBaselinePaint.setStrokeWidth(0);
     81         float density = getResources().getDisplayMetrics().density;
     82         mFramePaint.setShadowLayer(density, density, density, 0xff000000);
     83         mFrameNoTransformPaint.setColor(0xff0000ff);
     84         mFrameNoTransformPaint.setStyle(Paint.Style.STROKE);
     85         mFrameNoTransformPaint.setStrokeWidth(0);
     86         mFrameNoTransformPaint.setShadowLayer(density, density, density, 0xff000000);
     87     }
     88 
     89     public void setAssistStructure(AssistStructure as) {
     90         mAssistStructure = as;
     91         mTextRects.clear();
     92         final int N = as.getWindowNodeCount();
     93         if (N > 0) {
     94             for (int i=0; i<N; i++) {
     95                 AssistStructure.WindowNode windowNode = as.getWindowNodeAt(i);
     96                 mMatrixStack.clear();
     97                 Matrix matrix = new Matrix();
     98                 matrix.setTranslate(windowNode.getLeft(), windowNode.getTop());
     99                 mMatrixStack.add(matrix);
    100                 buildTextRects(windowNode.getRootViewNode(), 0, windowNode.getLeft(),
    101                         windowNode.getTop());
    102             }
    103         }
    104         Log.d(TAG, "Building text rects in " + this + ": found " + mTextRects.size());
    105         invalidate();
    106     }
    107 
    108     public void logTree() {
    109         if (mAssistStructure != null) {
    110             mAssistStructure.dump(true);
    111         }
    112     }
    113 
    114     public void logText() {
    115         final int N = mTextRects.size();
    116         for (int i=0; i<N; i++) {
    117             TextEntry te = mTextRects.get(i);
    118             Log.d(TAG, "View " + te.className + " " + te.bounds.toShortString()
    119                     + " in " + te.parentLeft + "," + te.parentTop
    120                     + " matrix=" + te.matrix.toShortString()
    121                     + " size=" + te.textSize + " color=#" + Integer.toHexString(te.textColor)
    122                     + ": "
    123                     + te.text);
    124             if (te.lineCharOffsets != null && te.lineBaselines != null) {
    125                 final int num = te.lineCharOffsets.length < te.lineBaselines.length
    126                         ? te.lineCharOffsets.length : te.lineBaselines.length;
    127                 for (int j=0; j<num; j++) {
    128                     Log.d(TAG, "  Line #" + j + ": offset=" + te.lineCharOffsets[j]
    129                             + " baseline=" + te.lineBaselines[j]);
    130                 }
    131             }
    132         }
    133     }
    134 
    135     public void clearAssistData() {
    136         mAssistStructure = null;
    137         mTextRects.clear();
    138     }
    139 
    140     void buildTextRects(AssistStructure.ViewNode root, int matrixStackIndex,
    141             int parentLeft, int parentTop) {
    142         if (root.getVisibility() != View.VISIBLE) {
    143             return;
    144         }
    145         Matrix parentMatrix = mMatrixStack.get(matrixStackIndex);
    146         matrixStackIndex++;
    147         Matrix matrix;
    148         if (mMatrixStack.size() > matrixStackIndex) {
    149             matrix = mMatrixStack.get(matrixStackIndex);
    150             matrix.set(parentMatrix);
    151         } else {
    152             matrix = new Matrix(parentMatrix);
    153             mMatrixStack.add(matrix);
    154         }
    155         matrix.preTranslate(root.getLeft(), root.getTop());
    156         int left = parentLeft + root.getLeft();
    157         int top = parentTop + root.getTop();
    158         Matrix transform = root.getTransformation();
    159         if (transform != null) {
    160             matrix.preConcat(transform);
    161         }
    162         if (root.getText() != null || root.getContentDescription() != null) {
    163             TextEntry te = new TextEntry(root, parentLeft, parentTop, matrix);
    164             mTextRects.add(te);
    165         }
    166         final int N = root.getChildCount();
    167         if (N > 0) {
    168             left -= root.getScrollX();
    169             top -= root.getScrollY();
    170             matrix.preTranslate(-root.getScrollX(), -root.getScrollY());
    171             for (int i=0; i<N; i++) {
    172                 AssistStructure.ViewNode child = root.getChildAt(i);
    173                 buildTextRects(child, matrixStackIndex, left, top);
    174             }
    175         }
    176     }
    177 
    178     @Override
    179     protected void onDraw(Canvas canvas) {
    180         super.onDraw(canvas);
    181         getLocationOnScreen(mTmpLocation);
    182         final int N = mTextRects.size();
    183         Log.d(TAG, "Drawing text rects in " + this + ": found " + mTextRects.size());
    184         for (int i=0; i<N; i++) {
    185             TextEntry te = mTextRects.get(i);
    186             canvas.drawRect(te.bounds.left - mTmpLocation[0], te.bounds.top - mTmpLocation[1],
    187                     te.bounds.right - mTmpLocation[0], te.bounds.bottom - mTmpLocation[1],
    188                     mFrameNoTransformPaint);
    189         }
    190         for (int i=0; i<N; i++) {
    191             TextEntry te = mTextRects.get(i);
    192             canvas.save();
    193             canvas.translate(-mTmpLocation[0], -mTmpLocation[1]);
    194             canvas.concat(te.matrix);
    195             canvas.drawRect(0, 0, te.bounds.right - te.bounds.left, te.bounds.bottom - te.bounds.top,
    196                     mFramePaint);
    197             if (te.lineBaselines != null) {
    198                 for (int j=0; j<te.lineBaselines.length; j++) {
    199                     canvas.drawLine(0, te.lineBaselines[j] - te.scrollY,
    200                             te.bounds.right - te.bounds.left, te.lineBaselines[j] - te.scrollY,
    201                             mFrameBaselinePaint);
    202                 }
    203             }
    204             canvas.restore();
    205         }
    206     }
    207 }
    208