From 082c7925e5109092ff31d7021f1f9bb6daabee12 Mon Sep 17 00:00:00 2001 From: Kevin Jin Date: Thu, 19 Feb 2015 16:27:53 -0800 Subject: Change minSdkVersion to 8 Guard access to newer API. Ideally we should have lint check the usage of our API in user code. E.g. the users specify minSdkVersion of their app to a value lower than 11, but use SingleKeyAction.CTRL_MOVE_HOME. It should be a feature of the Android lint, as described in http://stackoverflow.com/questions/14066857/sdk-version-required-annotation We may write our own lint extension, but it's hard to get it into users' workflow. Change-Id: I8efe88475646530dd8c90ccf443fb3ee9b62c900 --- build.gradle | 2 +- src/com/google/android/droiddriver/UiElement.java | 13 ++++++---- .../droiddriver/actions/SingleKeyAction.java | 28 +++++++++++----------- .../android/droiddriver/actions/TextAction.java | 2 ++ .../android/droiddriver/base/BaseUiElement.java | 13 ++++++---- .../android/droiddriver/helpers/SingleRun.java | 2 +- .../google/android/droiddriver/util/Events.java | 23 ++++++++++++++---- .../google/android/droiddriver/util/FileUtils.java | 18 ++++++++++---- .../validators/ExemptedClassesValidator.java | 3 ++- 9 files changed, 69 insertions(+), 35 deletions(-) diff --git a/build.gradle b/build.gradle index 0128b26..6b947c8 100644 --- a/build.gradle +++ b/build.gradle @@ -39,7 +39,7 @@ android { buildToolsVersion '21.1.2' defaultConfig { - minSdkVersion 12 // TODO: need to support SDK 9? + minSdkVersion 8 targetSdkVersion 21 versionCode 1 versionName version diff --git a/src/com/google/android/droiddriver/UiElement.java b/src/com/google/android/droiddriver/UiElement.java index 571f3bc..03521a9 100644 --- a/src/com/google/android/droiddriver/UiElement.java +++ b/src/com/google/android/droiddriver/UiElement.java @@ -139,7 +139,7 @@ public interface UiElement { /** * Executes the given action. * - * @param action The action to execute + * @param action the action to execute * @return true if the action is successful */ boolean perform(Action action); @@ -148,15 +148,18 @@ public interface UiElement { * Sets the text of this element. The implementation may not work on all * UiElements if the underlying view is not EditText. *

- * If IME is open after this call, you can call - * + * If this element already has text, it is cleared first if the device has API 11 or higher. + *

+ * TODO: Support this behavior on older devices. + *

+ * If the {@code text} ends with {@code '\n'}, the IME may be closed automatically after this + * call. If the IME is open after this call, you can call *

    * perform(SingleKeyAction.BACK);
    * 
