diff --git a/src/main/java/io/appium/java_client/MobileCommand.java b/src/main/java/io/appium/java_client/MobileCommand.java index 997242cf6..0b386aafe 100644 --- a/src/main/java/io/appium/java_client/MobileCommand.java +++ b/src/main/java/io/appium/java_client/MobileCommand.java @@ -58,6 +58,11 @@ public class MobileCommand { protected static final String REMOVE_APP; //endregion + //region Clipboard + public static final String GET_CLIPBOARD; + public static final String SET_CLIPBOARD; + //endregion + protected static final String GET_PERFORMANCE_DATA; protected static final String GET_SUPPORTED_PERFORMANCE_DATA_TYPES; @@ -127,6 +132,11 @@ public class MobileCommand { INSTALL_APP = "installApp"; //endregion + //region Clipboard + SET_CLIPBOARD = "setClipboard"; + GET_CLIPBOARD = "getClipboard"; + //endregion + GET_PERFORMANCE_DATA = "getPerformanceData"; GET_SUPPORTED_PERFORMANCE_DATA_TYPES = "getSuppportedPerformanceDataTypes"; @@ -205,6 +215,11 @@ public class MobileCommand { commandRepository.put(QUERY_APP_STATE, postC("/session/:sessionId/appium/device/app_state")); //endregion + //region Clipboard + commandRepository.put(GET_CLIPBOARD, postC("/session/:sessionId/appium/device/get_clipboard")); + commandRepository.put(SET_CLIPBOARD, postC("/session/:sessionId/appium/device/set_clipboard")); + //endregion + //iOS commandRepository.put(SHAKE, postC("/session/:sessionId/appium/device/shake")); commandRepository.put(TOUCH_ID, postC("/session/:sessionId/appium/simulator/touch_id")); diff --git a/src/main/java/io/appium/java_client/android/AndroidDriver.java b/src/main/java/io/appium/java_client/android/AndroidDriver.java index 3af82826b..c682d1d2e 100644 --- a/src/main/java/io/appium/java_client/android/AndroidDriver.java +++ b/src/main/java/io/appium/java_client/android/AndroidDriver.java @@ -53,7 +53,7 @@ public class AndroidDriver FindsByAndroidUIAutomator, LocksDevice, HasAndroidSettings, HasDeviceDetails, HasSupportedPerformanceDataType, AuthenticatesByFinger, CanRecordScreen, SupportsSpecialEmulatorCommands, - SupportsNetworkStateManagement { + SupportsNetworkStateManagement, HasAndroidClipboard { private static final String ANDROID_PLATFORM = MobilePlatform.ANDROID; diff --git a/src/main/java/io/appium/java_client/android/HasAndroidClipboard.java b/src/main/java/io/appium/java_client/android/HasAndroidClipboard.java new file mode 100644 index 000000000..063f689cc --- /dev/null +++ b/src/main/java/io/appium/java_client/android/HasAndroidClipboard.java @@ -0,0 +1,58 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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.java_client.android; + +import static com.google.common.base.Preconditions.checkNotNull; +import static io.appium.java_client.MobileCommand.SET_CLIPBOARD; +import static io.appium.java_client.MobileCommand.prepareArguments; + +import io.appium.java_client.CommandExecutionHelper; +import io.appium.java_client.clipboard.ClipboardContentType; +import io.appium.java_client.clipboard.HasClipboard; + +import java.nio.charset.StandardCharsets; +import java.util.AbstractMap; +import java.util.Base64; + +public interface HasAndroidClipboard extends HasClipboard { + /** + * Set the content of device's clipboard. + * + * @param label clipboard data label. + * @param contentType one of supported content types. + * @param base64Content base64-encoded content to be set. + */ + default void setClipboard(String label, ClipboardContentType contentType, byte[] base64Content) { + String[] parameters = new String[]{"content", "contentType", "label"}; + Object[] values = new Object[]{new String(checkNotNull(base64Content), StandardCharsets.UTF_8), + contentType.name().toLowerCase(), checkNotNull(label)}; + CommandExecutionHelper.execute(this, new AbstractMap.SimpleEntry<>(SET_CLIPBOARD, + prepareArguments(parameters, values))); + } + + /** + * Set the clipboard text. + * + * @param label clipboard data label. + * @param text The actual text to be set. + */ + default void setClipboardText(String label, String text) { + setClipboard(label, ClipboardContentType.PLAINTEXT, Base64 + .getEncoder() + .encode(text.getBytes(StandardCharsets.UTF_8))); + } +} diff --git a/src/main/java/io/appium/java_client/clipboard/ClipboardContentType.java b/src/main/java/io/appium/java_client/clipboard/ClipboardContentType.java new file mode 100644 index 000000000..006d88e43 --- /dev/null +++ b/src/main/java/io/appium/java_client/clipboard/ClipboardContentType.java @@ -0,0 +1,21 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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.java_client.clipboard; + +public enum ClipboardContentType { + PLAINTEXT, IMAGE, URL +} diff --git a/src/main/java/io/appium/java_client/clipboard/HasClipboard.java b/src/main/java/io/appium/java_client/clipboard/HasClipboard.java new file mode 100644 index 000000000..a0a07c556 --- /dev/null +++ b/src/main/java/io/appium/java_client/clipboard/HasClipboard.java @@ -0,0 +1,79 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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.java_client.clipboard; + +import static com.google.common.base.Preconditions.checkNotNull; +import static io.appium.java_client.MobileCommand.GET_CLIPBOARD; +import static io.appium.java_client.MobileCommand.SET_CLIPBOARD; +import static io.appium.java_client.MobileCommand.prepareArguments; + +import io.appium.java_client.CommandExecutionHelper; +import io.appium.java_client.ExecutesMethod; + +import java.nio.charset.StandardCharsets; +import java.util.AbstractMap; +import java.util.Base64; + +public interface HasClipboard extends ExecutesMethod { + /** + * Set the content of device's clipboard. + * + * @param contentType one of supported content types. + * @param base64Content base64-encoded content to be set. + */ + default void setClipboard(ClipboardContentType contentType, byte[] base64Content) { + String[] parameters = new String[]{"content", "contentType"}; + Object[] values = new Object[]{new String(checkNotNull(base64Content), StandardCharsets.UTF_8), + contentType.name().toLowerCase()}; + CommandExecutionHelper.execute(this, new AbstractMap.SimpleEntry<>(SET_CLIPBOARD, + prepareArguments(parameters, values))); + } + + /** + * Get the content of the clipboard. + * + * @param contentType one of supported content types. + * @return the actual content of the clipboard as base64-encoded string or an empty string if the clipboard is empty + */ + default String getClipboard(ClipboardContentType contentType) { + return CommandExecutionHelper.execute(this, new AbstractMap.SimpleEntry<>(GET_CLIPBOARD, + prepareArguments("contentType", contentType.name().toLowerCase()))); + } + + /** + * Set the clipboard text. + * + * @param text The actual text to be set. + */ + default void setClipboardText(String text) { + setClipboard(ClipboardContentType.PLAINTEXT, Base64 + .getEncoder() + .encode(text.getBytes(StandardCharsets.UTF_8))); + } + + /** + * Get the clipboard text. + * + * @return Either the text, which is stored in the clipboard or an empty string if the clipboard is empty + */ + default String getClipboardText() { + byte[] base64decodedBytes = Base64 + .getDecoder() + .decode(getClipboard(ClipboardContentType.PLAINTEXT)); + return new String(base64decodedBytes, StandardCharsets.UTF_8); + } +} diff --git a/src/main/java/io/appium/java_client/ios/HasIOSClipboard.java b/src/main/java/io/appium/java_client/ios/HasIOSClipboard.java new file mode 100644 index 000000000..e04166116 --- /dev/null +++ b/src/main/java/io/appium/java_client/ios/HasIOSClipboard.java @@ -0,0 +1,86 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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.java_client.ios; + +import static com.google.common.base.Preconditions.checkNotNull; + +import io.appium.java_client.clipboard.ClipboardContentType; +import io.appium.java_client.clipboard.HasClipboard; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import javax.imageio.ImageIO; + +public interface HasIOSClipboard extends HasClipboard { + /** + * Set an image to the clipboard. + * + * @param img the actual image to be set. + * @throws IOException if the image cannot be decoded in PNG representation + */ + default void setClipboardImage(BufferedImage img) throws IOException { + try (final ByteArrayOutputStream os = new ByteArrayOutputStream()) { + ImageIO.write(checkNotNull(img), "png", os); + setClipboard(ClipboardContentType.IMAGE, Base64 + .getEncoder() + .encode(os.toByteArray())); + } + } + + /** + * Get an image from the clipboard + * + * @return the actual image instance. + * @throws IOException If the returned image cannot be decoded or if the clipboard is empty. + */ + default BufferedImage getClipboardImage() throws IOException { + final byte[] base64decodedBytes = Base64 + .getDecoder() + .decode(getClipboard(ClipboardContentType.IMAGE)); + return ImageIO.read(new ByteArrayInputStream(base64decodedBytes)); + } + + /** + * Set an URL to the clipboard. + * + * @param url the actual URL to set. + */ + default void setClipboardUrl(URL url) { + setClipboard(ClipboardContentType.URL, Base64 + .getEncoder() + .encode(checkNotNull(url).toString().getBytes(StandardCharsets.UTF_8))); + } + + /** + * Get an URL from the clipboard + * + * @return the actual URL instance. + * @throws MalformedURLException if the URL in the clipboard is not valid or if the clipboard is empty. + */ + default URL getClipboardUrl() throws MalformedURLException { + final byte[] base64decodedBytes = Base64 + .getDecoder() + .decode(getClipboard(ClipboardContentType.URL)); + return new URL(new String(base64decodedBytes, StandardCharsets.UTF_8)); + } +} diff --git a/src/main/java/io/appium/java_client/ios/IOSDriver.java b/src/main/java/io/appium/java_client/ios/IOSDriver.java index 569beaf39..1b70f5765 100644 --- a/src/main/java/io/appium/java_client/ios/IOSDriver.java +++ b/src/main/java/io/appium/java_client/ios/IOSDriver.java @@ -56,7 +56,7 @@ public class IOSDriver extends AppiumDriver implements HidesKeyboardWithKeyName, ShakesDevice, HasIOSSettings, FindsByIosUIAutomation, LocksDevice, PerformsTouchID, FindsByIosNSPredicate, - FindsByIosClassChain, PushesFiles, CanRecordScreen { + FindsByIosClassChain, PushesFiles, CanRecordScreen, HasIOSClipboard { private static final String IOS_PLATFORM = MobilePlatform.IOS; diff --git a/src/test/java/io/appium/java_client/android/ClipboardTest.java b/src/test/java/io/appium/java_client/android/ClipboardTest.java new file mode 100644 index 000000000..0e58ae3b6 --- /dev/null +++ b/src/test/java/io/appium/java_client/android/ClipboardTest.java @@ -0,0 +1,35 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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.java_client.android; + +import static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Test; + +public class ClipboardTest extends BaseAndroidTest { + + @Before public void setUp() { + driver.resetApp(); + } + + @Test public void verifySetAndGetClipboardText() { + final String text = "Happy testing"; + driver.setClipboardText(text); + assertEquals(driver.getClipboardText(), text); + } +} diff --git a/src/test/java/io/appium/java_client/ios/ClipboardTest.java b/src/test/java/io/appium/java_client/ios/ClipboardTest.java new file mode 100644 index 000000000..ac7618fd8 --- /dev/null +++ b/src/test/java/io/appium/java_client/ios/ClipboardTest.java @@ -0,0 +1,30 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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.java_client.ios; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class ClipboardTest extends AppIOSTest { + + @Test public void verifySetAndGetClipboardText() { + final String text = "Happy testing"; + driver.setClipboardText(text); + assertEquals(driver.getClipboardText(), text); + } +}