Home | History | Annotate | Download | only in tree
      1 /*
      2  * Copyright (C) 2012 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.uiautomator.tree;
     18 
     19 import java.util.Collections;
     20 import java.util.LinkedHashMap;
     21 import java.util.Map;
     22 import java.util.regex.Matcher;
     23 import java.util.regex.Pattern;
     24 
     25 public class UiNode extends BasicTreeNode {
     26     private static final Pattern BOUNDS_PATTERN = Pattern
     27             .compile("\\[-?(\\d+),-?(\\d+)\\]\\[-?(\\d+),-?(\\d+)\\]");
     28     // use LinkedHashMap to preserve the order of the attributes
     29     private final Map<String, String> mAttributes = new LinkedHashMap<String, String>();
     30     private String mDisplayName = "ShouldNotSeeMe";
     31     private Object[] mCachedAttributesArray;
     32 
     33     public void addAtrribute(String key, String value) {
     34         mAttributes.put(key, value);
     35         updateDisplayName();
     36         if ("bounds".equals(key)) {
     37             updateBounds(value);
     38         }
     39     }
     40 
     41     public Map<String, String> getAttributes() {
     42         return Collections.unmodifiableMap(mAttributes);
     43     }
     44 
     45     /**
     46      * Builds the display name based on attributes of the node
     47      */
     48     private void updateDisplayName() {
     49         String className = mAttributes.get("class");
     50         if (className == null)
     51             return;
     52         String text = mAttributes.get("text");
     53         if (text == null)
     54             return;
     55         String contentDescription = mAttributes.get("content-desc");
     56         if (contentDescription == null)
     57             return;
     58         String index = mAttributes.get("index");
     59         if (index == null)
     60             return;
     61         String bounds = mAttributes.get("bounds");
     62         if (bounds == null) {
     63             return;
     64         }
     65         // shorten the standard class names, otherwise it takes up too much space on UI
     66         className = className.replace("android.widget.", "");
     67         className = className.replace("android.view.", "");
     68         StringBuilder builder = new StringBuilder();
     69         builder.append('(');
     70         builder.append(index);
     71         builder.append(") ");
     72         builder.append(className);
     73         if (!text.isEmpty()) {
     74             builder.append(':');
     75             builder.append(text);
     76         }
     77         if (!contentDescription.isEmpty()) {
     78             builder.append(" {");
     79             builder.append(contentDescription);
     80             builder.append('}');
     81         }
     82         builder.append(' ');
     83         builder.append(bounds);
     84         mDisplayName = builder.toString();
     85     }
     86 
     87     private void updateBounds(String bounds) {
     88         Matcher m = BOUNDS_PATTERN.matcher(bounds);
     89         if (m.matches()) {
     90             x = Integer.parseInt(m.group(1));
     91             y = Integer.parseInt(m.group(2));
     92             width = Integer.parseInt(m.group(3)) - x;
     93             height = Integer.parseInt(m.group(4)) - y;
     94             mHasBounds = true;
     95         } else {
     96             throw new RuntimeException("Invalid bounds: " + bounds);
     97         }
     98     }
     99 
    100     @Override
    101     public String toString() {
    102         return mDisplayName;
    103     }
    104 
    105     public String getAttribute(String key) {
    106         return mAttributes.get(key);
    107     }
    108 
    109     @Override
    110     public Object[] getAttributesArray() {
    111         // this approach means we do not handle the situation where an attribute is added
    112         // after this function is first called. This is currently not a concern because the
    113         // tree is supposed to be readonly
    114         if (mCachedAttributesArray == null) {
    115             mCachedAttributesArray = new Object[mAttributes.size()];
    116             int i = 0;
    117             for (String attr : mAttributes.keySet()) {
    118                 mCachedAttributesArray[i++] = new AttributePair(attr, mAttributes.get(attr));
    119             }
    120         }
    121         return mCachedAttributesArray;
    122     }
    123 }
    124