通过Web SDK在Safari浏览器实现粘贴

由于Safari存在安全限制,访问剪贴板内容时会弹出粘贴气泡按钮,影响将客户端剪贴板内容同步至云手机剪贴板的体验。本文介绍如何通过Web SDK实现将本地复制的文本粘贴至Safari浏览器。

方案概述

  1. 在本地客户端添加剪贴板按钮,点击剪贴板按钮,弹出气泡输入框。

  2. 将客户端中需要发送至云手机的内容输入至气泡输入框。

  3. 点击气泡输入框中的立即粘贴按钮,将内容同步至云手机的剪贴板和输入框。

image

实现步骤

云手机参数配置

  • 配置readClipboardDataByUser开启,无需SDK自行读取剪贴板。

  • 配置useCustomIme开启本地输入法,将内容同步到云机输入框需要开启本地输入法。

//是否是Safari
function isSafari() {
    return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
}
//主控配置 禁止SDK自行读取剪贴板和开启本地输入法
connConfig = {..., readClipboardDataByUser:isSafari(), useCustomIme:true}; 
var appInfo = {
  ...,
  connConfig: connConfig,
  ...
};
var sessionParam = {
  ...,
  appInfo: appInfo,
  ....
};
var wuyingSdk = Wuying.WebSDK;
session = wuyingSdk.createSession('appstream', sessionParam);
//缩略图配置 开启本地输入法
this.thumbnail = new window.Wuying.ThumbnailSDK({
  ...,
  connectionConfig: {
    useCustomIme: true,
  },
},
{
  onConnected: (data) => {
    this.thumbnail.session.getLocalConfig().setClipboardEnabled(true);
  },
  onDisConnected: (data) => {},
  onThumbnailData: (url) => {},
}
);

构建气泡输入框

在客户端构建剪贴板按钮,并通过点击按钮唤出支持与剪贴板交互的对话框。可参考如下代码:

function showInput() {
  showCustomDialog();
  // 点击遮罩层关闭弹框
  document.querySelector('.modal-overlay').addEventListener('click', function (e) {
      if (e.target === this) {
          closeModal();
      }
  });

  // ESC键关闭弹框
  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('请输入内容');
      return;
  }
  //同步内容到云机
  sendMsgToCloudPhoneOperation(content);
  closeModal();
}

function showCustomDialog() {
  // 创建模态框背景
  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">剪贴板</h2>
              <button class="close-btn" onclick="closeModal()">×</button>
          </div>
          
          <div class="input-container">
              <textarea 
                  class="input-field" 
                  placeholder="请输入内容"
                  maxlength="500"
              ></textarea>
          </div>
          
          <button class="submit-btn" onclick="submitContent()">
              立即粘贴
          </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);
}

将复制的文本发送至云手机

触发同步任务

function submitContent() {
  const content = document.querySelector('.input-field').value.trim();
  if (!content) {
      alert('请输入内容');
      return;
  }
  //同步内容到云机
  sendMsgToCloudPhoneOperation(content);
  closeModal();
}

将内容同步至云手机

function sendMsgToCloudPhoneOperation(msg) {
  //主控
  if (session) {
    session.setClipboardModule('sendClipboardDataToRemote', msg) 
  }
  //缩略图
  for (const [key, value] of thumbnailSDKMap) {
      console.log('thumbnial sendMsgToCloudPhoneOperation ', key, msg);
      value.thumbnail.session.getClipboardModule().sendClipboardDataToRemote(msg);
  }
}

注意事项

新创建的对话框需要完全接管键盘事件,避免键盘事件被其他桌面元素接管,否则普通按键信息无法在对话框中正确响应。配置参考如下:

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);