Due to security restrictions, Safari triggers a native paste pop-up when accessing the clipboard. This degrades the user experience when syncing content to the Cloud Phone. This topic explains how to use the Web SDK to paste text from your local clipboard into Safari.
Solution overview
Add a custom clipboard button to your local client. Clicking this button opens a pop-up input box.
Enter the content to send to the Cloud Phone in the pop-up input box.
Click the Paste Now button in the pop-up input box to sync the content to the clipboard and input field of the Cloud Phone.
Procedure
Cloud phone parameters
Set
readClipboardDataByUserto true to prevent the SDK from automatically reading from the clipboard.Set
useCustomImeto true to enable the local input method editor (IME). This is required to sync content to the input field of the Cloud Phone.
// Check if the browser is Safari.
function isSafari() {
return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
}
// Main control: Disable automatic clipboard reads and enable the local IME.
connConfig = {..., readClipboardDataByUser:isSafari(), useCustomIme:true};
var appInfo = {
...,
connConfig: connConfig,
...
};
var sessionParam = {
...,
appInfo: appInfo,
....
};
var wuyingSdk = Wuying.WebSDK;
session = wuyingSdk.createSession('appstream', sessionParam);
// Thumbnail configuration: Enable the local IME.
this.thumbnail = new window.Wuying.ThumbnailSDK({
...,
connectionConfig: {
useCustomIme: true,
},
},
{
onConnected: (data) => {
this.thumbnail.session.getLocalConfig().setClipboardEnabled(true);
},
onDisConnected: (data) => {},
onThumbnailData: (url) => {},
}
);Build the pop-up input box
Create a clipboard button that opens the pop-up input box. The following code provides an example.
function showInput() {
showCustomDialog();
// Close the dialog when the overlay is clicked.
document.querySelector('.modal-overlay').addEventListener('click', function (e) {
if (e.target === this) {
closeModal();
}
});
// Close the dialog when the Escape key is pressed.
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') {
closeModal();
}
});
}
function closeModal() {
console.log('closeModal');
const overlay = document.querySelector('.modal-overlay');
if (overlay) {
overlay.style.display = 'none';
overlay.remove();
}
}
function submitContent() {
const content = document.querySelector('.input-field').value.trim();
if (!content) {
alert('Please enter content.');
return;
}
// Sync the content to the Cloud Phone.
sendMsgToCloudPhoneOperation(content);
closeModal();
}
function showCustomDialog() {
// Create the modal overlay.
const overlay = document.createElement('div');
overlay.className = 'modal-overlay';
// Ensure the modal captures all events and is displayed at the highest level
overlay.style.pointerEvents = 'all';
overlay.style.zIndex = '9999';
// Prevent events from propagating to underlying layers
const stopPropagation = function(e) {
e.stopPropagation();
};
overlay.addEventListener('contextmenu', stopPropagation, true);
// Add event listeners to intercept all keyboard events
overlay.addEventListener('keydown', stopPropagation, true);
overlay.addEventListener('keyup', stopPropagation, true);
overlay.addEventListener('keypress', stopPropagation, true);
overlay.innerHTML = `
<div class="modal">
<div class="modal-header">
<h2 class="modal-title">Clipboard</h2>
<button class="close-btn" onclick="closeModal()">×</button>
</div>
<div class="input-container">
<textarea
class="input-field"
placeholder="Enter content"
maxlength="500"
></textarea>
</div>
<button class="submit-btn" onclick="submitContent()">
Paste Now
</button>
</div>
`;
document.body.appendChild(overlay);
// Focus the textarea when the modal opens to ensure keyboard events are captured
setTimeout(() => {
const textarea = overlay.querySelector('.input-field');
if (textarea) {
textarea.focus();
}
}, 100);
}Send text to the Cloud Phone
Trigger the sync task
function submitContent() {
const content = document.querySelector('.input-field').value.trim();
if (!content) {
alert('Please enter content.');
return;
}
// Sync the content to the Cloud Phone.
sendMsgToCloudPhoneOperation(content);
closeModal();
}Sync content to the Cloud Phone
function sendMsgToCloudPhoneOperation(msg) {
// For the main control session
if (session) {
session.setClipboardModule('sendClipboardDataToRemote', msg)
}
// For thumbnail sessions
for (const [key, value] of thumbnailSDKMap) {
console.log('thumbnail sendMsgToCloudPhoneOperation ', key, msg);
value.thumbnail.session.getClipboardModule().sendClipboardDataToRemote(msg);
}
}Usage notes
The custom dialog must capture all keyboard events to prevent other UI elements from intercepting them. This ensures that key presses register correctly within the dialog.
overlay.addEventListener('contextmenu', stopPropagation, true);
// Add event listeners to intercept all keyboard events
overlay.addEventListener('keydown', stopPropagation, true);
overlay.addEventListener('keyup', stopPropagation, true);
overlay.addEventListener('keypress', stopPropagation, true);