1cddda72410c992a12db61cef26713b498e31fea4Thanh Le/* 2cddda72410c992a12db61cef26713b498e31fea4Thanh Le * Copyright (C) 2013 DroidDriver committers 3cddda72410c992a12db61cef26713b498e31fea4Thanh Le * 4cddda72410c992a12db61cef26713b498e31fea4Thanh Le * Licensed under the Apache License, Version 2.0 (the "License"); 5cddda72410c992a12db61cef26713b498e31fea4Thanh Le * you may not use this file except in compliance with the License. 6cddda72410c992a12db61cef26713b498e31fea4Thanh Le * You may obtain a copy of the License at 7cddda72410c992a12db61cef26713b498e31fea4Thanh Le * 8cddda72410c992a12db61cef26713b498e31fea4Thanh Le * http://www.apache.org/licenses/LICENSE-2.0 9cddda72410c992a12db61cef26713b498e31fea4Thanh Le * 10cddda72410c992a12db61cef26713b498e31fea4Thanh Le * Unless required by applicable law or agreed to in writing, software 11cddda72410c992a12db61cef26713b498e31fea4Thanh Le * distributed under the License is distributed on an "AS IS" BASIS, 12cddda72410c992a12db61cef26713b498e31fea4Thanh Le * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13cddda72410c992a12db61cef26713b498e31fea4Thanh Le * See the License for the specific language governing permissions and 14cddda72410c992a12db61cef26713b498e31fea4Thanh Le * limitations under the License. 15cddda72410c992a12db61cef26713b498e31fea4Thanh Le */ 16cddda72410c992a12db61cef26713b498e31fea4Thanh Le 17cddda72410c992a12db61cef26713b498e31fea4Thanh Lepackage com.google.android.droiddriver.instrumentation; 18cddda72410c992a12db61cef26713b498e31fea4Thanh Le 1917342a5115d7575d44a99fed9c7032e3ab316dccKevin Jinimport static com.google.android.droiddriver.util.Strings.charSequenceToString; 2007704975f91b729d9be3a13d6a2d3dfdbbd7d426Kevin Jin 2107704975f91b729d9be3a13d6a2d3dfdbbd7d426Kevin Jinimport android.content.res.Resources; 22cddda72410c992a12db61cef26713b498e31fea4Thanh Leimport android.graphics.Rect; 23cddda72410c992a12db61cef26713b498e31fea4Thanh Leimport android.view.View; 24cddda72410c992a12db61cef26713b498e31fea4Thanh Leimport android.view.ViewGroup; 25394b364bfa602c0bbe813229ad099e8075a20c9dKevin Jinimport android.view.accessibility.AccessibilityNodeInfo; 2607704975f91b729d9be3a13d6a2d3dfdbbd7d426Kevin Jinimport android.widget.Checkable; 27cddda72410c992a12db61cef26713b498e31fea4Thanh Leimport android.widget.TextView; 28cddda72410c992a12db61cef26713b498e31fea4Thanh Le 29f50519233078e65a056cff49d7b4989d57c3e750Kevin Jinimport com.google.android.droiddriver.actions.InputInjector; 30f50519233078e65a056cff49d7b4989d57c3e750Kevin Jinimport com.google.android.droiddriver.base.BaseUiElement; 3174676fdd3c8a9e599eddd13bea56898674d9916aKevin Jinimport com.google.android.droiddriver.base.DroidDriverContext; 32dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jinimport com.google.android.droiddriver.exceptions.DroidDriverException; 33dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jinimport com.google.android.droiddriver.finders.Attribute; 3417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jinimport com.google.android.droiddriver.util.Preconditions; 359f81086b618594cb080adf33548edd0c999e388eKevin Jin 3617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jinimport java.util.ArrayList; 3717342a5115d7575d44a99fed9c7032e3ab316dccKevin Jinimport java.util.Collections; 3817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jinimport java.util.EnumMap; 3917342a5115d7575d44a99fed9c7032e3ab316dccKevin Jinimport java.util.HashMap; 40dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jinimport java.util.List; 419f81086b618594cb080adf33548edd0c999e388eKevin Jinimport java.util.Map; 428d19bb634c670a49f7a58636a2a535c86b57d538Kevin Jinimport java.util.concurrent.FutureTask; 4352107c27b6b0f2b0fdfec995784c73746bb95c4eKevin Jin 44cddda72410c992a12db61cef26713b498e31fea4Thanh Le/** 4507401c162d1957a497ab08937f1188cc602f29c6Kevin Jin * A UiElement that is backed by a View. 46cddda72410c992a12db61cef26713b498e31fea4Thanh Le */ 4774676fdd3c8a9e599eddd13bea56898674d9916aKevin Jinpublic class ViewElement extends BaseUiElement<View, ViewElement> { 48dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin private static class SnapshotViewAttributesRunnable implements Runnable { 49dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin private final View view; 5017342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin final Map<Attribute, Object> attribs = new EnumMap<Attribute, Object>(Attribute.class); 51dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin boolean visible; 52dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin Rect visibleBounds; 53dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin List<View> childViews; 54dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin Throwable exception; 559f81086b618594cb080adf33548edd0c999e388eKevin Jin 56dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin private SnapshotViewAttributesRunnable(View view) { 57dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin this.view = view; 58dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 59dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 60dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin @Override 61dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin public void run() { 62dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin try { 63dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin put(Attribute.PACKAGE, view.getContext().getPackageName()); 64dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin put(Attribute.CLASS, getClassName()); 65dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin put(Attribute.TEXT, getText()); 66dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin put(Attribute.CONTENT_DESC, charSequenceToString(view.getContentDescription())); 67dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin put(Attribute.RESOURCE_ID, getResourceId()); 68dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin put(Attribute.CHECKABLE, view instanceof Checkable); 69dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin put(Attribute.CHECKED, isChecked()); 70dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin put(Attribute.CLICKABLE, view.isClickable()); 71dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin put(Attribute.ENABLED, view.isEnabled()); 72dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin put(Attribute.FOCUSABLE, view.isFocusable()); 73dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin put(Attribute.FOCUSED, view.isFocused()); 74dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin put(Attribute.LONG_CLICKABLE, view.isLongClickable()); 75dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin put(Attribute.PASSWORD, isPassword()); 76dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin put(Attribute.SCROLLABLE, isScrollable()); 775c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin if (view instanceof TextView) { 785c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin TextView textView = (TextView) view; 795c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin if (textView.hasSelection()) { 805c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin attribs.put(Attribute.SELECTION_START, textView.getSelectionStart()); 815c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin attribs.put(Attribute.SELECTION_END, textView.getSelectionEnd()); 825c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin } 835c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin } 84dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin put(Attribute.SELECTED, view.isSelected()); 85dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin put(Attribute.BOUNDS, getBounds()); 86dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 87dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin // Order matters as setVisible() depends on setVisibleBounds(). 88dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin this.visibleBounds = getVisibleBounds(); 89dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin // isShown() checks the visibility flag of this view and ancestors; it 90dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin // needs to have the VISIBLE flag as well as non-empty bounds to be 91dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin // visible. 92dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin this.visible = view.isShown() && !visibleBounds.isEmpty(); 93dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin setChildViews(); 94dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } catch (Throwable e) { 95dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin exception = e; 96dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 97dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 98dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 99dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin private void put(Attribute key, Object value) { 100dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin if (value != null) { 101dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin attribs.put(key, value); 102dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 103dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 104dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 105dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin private String getText() { 106dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin if (!(view instanceof TextView)) { 107dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return null; 108dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 109dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return charSequenceToString(((TextView) view).getText()); 110dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 111dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 112dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin private String getClassName() { 113dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin String className = view.getClass().getName(); 114dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return CLASS_NAME_OVERRIDES.containsKey(className) ? CLASS_NAME_OVERRIDES.get(className) 115dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin : className; 116dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 117dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 118dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin private String getResourceId() { 119dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin if (view.getId() != View.NO_ID && view.getResources() != null) { 120dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin try { 121dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return charSequenceToString(view.getResources().getResourceName(view.getId())); 122dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } catch (Resources.NotFoundException nfe) { 123dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin /* ignore */ 124dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 125dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 126dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return null; 127dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 128dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 129dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin private boolean isChecked() { 130dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return view instanceof Checkable && ((Checkable) view).isChecked(); 131dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 132dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 133dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin private boolean isScrollable() { 134dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin // TODO: find a meaningful implementation 135dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return true; 136dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 137dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 138dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin private boolean isPassword() { 139dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin // TODO: find a meaningful implementation 140dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return false; 141dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 142dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 143dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin private Rect getBounds() { 144dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin Rect rect = new Rect(); 145dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin int[] xy = new int[2]; 146dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin view.getLocationOnScreen(xy); 147dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin rect.set(xy[0], xy[1], xy[0] + view.getWidth(), xy[1] + view.getHeight()); 148dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return rect; 149dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 150dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 151dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin private Rect getVisibleBounds() { 152dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin Rect visibleBounds = new Rect(); 153dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin if (!view.isShown() || !view.getGlobalVisibleRect(visibleBounds)) { 154dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin visibleBounds.setEmpty(); 155dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 156dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin int[] xyScreen = new int[2]; 157dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin view.getLocationOnScreen(xyScreen); 158dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin int[] xyWindow = new int[2]; 159dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin view.getLocationInWindow(xyWindow); 160dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin int windowLeft = xyScreen[0] - xyWindow[0]; 161dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin int windowTop = xyScreen[1] - xyWindow[1]; 162dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 163dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin // Bounds are relative to root view; adjust to screen coordinates. 164dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin visibleBounds.offset(windowLeft, windowTop); 165dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return visibleBounds; 166dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 167dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 168dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin private void setChildViews() { 169dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin if (!(view instanceof ViewGroup)) { 170dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return; 171dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 172dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin ViewGroup group = (ViewGroup) view; 173dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin int childCount = group.getChildCount(); 17417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin childViews = new ArrayList<View>(childCount); 175dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin for (int i = 0; i < childCount; i++) { 176dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin View child = group.getChildAt(i); 177dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin if (child != null) { 178dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin childViews.add(child); 179dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 180dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 181dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 182dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 183dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 18417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin private static final Map<String, String> CLASS_NAME_OVERRIDES = new HashMap<String, String>(); 1859f81086b618594cb080adf33548edd0c999e388eKevin Jin 1869f81086b618594cb080adf33548edd0c999e388eKevin Jin /** 1879f81086b618594cb080adf33548edd0c999e388eKevin Jin * Typically users find the class name to use in tests using SDK tool 1889f81086b618594cb080adf33548edd0c999e388eKevin Jin * uiautomatorviewer. This name is returned by 1899f81086b618594cb080adf33548edd0c999e388eKevin Jin * {@link AccessibilityNodeInfo#getClassName}. If the app uses custom View 1909f81086b618594cb080adf33548edd0c999e388eKevin Jin * classes that do not call {@link AccessibilityNodeInfo#setClassName} with 1919f81086b618594cb080adf33548edd0c999e388eKevin Jin * the actual class name, different types of drivers see different class names 1929f81086b618594cb080adf33548edd0c999e388eKevin Jin * (InstrumentationDriver sees the actual class name, while UiAutomationDriver 1939f81086b618594cb080adf33548edd0c999e388eKevin Jin * sees {@link AccessibilityNodeInfo#getClassName}). 1949f81086b618594cb080adf33548edd0c999e388eKevin Jin * <p> 1959f81086b618594cb080adf33548edd0c999e388eKevin Jin * If tests fail with InstrumentationDriver, find the actual class name by 1969f81086b618594cb080adf33548edd0c999e388eKevin Jin * examining app code or by calling 1979f81086b618594cb080adf33548edd0c999e388eKevin Jin * {@link com.google.android.droiddriver.DroidDriver#dumpUiElementTree}, then 1989f81086b618594cb080adf33548edd0c999e388eKevin Jin * call this method in setUp to override it with the class name seen in 1999f81086b618594cb080adf33548edd0c999e388eKevin Jin * uiautomatorviewer. 2005cf5f03c64b65b1f1ecd2140b8d6605ac05b6199Kevin Jin * </p> 2015cf5f03c64b65b1f1ecd2140b8d6605ac05b6199Kevin Jin * A better solution is to use resource-id instead of classname, which is an 2025cf5f03c64b65b1f1ecd2140b8d6605ac05b6199Kevin Jin * implementation detail and subject to change. 2039f81086b618594cb080adf33548edd0c999e388eKevin Jin */ 2049f81086b618594cb080adf33548edd0c999e388eKevin Jin public static void overrideClassName(String actualClassName, String overridingClassName) { 2059f81086b618594cb080adf33548edd0c999e388eKevin Jin CLASS_NAME_OVERRIDES.put(actualClassName, overridingClassName); 2069f81086b618594cb080adf33548edd0c999e388eKevin Jin } 207cddda72410c992a12db61cef26713b498e31fea4Thanh Le 20874676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin private final DroidDriverContext<View, ViewElement> context; 20974676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin private final View view; 210dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin private final Map<Attribute, Object> attributes; 211dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin private final boolean visible; 212dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin private final Rect visibleBounds; 213dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin private final ViewElement parent; 214dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin private final List<ViewElement> children; 215cddda72410c992a12db61cef26713b498e31fea4Thanh Le 21607401c162d1957a497ab08937f1188cc602f29c6Kevin Jin /** 21707401c162d1957a497ab08937f1188cc602f29c6Kevin Jin * A snapshot of all attributes is taken at construction. The attributes of a 21807401c162d1957a497ab08937f1188cc602f29c6Kevin Jin * {@code ViewElement} instance are immutable. If the underlying view is 21907401c162d1957a497ab08937f1188cc602f29c6Kevin Jin * updated, a new {@code ViewElement} instance will be created in 22007401c162d1957a497ab08937f1188cc602f29c6Kevin Jin * {@link com.google.android.droiddriver.DroidDriver#refreshUiElementTree}. 22107401c162d1957a497ab08937f1188cc602f29c6Kevin Jin */ 22274676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin public ViewElement(DroidDriverContext<View, ViewElement> context, View view, ViewElement parent) { 223dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin this.context = Preconditions.checkNotNull(context); 22474676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin this.view = Preconditions.checkNotNull(view); 225dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin this.parent = parent; 226dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin SnapshotViewAttributesRunnable attributesSnapshot = new SnapshotViewAttributesRunnable(view); 227eacc6c8c1f05ad4c6d9ca4c612204240b9dc1d4eKevin Jin context.runOnMainSync(attributesSnapshot); 228dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin if (attributesSnapshot.exception != null) { 229dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin throw new DroidDriverException(attributesSnapshot.exception); 23007704975f91b729d9be3a13d6a2d3dfdbbd7d426Kevin Jin } 23107704975f91b729d9be3a13d6a2d3dfdbbd7d426Kevin Jin 23217342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin attributes = Collections.unmodifiableMap(attributesSnapshot.attribs); 233dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin this.visibleBounds = attributesSnapshot.visibleBounds; 234dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin this.visible = attributesSnapshot.visible; 23517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin if (attributesSnapshot.childViews == null) { 23617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin this.children = null; 23717342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin } else { 23817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin List<ViewElement> children = new ArrayList<ViewElement>(attributesSnapshot.childViews.size()); 23917342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin for (View childView : attributesSnapshot.childViews) { 24074676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin children.add(context.getElement(childView, this)); 24117342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin } 24217342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin this.children = Collections.unmodifiableList(children); 24317342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin } 24407704975f91b729d9be3a13d6a2d3dfdbbd7d426Kevin Jin } 24507704975f91b729d9be3a13d6a2d3dfdbbd7d426Kevin Jin 24607704975f91b729d9be3a13d6a2d3dfdbbd7d426Kevin Jin @Override 247dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin public Rect getVisibleBounds() { 248dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return visibleBounds; 249cddda72410c992a12db61cef26713b498e31fea4Thanh Le } 250cddda72410c992a12db61cef26713b498e31fea4Thanh Le 251cddda72410c992a12db61cef26713b498e31fea4Thanh Le @Override 252cddda72410c992a12db61cef26713b498e31fea4Thanh Le public boolean isVisible() { 253dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return visible; 25407704975f91b729d9be3a13d6a2d3dfdbbd7d426Kevin Jin } 25507704975f91b729d9be3a13d6a2d3dfdbbd7d426Kevin Jin 25607704975f91b729d9be3a13d6a2d3dfdbbd7d426Kevin Jin @Override 257dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin public ViewElement getParent() { 258dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return parent; 2597dde4b200c490587409e0301e58261210e7a5896Tony Wickham } 2607dde4b200c490587409e0301e58261210e7a5896Tony Wickham 2617dde4b200c490587409e0301e58261210e7a5896Tony Wickham @Override 262dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin protected List<ViewElement> getChildren() { 263dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return children; 26452107c27b6b0f2b0fdfec995784c73746bb95c4eKevin Jin } 26552107c27b6b0f2b0fdfec995784c73746bb95c4eKevin Jin 26652107c27b6b0f2b0fdfec995784c73746bb95c4eKevin Jin @Override 267dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin protected Map<Attribute, Object> getAttributes() { 268dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return attributes; 26952107c27b6b0f2b0fdfec995784c73746bb95c4eKevin Jin } 2700d7b67b43f83536708a6ae0330e739744049a48eKevin Jin 2710d7b67b43f83536708a6ae0330e739744049a48eKevin Jin @Override 2729c92f46280cf3943701e75349833c68b584992e2Kevin Jin public InputInjector getInjector() { 27374676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin return context.getDriver().getInjector(); 2740d7b67b43f83536708a6ae0330e739744049a48eKevin Jin } 2758d19bb634c670a49f7a58636a2a535c86b57d538Kevin Jin 2768d19bb634c670a49f7a58636a2a535c86b57d538Kevin Jin @Override 2778d19bb634c670a49f7a58636a2a535c86b57d538Kevin Jin protected void doPerformAndWait(FutureTask<Boolean> futureTask, long timeoutMillis) { 2788d19bb634c670a49f7a58636a2a535c86b57d538Kevin Jin futureTask.run(); 279a6749c6913f014416419850a9fb5235a745fdeb8Kevin Jin context.tryWaitForIdleSync(timeoutMillis); 2808d19bb634c670a49f7a58636a2a535c86b57d538Kevin Jin } 28174676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin 28274676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin @Override 28374676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin public View getRawElement() { 28474676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin return view; 28574676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin } 286cddda72410c992a12db61cef26713b498e31fea4Thanh Le} 287