从API级别11开始,通过adb shell设置剪贴板文本

时间:2013-01-09 17:54:35

标签: android api adb clipboard

在API级别11之前,可以使用service上的adb shell程序设置剪贴板的内容:

service call SERVICE CODE [i32 INT | s16 STR] ...
Options:
    i32: Write the integer INT into the send parcel.
    s16: Write the UTF-16 string STR into the send parcel.

有三个整数代码来定义方法:

1 TRANSACTION_getClipboardText
2 TRANSACTION_setClipboardText
3 TRANSACTION_hasClipboardText

例如这个命令

$ adb shell service call clipboard 2 i32 1 i32 1 s16 "Hello Android!"

将剪贴板的内容设置为"Hello Android!"。从API级别11开始,列出的方法为deprecated,新的方法以ClipData为参数。如何通过adb shell设置剪贴板内容?

2 个答案:

答案 0 :(得分:10)

你在这里问了两个不同的问题。服务调用与API函数无关。

对于将API标记为已弃用,Android一般过于激进。在这种情况下,它只意味着有更多功能的新功能。 getText()hasText()setText()的功能仍然存在,并且这些功能将继续有效,但它们现在可以作为较新功能的简单包装实现。

就服务调用而言,这些是内部实现细节,并且您已经注意到不能保证在Android版本中工作。如果您查看Android source code,您会发现当前定义的这些交易:

TRANSACTION_setPrimaryClip = 1
TRANSACTION_getPrimaryClip = 2
TRANSACTION_getPrimaryClipDescription = 3
TRANSACTION_hasPrimaryClip = 4
TRANSACTION_addPrimaryClipChangedListener = 5
TRANSACTION_removePrimaryClipChangedListener = 6
TRANSACTION_hasClipboardText = 7

源代码还指出了这些事务需要的参数。不幸的是,TRANSACTION_setPrimaryClip需要一个ClipData,它不是i32或s16,因此与service call不兼容。然而,我们遇到了更大的问题;这些事务需要将调用包名称作为参数,剪贴板服务验证指定的包名称是否与调用uid匹配。使用adb shell时,调用uid是UID_ROOT或UID_SHELL,它们都不拥有任何包,因此无法通过该检查。简单地说,新的剪贴板服务不能以这种方式使用。

你能做些什么呢?您可以创建自己的服务,以便从命令行操作剪贴板并将其安装到您的设备上。我不知道是否有任何方法可以扩展service call,但您可以使用am startservice作为合适的替代品。如果您已创建并安装了该自定义剪贴板服务,则可以将其调用为:

am startservice -a MySetClipboard -e text "clipboard text"

实现此服务的代码可能如下所示:

public MyService extends Service {
    public int onStartCommand(Intent intent, int flags, int startId) {
        String text = intent.getStringExtra("text");
        ClipboardManager.setText(text);
        stopSelf();
        return START_NOT_STICKY;
    }
}

该服务应该有一个intent-filter,声明它能够处理MySetClipboard意图操作。

答案 1 :(得分:0)

我找到了一种使用 com.tengu.sharetoclipboard 执行此操作的方法。您使用 F-Droid 安装它,然后使用以下参数以 am 超过 adb 启动它:

adb shell am start-activity \
        -a android.intent.action.SEND \
        -e android.intent.extra.TEXT <sh-escaped-text> \
        -t 'text/plain' \
        com.tengu.sharetoclipboard

<sh-escaped-text> 是安卓剪贴板的新内容。请注意,此文本必须进行转义,以便远程端的 sh 不会对其进行特殊解释。实际上,这意味着用单引号将其括起来并用 '\'' 替换所有单引号。例如,如果本地 shell 是 fish,这会正常工作:

adb shell am start-activity \
        -a android.intent.action.SEND \
        -e android.intent.extra.TEXT '\'I\'\\\'\'m pretty sure $HOME is set.\'' \
        -t 'text/plain' \
        com.tengu.sharetoclipboard

fish 解析后,参数为 'I'\''m pretty sure $HOME is set.'。 sh解析后,参数为I'm pretty sure $HOME is set.

这里有一个 Python 脚本来简化这个过程:

#!/usr/bin/env python

import sys
import os

def shsafe(s):
    return "'" + s.replace("'", "'\\''") + "'"

def exec_adb(text):
    os.execvp('adb', [
        'adb', 'shell', 'am', 'start-activity',
        '-a', 'android.intent.action.SEND',
        '-e', 'android.intent.extra.TEXT', shsafe(text),
        '-t', 'text/plain',
        'com.tengu.sharetoclipboard',
    ])

if sys.stdin.isatty():
    if len(sys.argv) == 2:
        exec_adb(sys.argv[1])
    else:
        sys.stderr.write(
'''Send something to the android clipboard over ADB. Requires
com.tengu.sharetoclipboard.
acb <text>
<some command> | acb''')
        exit(1)
else:
    exec_adb(sys.stdin.read())