Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using ADBKeyboard for injecting text #1751

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,7 @@ guess_record_format(const char *filename) {
#define OPT_DISABLE_SCREENSAVER 1020
#define OPT_SHORTCUT_MOD 1021
#define OPT_NO_KEY_REPEAT 1022
#define OPT_USE_ADB_KEYBOARD 1026
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some numbers don't exist :trollface:

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the PR +1

Ideally, I would like the IME code to be included in scrcpy-server: #37 (comment)

Moreover, this would also allow a socket connection between the server and the IME (to minimize latency).

However, until this is implemented (I have zero time to work on this currently), I think calling ADBkeyboard is an acceptable solution.

If this option is passed, I think it should also save the current IME, set the ADBKeyboard IME, and it should restore the previous one on cleanup.

All of these is right. 👍


bool
scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
Expand Down Expand Up @@ -688,6 +689,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
{"show-touches", no_argument, NULL, 't'},
{"stay-awake", no_argument, NULL, 'w'},
{"turn-screen-off", no_argument, NULL, 'S'},
{"use-adb-keyboard", no_argument, NULL, OPT_USE_ADB_KEYBOARD},
{"verbosity", required_argument, NULL, 'V'},
{"version", no_argument, NULL, 'v'},
{"window-title", required_argument, NULL, OPT_WINDOW_TITLE},
Expand Down Expand Up @@ -773,6 +775,9 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
case 'S':
opts->turn_screen_off = true;
break;
case OPT_USE_ADB_KEYBOARD:
opts->use_adb_keyboard = true;
break;
case 't':
opts->show_touches = true;
break;
Expand Down
1 change: 1 addition & 0 deletions app/src/scrcpy.c
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ scrcpy(const struct scrcpy_options *options) {
.stay_awake = options->stay_awake,
.codec_options = options->codec_options,
.force_adb_forward = options->force_adb_forward,
.use_adb_keyboard = options->use_adb_keyboard,
};
if (!server_start(&server, options->serial, &params)) {
return false;
Expand Down
2 changes: 2 additions & 0 deletions app/src/scrcpy.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ struct scrcpy_options {
bool force_adb_forward;
bool disable_screensaver;
bool forward_key_repeat;
bool use_adb_keyboard;
};

#define SCRCPY_OPTIONS_DEFAULT { \
Expand Down Expand Up @@ -123,6 +124,7 @@ struct scrcpy_options {
.force_adb_forward = false, \
.disable_screensaver = false, \
.forward_key_repeat = true, \
.use_adb_keyboard = false, \
}

bool
Expand Down
1 change: 1 addition & 0 deletions app/src/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ execute_server(struct server *server, const struct server_params *params) {
params->show_touches ? "true" : "false",
params->stay_awake ? "true" : "false",
params->codec_options ? params->codec_options : "-",
params->use_adb_keyboard ? "true" : "false",
};
#ifdef SERVER_DEBUGGER
LOGI("Server debugger waiting for a client on device port "
Expand Down
1 change: 1 addition & 0 deletions app/src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ struct server_params {
bool show_touches;
bool stay_awake;
bool force_adb_forward;
bool use_adb_keyboard;
};

// init default values
Expand Down
32 changes: 22 additions & 10 deletions server/src/main/java/com/genymobile/scrcpy/Controller.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class Controller {

private final Device device;
private final DesktopConnection connection;
private final Options options;
private final DeviceMessageSender sender;

private final KeyCharacterMap charMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
Expand All @@ -31,9 +32,10 @@ public class Controller {

private boolean keepPowerModeOff;

public Controller(Device device, DesktopConnection connection) {
public Controller(Device device, DesktopConnection connection, Options options) {
this.device = device;
this.connection = connection;
this.options = options;
initPointers();
sender = new DeviceMessageSender(connection);
}
Expand Down Expand Up @@ -145,18 +147,28 @@ private boolean injectKeycode(int action, int keycode, int repeat, int metaState
}

private boolean injectChar(char c) {
String decomposed = KeyComposition.decompose(c);
char[] chars = decomposed != null ? decomposed.toCharArray() : new char[]{c};
KeyEvent[] events = charMap.getEvents(chars);
if (events == null) {
return false;
}
for (KeyEvent event : events) {
if (!device.injectEvent(event)) {
if (options.useADBKeyboard()) {
// Process latin keys the same way in order to provide same reaction speed.
try {
Process process = Runtime.getRuntime().exec("am broadcast -a ADB_INPUT_CHARS --eia chars " + String.valueOf((int) c));
return process.waitFor() == 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should send the broadcast programmatically (to avoid execute a new process and add latency for every letter).

I guess this involves retrieving an IActivityManager by calling ActivityManager.getService() (by introspection) (or ActivityManagerNative.getService() on older versions), and calling broadcastIntent().

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that method stable enough? I only have A9 devices and there is something made about activities in A10. I'm not Android developer though.

} catch (Throwable throwable) {
return false;
}
} else {
String decomposed = KeyComposition.decompose(c);
char[] chars = decomposed != null ? decomposed.toCharArray() : new char[]{c};
KeyEvent[] events = charMap.getEvents(chars);
if (events == null) {
return false;
}
for (KeyEvent event : events) {
if (!device.injectEvent(event)) {
return false;
}
}
return true;
}
return true;
}

private int injectText(String text) {
Expand Down
9 changes: 9 additions & 0 deletions server/src/main/java/com/genymobile/scrcpy/Options.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class Options {
private boolean showTouches;
private boolean stayAwake;
private String codecOptions;
private boolean useADBKeyboard;

public Ln.Level getLogLevel() {
return logLevel;
Expand Down Expand Up @@ -120,4 +121,12 @@ public String getCodecOptions() {
public void setCodecOptions(String codecOptions) {
this.codecOptions = codecOptions;
}

public boolean useADBKeyboard() {
return useADBKeyboard;
}

public void setUseADBKeyboard(boolean useADBKeyboard) {
this.useADBKeyboard = useADBKeyboard;
}
}
7 changes: 5 additions & 2 deletions server/src/main/java/com/genymobile/scrcpy/Server.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ private static void scrcpy(Options options) throws IOException {
ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate(), options.getMaxFps(), codecOptions);

if (options.getControl()) {
final Controller controller = new Controller(device, connection);
final Controller controller = new Controller(device, connection, options);

// asynchronous
startController(controller);
Expand Down Expand Up @@ -120,7 +120,7 @@ private static Options createOptions(String... args) {
"The server version (" + BuildConfig.VERSION_NAME + ") does not match the client " + "(" + clientVersion + ")");
}

final int expectedParameters = 14;
final int expectedParameters = 15;
if (args.length != expectedParameters) {
throw new IllegalArgumentException("Expecting " + expectedParameters + " parameters");
}
Expand Down Expand Up @@ -167,6 +167,9 @@ private static Options createOptions(String... args) {
String codecOptions = args[13];
options.setCodecOptions(codecOptions);

boolean useADBKeyboard = Boolean.parseBoolean(args[14]);
options.setUseADBKeyboard(useADBKeyboard);

return options;
}

Expand Down