在Android WebView中手动触发剪切/复制/粘贴

时间:2019-04-26 12:51:51

标签: android webview menu selection

我正在使用android webview构建一个小的浏览器应用程序,并且我一直在javascript中使用window.getSelection()来获取用户选择的任何文本的性质,并根据浏览器的类型显示自定义上下文菜单选择,例如,是否是范围,克拉,是否处于可编辑的内容等。

除非所选内容位于iframe中,否则此方法工作正常,然后启动浏览器安全措施并阻止我使用window.getSelection()嗅探所选内容。我该如何解决?

理想情况下,我需要一种方法来获取有关从Web视图中选择的内容的更好信息,或者如果不可能,则需要一种方法来嗅探选择是否发生在iframe中,以便可以禁用自定义上下文菜单逻辑并回退到默认的Android上下文菜单。

更新/进一步澄清07/05/2019:

在我的最初描述中,我似乎还不够清楚...

我的目标是在选择Web视图中的内容时具有视觉上和功能上的自定义菜单,这些内容可以像标准上下文菜单在页面/ iframe的任何部分中一样进行剪切/复制/粘贴。例如

example custom menu

我意识到我使用JavaScript来检测选择类型并执行剪切/复制/粘贴操作的原始方法是错误的,因为它将被iframe中的跨源安全性阻止。

我需要的是一种基于本地android / webview的方法。我发现可以通过查看mode.getMenu()onActionModeStarted中的项目来嗅探Web视图中的选择类型。这将允许我在自定义菜单UI中显示正确的按钮,但是我无法手动触发单击“剪切/复制/粘贴”时调用的相同逻辑。我以为我找到了webView.performAccessibilityAction(AccessibilityNodeInfo.ACTION_CUT, null);的解决方案,但是由于某种原因该解决方案无法正常工作,所以我想我的问题确实是如何在不使用JavaScript的情况下如何手动触发Webview中所选文本的剪切/复制/粘贴?或任何其他方法可以使我拥有一个基于选择内容的自定义选择菜单,其中包含很多选项,而不会遇到浏览器安全性限制?

4 个答案:

答案 0 :(得分:2)

好的,我知道如何大致做到这一点。

步骤1)在您的活动中,覆盖onActionModeStarted并检查默认上下文菜单中可用的菜单项。这为您提供了有关选择的类型以及自定义菜单中需要显示哪些按钮的线索。此外,它还为您提供了商品ID的引用,您可以在以后使用该ID来触发操作,例如

systemSelectionMenu = mode.getMenu(); // keep a reference to the menu
MenuItem copyItem = systemSelectionMenu.getItem(0); // fetch any menu items you want
copyActionId = copyItem.getItemId(); // store reference to each item you want to manually trigger

第2步)不用清除菜单,而使用setVisible()隐藏要为其添加自定义按钮的每个菜单项。

copyItem.setVisible(false); 

步骤3)在您的自定义按钮onclick事件中,您可以使用以下方式触发复制操作:

myActivity.systemSelectionMenu.performIdentifierAction(myActivity.copyActionId, 0)

答案 1 :(得分:1)

仅当iframe的来源相同时,您才能检索它的选择。否则,您将没有机会跟踪任何iframe的事件(单击,触摸,按键等)。

const getSelectedText = (win, doc) => {    
  const isWindowSelectionAvailable = win && typeof win.getSelection != "undefined";
  if (isWindowSelectionAvailable) {
    return win.getSelection().toString();
  }

  const hasDocumentSelection = doc && typeof doc.selection != "undefined" && doc.selection.type == "Text";
  if (hasDocumentSelection) {
    return doc.selection.createRange().text;
  }

  return '';
}

const doIfTextSelected = (win, doc, cb) => () => {
  const selectedText = getSelectedText(win, doc);
  if (selectedText) {
      cb(selectedText);
  }
}

const setupSelectionListener = (win, doc, cb) => {
  doc.onmouseup = doIfTextSelected(win, doc, cb);
  doc.onkeyup = doIfTextSelected(win, doc, cb);
}

const getIframeWinAndDoc = (iframe) => {
  try {
    const doc = iframe.contentDocument || iframe.contentWindow.document;
    const win = iframe.contentWindow || iframe.contentDocument.defaultView;

    return { win, doc };
  } catch (e) {
    console.error(`${e}`);
    
    return {};
  }
}

const callback = console.log;

setupSelectionListener(window, document, callback);

document.querySelectorAll('iframe').forEach(iframe => {
  const { win, doc } = getIframeWinAndDoc(iframe, console.log);
  
  // Only for same origin iframes due to https://en.wikipedia.org/wiki/Same-origin_policy
  if (win && doc) {
    setupSelectionListener(win, doc, callback);
  }
})
<h3>Select me</h3>

<div class="container">
  <iframe src="https://teimurjan.github.io"></iframe>
</div>

答案 2 :(得分:0)

如果此问题与Internet Explorer配合使用,则该问题因浏览器而异,因此可能与chrome有关 试试这个

App.util.getSelectedText = function(frameId) {
var frame = Ext.getDom(frameId);
var frameWindow = frame.contentWindow;
var frameDocument = frameWindow.document;

if (frameDocument.getSelection) {
    return frameDocument.getSelection();
}
else if (frameDocument.selection) {
    return frameDocument.selection.createRange().text;
}
};

希望它运行正常

答案 3 :(得分:0)

主要问题是window.getSelection()仅返回主上下文/窗口的选择。由于iframe是另一个窗口和其他上下文,因此您应该从iframe调用getSelection(),它是“当前”的。