- * * to close the IME. * - * @param text The text to enter. + * @param text the text to enter */ void setText(String text); diff --git a/src/com/google/android/droiddriver/actions/SingleKeyAction.java b/src/com/google/android/droiddriver/actions/SingleKeyAction.java index 3f5fe15..b78e54c 100644 --- a/src/com/google/android/droiddriver/actions/SingleKeyAction.java +++ b/src/com/google/android/droiddriver/actions/SingleKeyAction.java @@ -16,6 +16,8 @@ package com.google.android.droiddriver.actions; +import android.annotation.SuppressLint; +import android.annotation.TargetApi; import android.os.Build; import android.view.KeyEvent; @@ -25,38 +27,35 @@ import com.google.android.droiddriver.util.Strings; import com.google.android.droiddriver.util.Strings.ToStringHelper; /** - * An action to press a single key. While it is convenient for navigating the - * UI, do not overuse it -- the application may interpret key codes in a custom - * way and, more importantly, application users may not have access to it - * because the device (physical or virtual keyboard) may not support all key - * codes. + * An action to press a single key. While it is convenient for navigating the UI, do not overuse it + * - the application may interpret key codes in a custom way and, more importantly, application + * users may not have access to it because the device (physical or virtual keyboard) may not support + * all key codes. */ public class SingleKeyAction extends KeyAction { - /** - * Common instances for convenience and memory preservation. - */ + // Common instances for convenience and memory preservation. public static final SingleKeyAction MENU = new SingleKeyAction(KeyEvent.KEYCODE_MENU); public static final SingleKeyAction SEARCH = new SingleKeyAction(KeyEvent.KEYCODE_SEARCH); public static final SingleKeyAction BACK = new SingleKeyAction(KeyEvent.KEYCODE_BACK); public static final SingleKeyAction DELETE = new SingleKeyAction(KeyEvent.KEYCODE_DEL); + /** Requires SDK API 11 or higher */ + @SuppressLint("InlinedApi") public static final SingleKeyAction CTRL_MOVE_HOME = new SingleKeyAction( KeyEvent.KEYCODE_MOVE_HOME, KeyEvent.META_CTRL_LEFT_ON); + /** Requires SDK API 11 or higher */ + @SuppressLint("InlinedApi") public static final SingleKeyAction CTRL_MOVE_END = new SingleKeyAction( KeyEvent.KEYCODE_MOVE_END, KeyEvent.META_CTRL_LEFT_ON); private final int keyCode; private final int metaState; - /** - * Defaults metaState to 0. - */ + /** Defaults metaState to 0 */ public SingleKeyAction(int keyCode) { this(keyCode, 0); } - /** - * Defaults timeoutMillis to 100 and checkFocused to false. - */ + /** Defaults timeoutMillis to 100 and checkFocused to false */ public SingleKeyAction(int keyCode, int metaState) { this(keyCode, metaState, 100L, false); } @@ -77,6 +76,7 @@ public class SingleKeyAction extends KeyAction { return true; } + @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) @Override public String toString() { String keyCodeString = diff --git a/src/com/google/android/droiddriver/actions/TextAction.java b/src/com/google/android/droiddriver/actions/TextAction.java index 586ad4e..6c4fda3 100644 --- a/src/com/google/android/droiddriver/actions/TextAction.java +++ b/src/com/google/android/droiddriver/actions/TextAction.java @@ -16,6 +16,7 @@ package com.google.android.droiddriver.actions; +import android.annotation.SuppressLint; import android.os.Build; import android.os.SystemClock; import android.view.KeyCharacterMap; @@ -31,6 +32,7 @@ import com.google.android.droiddriver.util.Strings; */ public class TextAction extends KeyAction { + @SuppressLint("InlinedApi") @SuppressWarnings("deprecation") private static final KeyCharacterMap KEY_CHAR_MAP = Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB ? KeyCharacterMap diff --git a/src/com/google/android/droiddriver/base/BaseUiElement.java b/src/com/google/android/droiddriver/base/BaseUiElement.java index 2c996f4..15ef840 100644 --- a/src/com/google/android/droiddriver/base/BaseUiElement.java +++ b/src/com/google/android/droiddriver/base/BaseUiElement.java @@ -17,6 +17,8 @@ package com.google.android.droiddriver.base; import android.graphics.Rect; +import android.os.Build; +import android.text.TextUtils; import android.view.KeyEvent; import com.google.android.droiddriver.UiElement; @@ -213,8 +215,12 @@ public abstract class BaseUiElement> implements @Override public void setText(String text) { Logs.call(this, "setText", text); - clearText(); - if (text == null || text.isEmpty()) { + longClick(); // Gain focus; single click always activates IME. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + clearText(); + } + + if (TextUtils.isEmpty(text)) { return; } @@ -222,9 +228,8 @@ public abstract class BaseUiElement> implements } private void clearText() { - longClick(); // Gain focus; single click always activates IME. String text = getText(); - if (text == null || text.isEmpty()) { + if (TextUtils.isEmpty(text)) { return; } diff --git a/src/com/google/android/droiddriver/helpers/SingleRun.java b/src/com/google/android/droiddriver/helpers/SingleRun.java index d11e1b0..f9c7197 100644 --- a/src/com/google/android/droiddriver/helpers/SingleRun.java +++ b/src/com/google/android/droiddriver/helpers/SingleRun.java @@ -31,7 +31,7 @@ public abstract class SingleRun { * * @return true if this is the first time it is called, otherwise false */ - public boolean singleRun() { + public final boolean singleRun() { if (hasRun.compareAndSet(false, true)) { run(); return true; diff --git a/src/com/google/android/droiddriver/util/Events.java b/src/com/google/android/droiddriver/util/Events.java index a905851..79c7958 100644 --- a/src/com/google/android/droiddriver/util/Events.java +++ b/src/com/google/android/droiddriver/util/Events.java @@ -16,6 +16,8 @@ package com.google.android.droiddriver.util; +import android.annotation.TargetApi; +import android.os.Build; import android.os.SystemClock; import android.util.Log; import android.view.InputDevice; @@ -33,37 +35,50 @@ public class Events { /** * @return a touch down event at the specified coordinates */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) private static MotionEvent newTouchDownEvent(int x, int y) { long downTime = SystemClock.uptimeMillis(); MotionEvent event = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, x, y, 1); - event.setSource(InputDevice.SOURCE_TOUCHSCREEN); + // TODO: Fix this if 'source' is required on devices older than HONEYCOMB_MR1. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) { + event.setSource(InputDevice.SOURCE_TOUCHSCREEN); + } return event; } /** * @return a touch up event at the specified coordinates */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) private static MotionEvent newTouchUpEvent(long downTime, int x, int y) { long eventTime = SystemClock.uptimeMillis(); MotionEvent event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 1); - event.setSource(InputDevice.SOURCE_TOUCHSCREEN); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) { + event.setSource(InputDevice.SOURCE_TOUCHSCREEN); + } return event; } /** * @return a touch move event at the specified coordinates */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) private static MotionEvent newTouchMoveEvent(long downTime, int x, int y) { long eventTime = SystemClock.uptimeMillis(); MotionEvent event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 1); - event.setSource(InputDevice.SOURCE_TOUCHSCREEN); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) { + event.setSource(InputDevice.SOURCE_TOUCHSCREEN); + } return event; } + @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) private static KeyEvent newKeyEvent(long downTime, long eventTime, int action, int keyCode, int metaState) { KeyEvent event = new KeyEvent(downTime, eventTime, action, keyCode, 0 /* repeat */, metaState); - event.setSource(InputDevice.SOURCE_KEYBOARD); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) { + event.setSource(InputDevice.SOURCE_KEYBOARD); + } return event; } diff --git a/src/com/google/android/droiddriver/util/FileUtils.java b/src/com/google/android/droiddriver/util/FileUtils.java index 07abb5a..6722dc1 100644 --- a/src/com/google/android/droiddriver/util/FileUtils.java +++ b/src/com/google/android/droiddriver/util/FileUtils.java @@ -16,6 +16,8 @@ package com.google.android.droiddriver.util; +import android.annotation.TargetApi; +import android.os.Build; import android.util.Log; import com.google.android.droiddriver.exceptions.DroidDriverException; @@ -34,13 +36,16 @@ public class FileUtils { * not exist, they will be created. The file will be readable and writable to * all. */ + @TargetApi(Build.VERSION_CODES.GINGERBREAD) public static BufferedOutputStream open(String path) throws FileNotFoundException { File file = getAbsoluteFile(path); Logs.log(Log.INFO, "opening file " + file.getAbsolutePath()); BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(file)); - file.setReadable(true /* readable */, false/* ownerOnly */); - file.setWritable(true /* readable */, false/* ownerOnly */); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { + file.setReadable(true /* readable */, false/* ownerOnly */); + file.setWritable(true /* readable */, false/* ownerOnly */); + } return stream; } @@ -60,6 +65,7 @@ public class FileUtils { return file; } + @TargetApi(Build.VERSION_CODES.GINGERBREAD) private static void mkdirs(File dir) { if (dir == null || dir.exists()) { return; @@ -69,8 +75,10 @@ public class FileUtils { if (!dir.mkdir()) { throw new DroidDriverException("failed to mkdir " + dir); } - dir.setReadable(true /* readable */, false/* ownerOnly */); - dir.setWritable(true /* readable */, false/* ownerOnly */); - dir.setExecutable(true /* executable */, false/* ownerOnly */); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { + dir.setReadable(true /* readable */, false/* ownerOnly */); + dir.setWritable(true /* readable */, false/* ownerOnly */); + dir.setExecutable(true /* executable */, false/* ownerOnly */); + } } } diff --git a/src/com/google/android/droiddriver/validators/ExemptedClassesValidator.java b/src/com/google/android/droiddriver/validators/ExemptedClassesValidator.java index b04ffc5..268e6b3 100644 --- a/src/com/google/android/droiddriver/validators/ExemptedClassesValidator.java +++ b/src/com/google/android/droiddriver/validators/ExemptedClassesValidator.java @@ -16,6 +16,7 @@ package com.google.android.droiddriver.validators; +import android.text.TextUtils; import android.util.Log; import com.google.android.droiddriver.UiElement; @@ -33,7 +34,7 @@ public class ExemptedClassesValidator implements Validator { @Override public boolean isApplicable(UiElement element, Action action) { String className = element.getClassName(); - if (className == null || className.isEmpty()) { + if (TextUtils.isEmpty(className)) { return false; } -- cgit v1.2.3