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 io.appium.droiddriver.helpers;
18
19import android.annotation.TargetApi;
20import android.app.Instrumentation;
21import android.os.Build;
22
23import io.appium.droiddriver.DroidDriver;
24import io.appium.droiddriver.exceptions.DroidDriverException;
25import io.appium.droiddriver.instrumentation.InstrumentationDriver;
26import io.appium.droiddriver.uiautomation.UiAutomationDriver;
27import io.appium.droiddriver.util.InstrumentationUtils;
28
29/**
30 * Static utility methods using a singleton {@link DroidDriver} instance. This class is NOT
31 * required, but it is handy and using a singleton driver can avoid memory leak when you have many
32 * instances around (for example, one in every test - JUnit framework keeps the test instances in
33 * memory after running them).
34 */
35public class DroidDrivers {
36  private static DroidDriver driver;
37
38  /**
39   * Gets the singleton driver. Throws if {@link #setSingleton} has not been called.
40   */
41  public static DroidDriver get() {
42    if (driver == null) {
43      throw new DroidDriverException("setSingleton() has not been called");
44    }
45    return driver;
46  }
47
48  /**
49   * Sets the singleton driver.
50   */
51  public static void setSingleton(DroidDriver driver) {
52    if (DroidDrivers.driver != null) {
53      throw new DroidDriverException("setSingleton() can only be called once");
54    }
55    DroidDrivers.driver = driver;
56  }
57
58  /**
59   * Returns whether the running target (device or emulator) has {@link android.app.UiAutomation}
60   * API, which is introduced in SDK API 18 (JELLY_BEAN_MR2).
61   */
62  public static boolean hasUiAutomation() {
63    return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2;
64  }
65
66  /**
67   * Returns a new DroidDriver instance. If am instrument options have "driver", treat it as the
68   * fully-qualified-class-name and create a new instance of it with {@code instrumentation} as the
69   * argument; otherwise a new platform-dependent default DroidDriver instance.
70   */
71  public static DroidDriver newDriver() {
72    Instrumentation instrumentation = InstrumentationUtils.getInstrumentation();
73    String driverClass = InstrumentationUtils.getD2Option("driver");
74    if (driverClass != null) {
75      try {
76        return (DroidDriver) Class.forName(driverClass).getConstructor(Instrumentation.class)
77            .newInstance(instrumentation);
78      } catch (Throwable t) {
79        throw DroidDriverException.propagate(t);
80      }
81    }
82
83    // If "dd.driver" is not specified, return default.
84    if (hasUiAutomation()) {
85      checkUiAutomation();
86      return new UiAutomationDriver(instrumentation);
87    }
88    return new InstrumentationDriver(instrumentation);
89  }
90
91  /** Checks if UiAutomation API is available */
92  @TargetApi(18)
93  public static void checkUiAutomation() {
94    if (!hasUiAutomation()) {
95      throw new DroidDriverException("UiAutomation is not available below API 18. "
96          + "See http://developer.android.com/reference/android/app/UiAutomation.html");
97    }
98    if (InstrumentationUtils.getInstrumentation().getUiAutomation() == null) {
99      throw new DroidDriverException(
100          "uiAutomation==null: did you forget to set '-w' flag for 'am instrument'?");
101    }
102  }
103}
104