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