diff --git a/java-client.iml b/java-client.iml
index 961a621f6..e881e4a50 100644
--- a/java-client.iml
+++ b/java-client.iml
@@ -19,6 +19,7 @@
+
diff --git a/src/io/appium/java_client/AppiumDriver.java b/src/io/appium/java_client/AppiumDriver.java
index c07634e1d..aa37e209f 100644
--- a/src/io/appium/java_client/AppiumDriver.java
+++ b/src/io/appium/java_client/AppiumDriver.java
@@ -21,6 +21,7 @@
import org.openqa.selenium.*;
import org.openqa.selenium.remote.*;
+import javax.xml.bind.DatatypeConverter;
import java.net.URL;
import java.util.LinkedHashSet;
import java.util.List;
@@ -45,6 +46,10 @@ public AppiumDriver(URL remoteAddress, Capabilities desiredCapabilities){
.put(KEY_EVENT, postC("/session/:sessionId/appium/device/keyevent"))
.put(CURRENT_ACTIVITY, getC("/session/:sessionId/appium/device/current_activity"))
.put(SET_VALUE, postC("/session/:sessionId/appium/element/:id/value"))
+ .put(PULL_FILE, postC("/session/:sessionId/appium/device/pull_file"))
+ .put(HIDE_KEYBOARD, postC("/session/:sessionId/appium/device/hide_keyboard"))
+ .put(PUSH_FILE, postC("/session/:sessionId/appium/device/push_file"))
+ .put(RUN_APP_IN_BACKGROUND, postC("/session/:sessionId/appium/app/background"))
;
ImmutableMap mobileCommands = builder.build();
@@ -71,19 +76,39 @@ protected Response execute(String command) {
}
+ /**
+ * Reset the currently running app for this session
+ */
public void resetApp() {
execute(MobileCommand.RESET);
}
+ /**
+ * Get all defined Strings from an Android app
+ *
+ * @return a string of all the localized strings defined in the app
+ */
public String getAppStrings() {
Response response = execute(GET_STRINGS);
return response.getValue().toString();
}
+ /**
+ * Send a key event to the device
+ *
+ * @param key code for the key pressed on the device
+ */
public void sendKeyEvent(int key) {
sendKeyEvent(key, null);
}
+ /**
+ * Send a key event along with an Android metastate to an Android device
+ * Metastates are things like *shift* to get uppercase characters
+ *
+ * @param key code for the key pressed on the Android device
+ * @param metastate metastate for the keypress
+ */
public void sendKeyEvent(int key, Integer metastate) {
ImmutableMap.Builder builder = ImmutableMap.builder();
builder.put("keycode", key);
@@ -92,11 +117,65 @@ public void sendKeyEvent(int key, Integer metastate) {
execute(KEY_EVENT, parameters);
}
+ /**
+ * Get the current activity being run on the mobile device
+ */
public String currentActivity() {
Response response = execute(CURRENT_ACTIVITY);
return response.getValue().toString();
}
+ /**
+ *
+ * @param remotePath On Android and iOS, this is either the path to the file (relative to the root of the app's file system).
+ * On iOS only, if path starts with /AppName.app, which will be replaced with the application's .app directory
+ * @return A byte array of Base64 encoded data.
+ */
+ public byte[] pullFile(String remotePath) {
+ Response response = execute(PULL_FILE, ImmutableMap.of("path", remotePath));
+ String base64String = response.getValue().toString();
+
+ return DatatypeConverter.parseBase64Binary(base64String);
+ }
+
+ /**
+ * Save base64 encoded data as a file on the remote mobile device.
+ * This is an Android only method.
+ * @param remotePath Path to file to write data to on remote device
+ * @param base64Data Base64 encoded byte array of data to write to remote device
+ */
+ public void pushFile(String remotePath, byte[] base64Data) {
+ ImmutableMap.Builder builder = ImmutableMap.builder();
+ builder.put("path", remotePath).put("data", base64Data);
+ execute(PUSH_FILE, builder.build());
+ }
+
+ /**
+ * Hides the keyboard if it is showing.
+ * This is an iOS only command.
+ */
+ public void hideKeyboard() {
+ execute(HIDE_KEYBOARD);
+ }
+
+ /**
+ * Hides the keyboard by pressing the button specified by keyName if it is showing.
+ * This is an iOS only command.
+ * @param keyName The button pressed by the mobile driver to attempt hiding the keyboard
+ */
+ public void hideKeyboard(String keyName) {
+ execute(HIDE_KEYBOARD, ImmutableMap.of("keyName", keyName));
+ }
+
+ /**
+ * Runs the current app as a background app for the number of seconds requested.
+ * This is a synchronous method, it returns after the back has been returned to the foreground.
+ * @param seconds Number of seconds to run App in background
+ */
+ public void runAppInBackground(int seconds) {
+ execute(RUN_APP_IN_BACKGROUND, ImmutableMap.of("seconds", seconds));
+ }
+
@Override
public WebDriver context(String name) {
diff --git a/src/io/appium/java_client/MobileCommand.java b/src/io/appium/java_client/MobileCommand.java
index d7b2c383d..062186d22 100644
--- a/src/io/appium/java_client/MobileCommand.java
+++ b/src/io/appium/java_client/MobileCommand.java
@@ -29,6 +29,10 @@ public interface MobileCommand {
String KEY_EVENT = "keyEvent";
String CURRENT_ACTIVITY = "currentActivity";
String SET_VALUE = "setValue";
+ String PULL_FILE = "pullFile";
+ String PUSH_FILE = "pushFile";
+ String HIDE_KEYBOARD = "hideKeyboard";
+ String RUN_APP_IN_BACKGROUND = "runAppInBackground";
}
diff --git a/src/io/appium/java_client/MobileDriver.java b/src/io/appium/java_client/MobileDriver.java
index a1151e378..80453b202 100644
--- a/src/io/appium/java_client/MobileDriver.java
+++ b/src/io/appium/java_client/MobileDriver.java
@@ -18,45 +18,15 @@
package io.appium.java_client;
+import org.openqa.selenium.ContextAware;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.Response;
import java.util.Map;
-public interface MobileDriver extends WebDriver {
+public interface MobileDriver extends WebDriver, ContextAware {
public Response execute(String driverCommand, Map parameters);
- /**
- * Reset the currently running app for this session
- */
- void resetApp();
-
- /**
- * Get all defined Strings from an Android app
- */
- String getAppStrings();
-
- /**
- * Send a key event to the device
- *
- * @param key code for the key pressed on the device
- */
- void sendKeyEvent(int key);
-
- /**
- * Send a key event along with an Android metastate to an Android device
- * Metastates are things like *shift* to get uppercase characters
- *
- * @param key code for the key pressed on the Android device
- * @param metastate metastate for the keypress
- */
- void sendKeyEvent(int key, Integer metastate);
-
- /**
- * Get the current activity being run on the mobile device
- */
- String currentActivity();
-
}
diff --git a/test/io/appium/java_client/AndroidUIAutomatorTest.java b/test/io/appium/java_client/AndroidUIAutomatorTest.java
index f0f5df2fa..73ff2c7e3 100644
--- a/test/io/appium/java_client/AndroidUIAutomatorTest.java
+++ b/test/io/appium/java_client/AndroidUIAutomatorTest.java
@@ -1,5 +1,6 @@
package io.appium.java_client;
+import org.apache.commons.codec.binary.Base64;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -65,4 +66,13 @@ public void findElementsByTest() {
public void ErrorTest() {
driver.findElementByAndroidUIAutomator(null);
}
+
+ @Test
+ public void pushFileTest() {
+ byte[] data = Base64.encodeBase64("The eventual code is no more than the deposit of your understanding. ~E. W. Dijkstra".getBytes());
+ driver.pushFile("/data/local/tmp/remote.txt", data);
+ byte[] returnData = driver.pullFile("/data/local/tmp/remote.txt");
+ String returnDataDecoded = new String(Base64.decodeBase64(returnData));
+ assertEquals("The eventual code is no more than the deposit of your understanding. ~E. W. Dijkstra", returnDataDecoded);
+ }
}
\ No newline at end of file
diff --git a/test/io/appium/java_client/MobileDriverIOSTest.java b/test/io/appium/java_client/MobileDriverIOSTest.java
index e11b2fe46..ee0fe0989 100644
--- a/test/io/appium/java_client/MobileDriverIOSTest.java
+++ b/test/io/appium/java_client/MobileDriverIOSTest.java
@@ -65,4 +65,27 @@ public void setValueTest() {
element.setValue("Grace Hopper");
}
+ @Test
+ public void pullFileTest() {
+ byte[] data = driver.pullFile("Library/AddressBook/AddressBook.sqlitedb");
+ assert(data.length > 0);
+ }
+
+ @Test
+ public void hideKeyboardTest() {
+ MobileElement element = new MobileElement((RemoteWebElement)driver.findElementByAccessibilityId("TextFields, Uses of UITextField"), driver);
+ element.click();
+ element = new MobileElement((RemoteWebElement)driver.findElementByAccessibilityId("Normal"), driver);
+ element.click();
+ driver.hideKeyboard();
+ }
+
+ @Test
+ public void runAppInBackgroundTest() {
+ long time = System.currentTimeMillis();
+ driver.runAppInBackground(4);
+ long timeAfter = System.currentTimeMillis();
+ assert(timeAfter - time > 3000);
+ }
+
}