剪贴板 API 调用不调用 onPermisonRequest()就引发 NotAllowedError

我有一个带有按钮的简单页面,当按下该按钮时,使用 Async 剪贴板 API 向剪贴板写入。

<body>
<button type="button" onclick="testClipboard();">
Test Clipboard
</button>
</body>
function testClipboard() {
navigator.clipboard.writeText("Clipboard API Test").then(
v => alert("Success"),
e => alert("Fail\n" + e));
}

这在 Chrome 和 Firefox,桌面和移动设备上都可以使用,但是在 Android Webview 上会出现以下错误:

NotAllowError: Write permission denied.


我想我需要重写 WebChromeClient.onPermissionRequest()来授予权限,但奇怪的是 onPermissionRequest()似乎没有被调用,同样的错误仍然被抛出。

public class WebChromeController extends WebChromeClient {
@Override
public void onPermissionRequest(PermissionRequest request) {
Log.d("myTag", "Permission request");
Log.d("myTag", request.getResources().toString());
request.grant(request.getResources());
}
}
protected void initWebView() {
// ...
myWebView.setWebChromeClient(new WebChromeController());
}

我仍然得到相同的错误:

NotAllowError: Write permission denied.

Logcat 也没有任何记录。


我怀疑也许我的 Android 应用程序需要额外的权限访问剪贴板,但根据 https://developer.android.com/about/versions/10/privacy/changes#clipboard-data,我的应用程序应该有权限时,它的焦点。事实上,以下守则是有效的:

ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("MyLbl", "I have permission");
clipboard.setPrimaryClip(clip);

如果请求许可的操作需要许可,我还在 AndroidManifest.xml中声明了以下内容:

<uses-permission android:name="android.webkit.PermissionRequest" />

这什么用都没有。

因此,这可能不是一个应用程序级别的权限问题。


发生什么事了?

如何使异步剪贴板 API 调用在 Webview 中工作?


操作系统: Android 10Q

Webview: v. 81.0.4044.111 Webview: v. 81.0.4044.111

16932 次浏览

剪贴板 API 的 writeText方法文档说,我们需要使用 Permission API 获得 clipboard-write权限,但是在 WebView 中没有定义 navigator.permission,可能是因为他们不想将 Web 权限和 Android OS 权限混在一起。

还有一种方法可以将文本从 Android WebView 复制到剪贴板: 从 WebView JavaScript (JS)代码调用本机 Java 方法。

在 WebView 中启用 JS:

myWebView.getSettings().setJavaScriptEnabled(true);

添加 JS 界面:

myWebView.addJavascriptInterface(new WebAppInterface(), "NativeAndroid");

创建一个使用 android.content.ClipboardManager将文本复制到剪贴板的方法:

public class WebAppInterface {
@JavascriptInterface
public void copyToClipboard(String text) {
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("demo", text);
clipboard.setPrimaryClip(clip);
}
}

然后使用 testClipboard调用上述方法:

function testClipboard() {
navigator.clipboard.writeText("Clipboard API Test").then(
v => alert("Success"),
e => alert("Fail\n" + e));
    

NativeAndroid.copyToClipboard("Clipboard API Test");
}

也许我的解决方案能帮到别人。我警告你,这是个糟糕的解决方案:)但只对我有用

关键在于,当在 WebView 中使用 navigator.clipboard.writeText()进行复制时,会发生 DOMException,它将调用 WebChromeClient 的 onConsole Message 方法。

适合于那些没有访问 js 代码的人。

webView.webChromeClient = object : WebChromeClient() {
override fun onConsoleMessage(
message: String?,
lineNumber: Int,
sourceID: String?
) {
if(url?.contains("https://some-link.com") == true // the page where the copying takes place
&& message?.contains("object DOMException") == true) { // navigator.clipboard.writeText() causes this exception
evaluateJavascript(
"document.getElementById('text_to_be_copied').innerText"
) { text ->
val myClipboard: ClipboardManager = getSystemService(requireContext(), ClipboardManager::class.java) as ClipboardManager
val clip = ClipData.newPlainText("text", text)
myClipboard.setPrimaryClip(clip)
}
}
super.onConsoleMessage(message, lineNumber, sourceID)
}
}