Установить текст буфера обмена через оболочку adb начиная с уровня API 11

До уровня 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 перечисленные методы устарели, а новые принимают ClipData в качестве аргумента. Как вы устанавливаете содержимое буфера обмена сейчас через adb shell?

2 ответа

Решение

Вы задали два разных вопроса здесь. Сервисные вызовы не связаны с функциями API.

Android в целом чрезмерно агрессивен в отношении того, чтобы помечать API как устаревшие. В этом случае это только означает, что появились новые функции с большей функциональностью. Функциональность getText(), hasText(), а также setText() все еще существует, и эти функции будут продолжать работать, но теперь они реализованы в виде тривиальных оболочек вокруг новых функций.

Что касается вызовов службы, то это внутренняя деталь реализации, и, как вы заметили, работа с версиями Android не гарантируется. Если вы загляните в исходный код Android, вы найдете следующие транзакции:

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 вызывающим 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;
    }
}

Сервис должен иметь фильтр намерений, который объявляет, что он способен обрабатывать MySetClipboard умышленное действие.

Я нашел способ сделать это с помощью 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>это новое содержимое буфера обмена Android. Обратите внимание, что этот текст должен быть экранирован, чтобы он не интерпретировался специально shна удаленном конце. На практике это означает окружение его одинарными кавычками и замену всех одинарных кавычек на '\''. Например, это будет нормально, если местная раковина - рыба:

      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

После того, как рыба его проанализирует, аргумент будет '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())
Другие вопросы по тегам