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 174b31201b5a2dbf8036da5a8d089a68a39cc1dc44Kevin Jinpackage io.appium.droiddriver.base; 18cddda72410c992a12db61cef26713b498e31fea4Thanh Le 19dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jinimport android.graphics.Rect; 20082c7925e5109092ff31d7021f1f9bb6daabee12Kevin Jinimport android.os.Build; 21082c7925e5109092ff31d7021f1f9bb6daabee12Kevin Jinimport android.text.TextUtils; 22337fafc3b2ede7dbd1be4d1ed0bbfdfdbbbd684bEric Fitchettimport android.view.KeyEvent; 23dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 2417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jinimport java.util.ArrayList; 2517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jinimport java.util.Collections; 26f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jinimport java.util.List; 27dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jinimport java.util.Map; 2821a0001e2426644dd68e6140b5873ebaeafcc3dcKevin Jinimport java.util.concurrent.Callable; 2921a0001e2426644dd68e6140b5873ebaeafcc3dcKevin Jinimport java.util.concurrent.FutureTask; 30df8ca0b5f5b6975d351a424db3bc1e8de88fe0fcKevin Jin 314b31201b5a2dbf8036da5a8d089a68a39cc1dc44Kevin Jinimport io.appium.droiddriver.UiElement; 324b31201b5a2dbf8036da5a8d089a68a39cc1dc44Kevin Jinimport io.appium.droiddriver.actions.Action; 334b31201b5a2dbf8036da5a8d089a68a39cc1dc44Kevin Jinimport io.appium.droiddriver.actions.EventUiElementActor; 344b31201b5a2dbf8036da5a8d089a68a39cc1dc44Kevin Jinimport io.appium.droiddriver.actions.InputInjector; 354b31201b5a2dbf8036da5a8d089a68a39cc1dc44Kevin Jinimport io.appium.droiddriver.actions.SingleKeyAction; 364b31201b5a2dbf8036da5a8d089a68a39cc1dc44Kevin Jinimport io.appium.droiddriver.actions.TextAction; 374b31201b5a2dbf8036da5a8d089a68a39cc1dc44Kevin Jinimport io.appium.droiddriver.actions.UiElementActor; 384b31201b5a2dbf8036da5a8d089a68a39cc1dc44Kevin Jinimport io.appium.droiddriver.exceptions.DroidDriverException; 394b31201b5a2dbf8036da5a8d089a68a39cc1dc44Kevin Jinimport io.appium.droiddriver.finders.Attribute; 404b31201b5a2dbf8036da5a8d089a68a39cc1dc44Kevin Jinimport io.appium.droiddriver.finders.Predicate; 414b31201b5a2dbf8036da5a8d089a68a39cc1dc44Kevin Jinimport io.appium.droiddriver.finders.Predicates; 424b31201b5a2dbf8036da5a8d089a68a39cc1dc44Kevin Jinimport io.appium.droiddriver.scroll.Direction.PhysicalDirection; 434b31201b5a2dbf8036da5a8d089a68a39cc1dc44Kevin Jinimport io.appium.droiddriver.util.Events; 444b31201b5a2dbf8036da5a8d089a68a39cc1dc44Kevin Jinimport io.appium.droiddriver.util.Logs; 454b31201b5a2dbf8036da5a8d089a68a39cc1dc44Kevin Jinimport io.appium.droiddriver.util.Strings; 464b31201b5a2dbf8036da5a8d089a68a39cc1dc44Kevin Jinimport io.appium.droiddriver.util.Strings.ToStringHelper; 474b31201b5a2dbf8036da5a8d089a68a39cc1dc44Kevin Jinimport io.appium.droiddriver.validators.Validator; 484b31201b5a2dbf8036da5a8d089a68a39cc1dc44Kevin Jin 49cddda72410c992a12db61cef26713b498e31fea4Thanh Le/** 507c8b54f99e678a1b40b98fc3069217877ec5199cKevin Jin * Base UiElement that implements the common operations. 5174676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin * 5274676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin * @param <R> the type of the raw element this class wraps, for example, View or 5374676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin * AccessibilityNodeInfo 5474676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin * @param <E> the type of the concrete subclass of BaseUiElement 55cddda72410c992a12db61cef26713b498e31fea4Thanh Le */ 5674676fdd3c8a9e599eddd13bea56898674d9916aKevin Jinpublic abstract class BaseUiElement<R, E extends BaseUiElement<R, E>> implements UiElement { 57d2abd0b28789a4a187343b0485e2b8e3fc9ef7acKevin Jin // These two attribute names are used for debugging only. 58d2abd0b28789a4a187343b0485e2b8e3fc9ef7acKevin Jin // The two constants are used internally and must match to-uiautomator.xsl. 59d2abd0b28789a4a187343b0485e2b8e3fc9ef7acKevin Jin public static final String ATTRIB_VISIBLE_BOUNDS = "VisibleBounds"; 60d2abd0b28789a4a187343b0485e2b8e3fc9ef7acKevin Jin public static final String ATTRIB_NOT_VISIBLE = "NotVisible"; 61d2abd0b28789a4a187343b0485e2b8e3fc9ef7acKevin Jin 6274676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin private UiElementActor uiElementActor = EventUiElementActor.INSTANCE; 63cf1203b8078bed407ed0035c201746fae136439aKevin Jin private Validator validator = null; 64a738fe74f57f48dde2dd7a28479bab3f5441dadbKevin Jin 65dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin @SuppressWarnings("unchecked") 662acc13e041cb065f90ab7882e095f05cb275dc68Kevin Jin @Override 672acc13e041cb065f90ab7882e095f05cb275dc68Kevin Jin public <T> T get(Attribute attribute) { 68dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return (T) getAttributes().get(attribute); 69dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 70dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 71dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin @Override 72dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin public String getText() { 73dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return get(Attribute.TEXT); 74dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 75dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 76dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin @Override 77dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin public String getContentDescription() { 78dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return get(Attribute.CONTENT_DESC); 79dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 80dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 81dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin @Override 82dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin public String getClassName() { 83dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return get(Attribute.CLASS); 84dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 85dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 86dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin @Override 87dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin public String getResourceId() { 88dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return get(Attribute.RESOURCE_ID); 89dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 90dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 91dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin @Override 92dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin public String getPackageName() { 93dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return get(Attribute.PACKAGE); 94dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 95dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 96dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin @Override 97dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin public boolean isCheckable() { 98dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return (Boolean) get(Attribute.CHECKABLE); 99dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 100dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 101dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin @Override 102dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin public boolean isChecked() { 103dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return (Boolean) get(Attribute.CHECKED); 104dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 105dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 106dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin @Override 107dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin public boolean isClickable() { 108dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return (Boolean) get(Attribute.CLICKABLE); 109dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 110dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 111dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin @Override 112dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin public boolean isEnabled() { 113dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return (Boolean) get(Attribute.ENABLED); 114dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 115dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 116dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin @Override 117dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin public boolean isFocusable() { 118dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return (Boolean) get(Attribute.FOCUSABLE); 119dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 120dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 121dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin @Override 122dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin public boolean isFocused() { 123dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return (Boolean) get(Attribute.FOCUSED); 124dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 125dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 126dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin @Override 127dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin public boolean isScrollable() { 128dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return (Boolean) get(Attribute.SCROLLABLE); 129dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 130dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 131dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin @Override 132dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin public boolean isLongClickable() { 133dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return (Boolean) get(Attribute.LONG_CLICKABLE); 134dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 135dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 136dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin @Override 137dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin public boolean isPassword() { 138dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return (Boolean) get(Attribute.PASSWORD); 139dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 140dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 141dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin @Override 142dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin public boolean isSelected() { 143dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return (Boolean) get(Attribute.SELECTED); 144dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin } 145dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 146dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin @Override 147dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin public Rect getBounds() { 148dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return get(Attribute.BOUNDS); 1492acc13e041cb065f90ab7882e095f05cb275dc68Kevin Jin } 1502acc13e041cb065f90ab7882e095f05cb275dc68Kevin Jin 1515c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin // TODO: expose these 3 methods in UiElement? 1525c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin public int getSelectionStart() { 1535c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin Integer value = get(Attribute.SELECTION_START); 1545c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin return value == null ? 0 : value; 1555c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin } 1565c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin 1575c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin public int getSelectionEnd() { 1585c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin Integer value = get(Attribute.SELECTION_END); 1595c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin return value == null ? 0 : value; 1605c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin } 1615c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin 1625c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin public boolean hasSelection() { 1635c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin final int selectionStart = getSelectionStart(); 1645c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin final int selectionEnd = getSelectionEnd(); 1655c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin 1665c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin return selectionStart >= 0 && selectionStart != selectionEnd; 1675c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin } 1685c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin 169a6c69788f9ff3360c02b9362bb65c136fe0b9a99Kevin Jin @Override 170f9c2a591497874769b87bf492a0666cf853e0ae5Kevin Jin public boolean perform(Action action) { 1710d7b67b43f83536708a6ae0330e739744049a48eKevin Jin Logs.call(this, "perform", action); 172cf1203b8078bed407ed0035c201746fae136439aKevin Jin if (validator != null && validator.isApplicable(this, action)) { 173cf1203b8078bed407ed0035c201746fae136439aKevin Jin String failure = validator.validate(this, action); 174cf1203b8078bed407ed0035c201746fae136439aKevin Jin if (failure != null) { 175cf1203b8078bed407ed0035c201746fae136439aKevin Jin throw new DroidDriverException(toString() + " failed validation: " + failure); 17674676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin } 17774676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin } 178cf1203b8078bed407ed0035c201746fae136439aKevin Jin 179cf1203b8078bed407ed0035c201746fae136439aKevin Jin // timeoutMillis <= 0 means no need to wait 180cf1203b8078bed407ed0035c201746fae136439aKevin Jin if (action.getTimeoutMillis() <= 0) { 181cf1203b8078bed407ed0035c201746fae136439aKevin Jin return doPerform(action); 182cf1203b8078bed407ed0035c201746fae136439aKevin Jin } 18321a0001e2426644dd68e6140b5873ebaeafcc3dcKevin Jin return performAndWait(action); 18421a0001e2426644dd68e6140b5873ebaeafcc3dcKevin Jin } 18521a0001e2426644dd68e6140b5873ebaeafcc3dcKevin Jin 18621a0001e2426644dd68e6140b5873ebaeafcc3dcKevin Jin protected boolean doPerform(Action action) { 187a738fe74f57f48dde2dd7a28479bab3f5441dadbKevin Jin return action.perform(this); 188f9c2a591497874769b87bf492a0666cf853e0ae5Kevin Jin } 189a6c69788f9ff3360c02b9362bb65c136fe0b9a99Kevin Jin 1908d19bb634c670a49f7a58636a2a535c86b57d538Kevin Jin protected abstract void doPerformAndWait(FutureTask<Boolean> futureTask, long timeoutMillis); 19121a0001e2426644dd68e6140b5873ebaeafcc3dcKevin Jin 19221a0001e2426644dd68e6140b5873ebaeafcc3dcKevin Jin private boolean performAndWait(final Action action) { 19321a0001e2426644dd68e6140b5873ebaeafcc3dcKevin Jin FutureTask<Boolean> futureTask = new FutureTask<Boolean>(new Callable<Boolean>() { 19421a0001e2426644dd68e6140b5873ebaeafcc3dcKevin Jin @Override 19521a0001e2426644dd68e6140b5873ebaeafcc3dcKevin Jin public Boolean call() { 19621a0001e2426644dd68e6140b5873ebaeafcc3dcKevin Jin return doPerform(action); 19721a0001e2426644dd68e6140b5873ebaeafcc3dcKevin Jin } 19821a0001e2426644dd68e6140b5873ebaeafcc3dcKevin Jin }); 19921a0001e2426644dd68e6140b5873ebaeafcc3dcKevin Jin doPerformAndWait(futureTask, action.getTimeoutMillis()); 200cf1203b8078bed407ed0035c201746fae136439aKevin Jin 20121a0001e2426644dd68e6140b5873ebaeafcc3dcKevin Jin try { 20221a0001e2426644dd68e6140b5873ebaeafcc3dcKevin Jin return futureTask.get(); 2031dbff05bf375557ea73e07632d732bd90a71af9eKevin Jin } catch (Throwable t) { 2041dbff05bf375557ea73e07632d732bd90a71af9eKevin Jin throw DroidDriverException.propagate(t); 20521a0001e2426644dd68e6140b5873ebaeafcc3dcKevin Jin } 20621a0001e2426644dd68e6140b5873ebaeafcc3dcKevin Jin } 20721a0001e2426644dd68e6140b5873ebaeafcc3dcKevin Jin 208cddda72410c992a12db61cef26713b498e31fea4Thanh Le @Override 209cddda72410c992a12db61cef26713b498e31fea4Thanh Le public void setText(String text) { 210337fafc3b2ede7dbd1be4d1ed0bbfdfdbbbd684bEric Fitchett Logs.call(this, "setText", text); 211082c7925e5109092ff31d7021f1f9bb6daabee12Kevin Jin longClick(); // Gain focus; single click always activates IME. 212082c7925e5109092ff31d7021f1f9bb6daabee12Kevin Jin if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 213082c7925e5109092ff31d7021f1f9bb6daabee12Kevin Jin clearText(); 214082c7925e5109092ff31d7021f1f9bb6daabee12Kevin Jin } 215082c7925e5109092ff31d7021f1f9bb6daabee12Kevin Jin 216082c7925e5109092ff31d7021f1f9bb6daabee12Kevin Jin if (TextUtils.isEmpty(text)) { 217337fafc3b2ede7dbd1be4d1ed0bbfdfdbbbd684bEric Fitchett return; 218337fafc3b2ede7dbd1be4d1ed0bbfdfdbbbd684bEric Fitchett } 219337fafc3b2ede7dbd1be4d1ed0bbfdfdbbbd684bEric Fitchett 220337fafc3b2ede7dbd1be4d1ed0bbfdfdbbbd684bEric Fitchett perform(new TextAction(text)); 221cddda72410c992a12db61cef26713b498e31fea4Thanh Le } 222cddda72410c992a12db61cef26713b498e31fea4Thanh Le 223337fafc3b2ede7dbd1be4d1ed0bbfdfdbbbd684bEric Fitchett private void clearText() { 224337fafc3b2ede7dbd1be4d1ed0bbfdfdbbbd684bEric Fitchett String text = getText(); 225082c7925e5109092ff31d7021f1f9bb6daabee12Kevin Jin if (TextUtils.isEmpty(text)) { 226337fafc3b2ede7dbd1be4d1ed0bbfdfdbbbd684bEric Fitchett return; 227337fafc3b2ede7dbd1be4d1ed0bbfdfdbbbd684bEric Fitchett } 228337fafc3b2ede7dbd1be4d1ed0bbfdfdbbbd684bEric Fitchett 229337fafc3b2ede7dbd1be4d1ed0bbfdfdbbbd684bEric Fitchett InputInjector injector = getInjector(); 230337fafc3b2ede7dbd1be4d1ed0bbfdfdbbbd684bEric Fitchett SingleKeyAction.CTRL_MOVE_HOME.perform(injector, this); 231337fafc3b2ede7dbd1be4d1ed0bbfdfdbbbd684bEric Fitchett 232337fafc3b2ede7dbd1be4d1ed0bbfdfdbbbd684bEric Fitchett final long shiftDownTime = Events.keyDown(injector, KeyEvent.KEYCODE_SHIFT_LEFT, 0); 233337fafc3b2ede7dbd1be4d1ed0bbfdfdbbbd684bEric Fitchett SingleKeyAction.CTRL_MOVE_END.perform(injector, this); 234337fafc3b2ede7dbd1be4d1ed0bbfdfdbbbd684bEric Fitchett Events.keyUp(injector, shiftDownTime, KeyEvent.KEYCODE_SHIFT_LEFT, 0); 235337fafc3b2ede7dbd1be4d1ed0bbfdfdbbbd684bEric Fitchett SingleKeyAction.DELETE.perform(injector, this); 236ecbd0fbd7b3ed1aa31c70b2c0148a8a01ffe8428Jiangnan Shangguan } 237ecbd0fbd7b3ed1aa31c70b2c0148a8a01ffe8428Jiangnan Shangguan 238ecbd0fbd7b3ed1aa31c70b2c0148a8a01ffe8428Jiangnan Shangguan @Override 239cddda72410c992a12db61cef26713b498e31fea4Thanh Le public void click() { 240a738fe74f57f48dde2dd7a28479bab3f5441dadbKevin Jin uiElementActor.click(this); 241f9c2a591497874769b87bf492a0666cf853e0ae5Kevin Jin } 242f9c2a591497874769b87bf492a0666cf853e0ae5Kevin Jin 243f9c2a591497874769b87bf492a0666cf853e0ae5Kevin Jin @Override 244f9c2a591497874769b87bf492a0666cf853e0ae5Kevin Jin public void longClick() { 245a738fe74f57f48dde2dd7a28479bab3f5441dadbKevin Jin uiElementActor.longClick(this); 246f9c2a591497874769b87bf492a0666cf853e0ae5Kevin Jin } 247f9c2a591497874769b87bf492a0666cf853e0ae5Kevin Jin 248f9c2a591497874769b87bf492a0666cf853e0ae5Kevin Jin @Override 249f9c2a591497874769b87bf492a0666cf853e0ae5Kevin Jin public void doubleClick() { 250a738fe74f57f48dde2dd7a28479bab3f5441dadbKevin Jin uiElementActor.doubleClick(this); 251cddda72410c992a12db61cef26713b498e31fea4Thanh Le } 252cddda72410c992a12db61cef26713b498e31fea4Thanh Le 253cddda72410c992a12db61cef26713b498e31fea4Thanh Le @Override 25429d66eeee5d30f7db747cceeb84defec961b4125Kevin Jin public void scroll(PhysicalDirection direction) { 255a738fe74f57f48dde2dd7a28479bab3f5441dadbKevin Jin uiElementActor.scroll(this, direction); 256cddda72410c992a12db61cef26713b498e31fea4Thanh Le } 257cddda72410c992a12db61cef26713b498e31fea4Thanh Le 258dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin protected abstract Map<Attribute, Object> getAttributes(); 2596f160bb942de53103e4f4ed54acaafe2da629fcfKevin Jin 26074676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin protected abstract List<E> getChildren(); 26152107c27b6b0f2b0fdfec995784c73746bb95c4eKevin Jin 262f9c2a591497874769b87bf492a0666cf853e0ae5Kevin Jin @Override 26374676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin public List<E> getChildren(Predicate<? super UiElement> predicate) { 26474676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin List<E> children = getChildren(); 265dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin if (children == null) { 26617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin return Collections.emptyList(); 267f50519233078e65a056cff49d7b4989d57c3e750Kevin Jin } 26817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin if (predicate == null || predicate.equals(Predicates.any())) { 269dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin return children; 270f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin } 271dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin 27274676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin List<E> filteredChildren = new ArrayList<E>(children.size()); 27374676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin for (E child : children) { 27417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin if (predicate.apply(child)) { 27517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin filteredChildren.add(child); 27617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin } 27717342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin } 27817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin return Collections.unmodifiableList(filteredChildren); 279f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin } 280f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin 281f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin @Override 2826316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin public String toString() { 28317342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin ToStringHelper toStringHelper = Strings.toStringHelper(this); 28417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin for (Map.Entry<Attribute, Object> entry : getAttributes().entrySet()) { 28517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin addAttribute(toStringHelper, entry.getKey(), entry.getValue()); 2866316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin } 287c39b04e0e5d3962153cd860d1430857fe625da90Kevin Jin if (!isVisible()) { 288d2abd0b28789a4a187343b0485e2b8e3fc9ef7acKevin Jin toStringHelper.addValue(ATTRIB_NOT_VISIBLE); 2898d19bb634c670a49f7a58636a2a535c86b57d538Kevin Jin } else if (!getVisibleBounds().equals(getBounds())) { 290d2abd0b28789a4a187343b0485e2b8e3fc9ef7acKevin Jin toStringHelper.add(ATTRIB_VISIBLE_BOUNDS, getVisibleBounds().toShortString()); 291c39b04e0e5d3962153cd860d1430857fe625da90Kevin Jin } 2926316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin return toStringHelper.toString(); 2936316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin } 2946316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin 2956316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin private static void addAttribute(ToStringHelper toStringHelper, Attribute attr, Object value) { 2966316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin if (value != null) { 297eb4e1921c193bb90eb0122ea7b0fd37cef60e8e1Kevin Jin if (value instanceof Boolean) { 298eb4e1921c193bb90eb0122ea7b0fd37cef60e8e1Kevin Jin if ((Boolean) value) { 299eb4e1921c193bb90eb0122ea7b0fd37cef60e8e1Kevin Jin toStringHelper.addValue(attr.getName()); 300eb4e1921c193bb90eb0122ea7b0fd37cef60e8e1Kevin Jin } 301d2abd0b28789a4a187343b0485e2b8e3fc9ef7acKevin Jin } else if (value instanceof Rect) { 302d2abd0b28789a4a187343b0485e2b8e3fc9ef7acKevin Jin toStringHelper.add(attr.getName(), ((Rect) value).toShortString()); 3036316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin } else { 3046316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin toStringHelper.add(attr.getName(), value); 3057b2b76255593f0ecfbe2d7f996712fefb391dfedKevin Jin } 3067b2b76255593f0ecfbe2d7f996712fefb391dfedKevin Jin } 3077b2b76255593f0ecfbe2d7f996712fefb391dfedKevin Jin } 30874676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin 30974676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin /** 31074676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin * Gets the raw element used to create this UiElement. The attributes of this 31174676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin * UiElement are based on a snapshot of the raw element at construction time. 31274676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin * If the raw element is updated later, the attributes may not match. 31374676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin */ 31474676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin // TODO: expose in UiElement? 31574676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin public abstract R getRawElement(); 31674676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin 31774676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin public void setUiElementActor(UiElementActor uiElementActor) { 31874676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin this.uiElementActor = uiElementActor; 31974676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin } 32074676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin 321cf1203b8078bed407ed0035c201746fae136439aKevin Jin /** 322cf1203b8078bed407ed0035c201746fae136439aKevin Jin * Sets the validator to check when {@link #perform(Action)} is called. 323cf1203b8078bed407ed0035c201746fae136439aKevin Jin */ 324cf1203b8078bed407ed0035c201746fae136439aKevin Jin public void setValidator(Validator validator) { 325cf1203b8078bed407ed0035c201746fae136439aKevin Jin this.validator = validator; 32674676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin } 327cddda72410c992a12db61cef26713b498e31fea4Thanh Le} 328