UiAutomationElement.java revision 0858f7292b7e7f32c25662d853c9d8ed8db1403f
1/*
2 * Copyright (C) 2013 DroidDriver committers
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.google.android.droiddriver.uiautomation;
18
19import static com.google.android.droiddriver.util.TextUtils.charSequenceToString;
20
21import android.app.UiAutomation.AccessibilityEventFilter;
22import android.graphics.Rect;
23import android.util.Log;
24import android.view.accessibility.AccessibilityEvent;
25import android.view.accessibility.AccessibilityNodeInfo;
26
27import com.google.android.droiddriver.InputInjector;
28import com.google.android.droiddriver.actions.ClickAction;
29import com.google.android.droiddriver.base.AbstractUiElement;
30import com.google.android.droiddriver.util.Logs;
31import com.google.common.base.Objects;
32import com.google.common.base.Preconditions;
33
34import java.util.concurrent.TimeoutException;
35
36/**
37 * A UiElement that is backed by the UiAutomation object.
38 */
39public class UiAutomationElement extends AbstractUiElement {
40  // UiAutomator magic const
41  private static long WAIT_FOR_ACTION_ACKNOWLEDGMENT = 3 * 1000;
42
43  private final UiAutomationContext context;
44  private final AccessibilityNodeInfo node;
45
46  public UiAutomationElement(UiAutomationContext context, AccessibilityNodeInfo node) {
47    this.context = Preconditions.checkNotNull(context);
48    this.node = Preconditions.checkNotNull(node);
49  }
50
51  private static class AnyEventFilter implements AccessibilityEventFilter {
52    private final int mask;
53
54    private AnyEventFilter(int mask) {
55      this.mask = mask;
56    }
57
58    public static AnyEventFilter of(int mask) {
59      return new AnyEventFilter(mask);
60    }
61
62    @Override
63    public boolean accept(AccessibilityEvent t) {
64      return (t.getEventType() & mask) != 0;
65    }
66  }
67
68  // TODO: Address synchronization issues for other actions as well. I've seen
69  // problems with DrawerLayout (side panel) -- clicking has no effect. This
70  // seems to solve it, but I'm not sure executeAndWaitForEvent is the right
71  // solution, or simply because it adds delay.
72  @Override
73  public void click() {
74    try {
75      context.getUiAutomation().executeAndWaitForEvent(
76          new Runnable() {
77            @Override
78            public void run() {
79              perform(ClickAction.SINGLE);
80            }
81          },
82          AnyEventFilter.of(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
83              | AccessibilityEvent.TYPE_VIEW_SELECTED), WAIT_FOR_ACTION_ACKNOWLEDGMENT);
84    } catch (TimeoutException e) {
85      Log.w(Logs.TAG, "executeAndWaitForEvent timed out");
86    }
87  }
88
89  @Override
90  public String getText() {
91    return charSequenceToString(node.getText());
92  }
93
94  @Override
95  public String getContentDescription() {
96    return charSequenceToString(node.getContentDescription());
97  }
98
99  @Override
100  public String getClassName() {
101    return charSequenceToString(node.getClassName());
102  }
103
104  @Override
105  public String getResourceId() {
106    return charSequenceToString(node.getViewIdResourceName());
107  }
108
109  @Override
110  public String getPackageName() {
111    return charSequenceToString(node.getPackageName());
112  }
113
114  @Override
115  public InputInjector getInjector() {
116    return context.getInjector();
117  }
118
119  @Override
120  public boolean isVisible() {
121    return node.isVisibleToUser();
122  }
123
124  @Override
125  public boolean isCheckable() {
126    return node.isCheckable();
127  }
128
129  @Override
130  public boolean isChecked() {
131    return node.isChecked();
132  }
133
134  @Override
135  public boolean isClickable() {
136    return node.isClickable();
137  }
138
139  @Override
140  public boolean isEnabled() {
141    return node.isEnabled();
142  }
143
144  @Override
145  public boolean isFocusable() {
146    return node.isFocusable();
147  }
148
149  @Override
150  public boolean isFocused() {
151    return node.isFocused();
152  }
153
154  @Override
155  public boolean isScrollable() {
156    return node.isScrollable();
157  }
158
159  @Override
160  public boolean isLongClickable() {
161    return node.isLongClickable();
162  }
163
164  @Override
165  public boolean isPassword() {
166    return node.isPassword();
167  }
168
169  @Override
170  public boolean isSelected() {
171    return node.isSelected();
172  }
173
174  @Override
175  public Rect getBounds() {
176    Rect rect = new Rect();
177    node.getBoundsInScreen(rect);
178    return rect;
179  }
180
181  @Override
182  public String toString() {
183    return Objects.toStringHelper(this).add("node", node).toString();
184  }
185
186  @Override
187  public int getChildCount() {
188    return node.getChildCount();
189  }
190
191  @Override
192  public UiAutomationElement getChild(int index) {
193    AccessibilityNodeInfo child = node.getChild(index);
194    return child == null ? null : context.getUiElement(child);
195  }
196
197  @Override
198  public UiAutomationElement getParent() {
199    AccessibilityNodeInfo parent = node.getParent();
200    return parent == null ? null : context.getUiElement(parent);
201  }
202}
203