aboutsummaryrefslogtreecommitdiff
path: root/src/io/appium/droiddriver/base/DroidDriverContext.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/io/appium/droiddriver/base/DroidDriverContext.java')
-rw-r--r--src/io/appium/droiddriver/base/DroidDriverContext.java149
1 files changed, 149 insertions, 0 deletions
diff --git a/src/io/appium/droiddriver/base/DroidDriverContext.java b/src/io/appium/droiddriver/base/DroidDriverContext.java
new file mode 100644
index 0000000..89e2022
--- /dev/null
+++ b/src/io/appium/droiddriver/base/DroidDriverContext.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2013 DroidDriver committers
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.appium.droiddriver.base;
+
+import android.app.Instrumentation;
+import android.os.Looper;
+import android.util.Log;
+
+import java.util.Locale;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+
+import io.appium.droiddriver.exceptions.DroidDriverException;
+import io.appium.droiddriver.exceptions.TimeoutException;
+import io.appium.droiddriver.finders.ByXPath;
+import io.appium.droiddriver.util.Logs;
+
+/**
+ * Internal helper for DroidDriver implementation.
+ */
+public class DroidDriverContext<R, E extends BaseUiElement<R, E>> {
+ private final Instrumentation instrumentation;
+ private final BaseDroidDriver<R, E> driver;
+ private final Map<R, E> map;
+
+ public DroidDriverContext(Instrumentation instrumentation, BaseDroidDriver<R, E> driver) {
+ this.instrumentation = instrumentation;
+ this.driver = driver;
+ map = new WeakHashMap<R, E>();
+ }
+
+ public Instrumentation getInstrumentation() {
+ return instrumentation;
+ }
+
+ public BaseDroidDriver<R, E> getDriver() {
+ return driver;
+ }
+
+ public E getElement(R rawElement, E parent) {
+ E element = map.get(rawElement);
+ if (element == null) {
+ element = driver.newUiElement(rawElement, parent);
+ map.put(rawElement, element);
+ }
+ return element;
+ }
+
+ public E newRootElement(R rawRoot) {
+ clearData();
+ return getElement(rawRoot, null /* parent */);
+ }
+
+ private void clearData() {
+ map.clear();
+ ByXPath.clearData();
+ }
+
+ /**
+ * Tries to wait for an idle state on the main thread on best-effort basis up
+ * to {@code timeoutMillis}. The main thread may not enter the idle state when
+ * animation is playing, for example, the ProgressBar.
+ */
+ public boolean tryWaitForIdleSync(long timeoutMillis) {
+ validateNotAppThread();
+ FutureTask<?> futureTask = new FutureTask<Void>(new Runnable() {
+ @Override
+ public void run() {}
+ }, null);
+ instrumentation.waitForIdle(futureTask);
+
+ try {
+ futureTask.get(timeoutMillis, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ throw new DroidDriverException(e);
+ } catch (ExecutionException e) {
+ throw new DroidDriverException(e);
+ } catch (java.util.concurrent.TimeoutException e) {
+ Logs.log(Log.DEBUG, String.format(Locale.US,
+ "Timed out after %d milliseconds waiting for idle on main looper", timeoutMillis));
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Tries to run {@code runnable} on the main thread on best-effort basis up to
+ * {@code timeoutMillis}. The {@code runnable} may never run, for example, in
+ * case that the main Looper has exited due to uncaught exception.
+ */
+ public boolean tryRunOnMainSync(Runnable runnable, long timeoutMillis) {
+ validateNotAppThread();
+ final FutureTask<?> futureTask = new FutureTask<Void>(runnable, null);
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ instrumentation.runOnMainSync(futureTask);
+ }
+ }).start();
+
+ try {
+ futureTask.get(timeoutMillis, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ throw new DroidDriverException(e);
+ } catch (ExecutionException e) {
+ throw new DroidDriverException(e);
+ } catch (java.util.concurrent.TimeoutException e) {
+ Logs.log(Log.WARN, getRunOnMainSyncTimeoutMessage(timeoutMillis));
+ return false;
+ }
+ return true;
+ }
+
+ public void runOnMainSync(Runnable runnable) {
+ long timeoutMillis = getDriver().getPoller().getTimeoutMillis();
+ if (!tryRunOnMainSync(runnable, timeoutMillis)) {
+ throw new TimeoutException(getRunOnMainSyncTimeoutMessage(timeoutMillis));
+ }
+ }
+
+ private String getRunOnMainSyncTimeoutMessage(long timeoutMillis) {
+ return String.format(Locale.US,
+ "Timed out after %d milliseconds waiting for Instrumentation.runOnMainSync", timeoutMillis);
+ }
+
+ private void validateNotAppThread() {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ throw new DroidDriverException(
+ "This method can not be called from the main application thread");
+ }
+ }
+}