Skip to content

Add clipboard handlers #855

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/main/java/io/appium/java_client/MobileCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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";

Expand Down Expand Up @@ -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"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public class AndroidDriver<T extends WebElement>
FindsByAndroidUIAutomator<T>, LocksDevice, HasAndroidSettings, HasDeviceDetails,
HasSupportedPerformanceDataType, AuthenticatesByFinger,
CanRecordScreen, SupportsSpecialEmulatorCommands,
SupportsNetworkStateManagement {
SupportsNetworkStateManagement, HasAndroidClipboard {

private static final String ANDROID_PLATFORM = MobilePlatform.ANDROID;

Expand Down
Original file line number Diff line number Diff line change
@@ -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)));
}
}
Original file line number Diff line number Diff line change
@@ -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
}
79 changes: 79 additions & 0 deletions src/main/java/io/appium/java_client/clipboard/HasClipboard.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
86 changes: 86 additions & 0 deletions src/main/java/io/appium/java_client/ios/HasIOSClipboard.java
Original file line number Diff line number Diff line change
@@ -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()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getURLEncoder() & getURLDecoder() should be good than basic encoding/decoding as it is not URL safe. Also for text encoding, i would recommend to use MIME encoding over basic encoding. what say @mykola-mokhnach ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this is not the case for Apple's NSData class, since it requires the data to be always padded: https://stackoverflow.com/questions/21406482/nsdata-wont-accept-valid-base64-encoded-string

.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));
}
}
2 changes: 1 addition & 1 deletion src/main/java/io/appium/java_client/ios/IOSDriver.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public class IOSDriver<T extends WebElement>
extends AppiumDriver<T>
implements HidesKeyboardWithKeyName, ShakesDevice, HasIOSSettings,
FindsByIosUIAutomation<T>, LocksDevice, PerformsTouchID, FindsByIosNSPredicate<T>,
FindsByIosClassChain<T>, PushesFiles, CanRecordScreen {
FindsByIosClassChain<T>, PushesFiles, CanRecordScreen, HasIOSClipboard {

private static final String IOS_PLATFORM = MobilePlatform.IOS;

Expand Down
35 changes: 35 additions & 0 deletions src/test/java/io/appium/java_client/android/ClipboardTest.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
30 changes: 30 additions & 0 deletions src/test/java/io/appium/java_client/ios/ClipboardTest.java
Original file line number Diff line number Diff line change
@@ -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);
}
}