Compare commits

..

13 Commits

Author SHA1 Message Date
Romain Vimont
dd73a71a15 Fix feature test macro
The expected feature test macro is _POSIX_C_SOURCE, not _POSIX_SOURCE.

Fixes #1726 <https://github.com/Genymobile/scrcpy/issues/1726>
2020-08-31 14:01:30 +02:00
Romain Vimont
c243fd4c3f Fix more log format warning
The expression port + 1 is promoted to int, but printed as uint16_t.

This is the same mistake fixed for a different log by
7eb16ce364.

Refs #1726 <https://github.com/Genymobile/scrcpy/issues/1726>
2020-08-31 13:40:32 +02:00
brunoais
0bf110dd5c Reset power mode only if screen is on
PR #1670 <https://github.com/Genymobile/scrcpy/pull/1670>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2020-08-21 12:34:59 +02:00
Romain Vimont
0c5e0a4f6d Make Device methods static when possible
The behavior of some methods do not depend on the user-provided options.
These methods can be static. This will allow to call them directly from
the cleanup process.
2020-08-21 12:34:50 +02:00
Romain Vimont
0be766e71a Add undetected device error message in FAQ 2020-08-20 19:14:45 +02:00
Romain Vimont
d02789ce21 List available shortcut keys on error
Fixes #1681 <https://github.com/Genymobile/scrcpy/issues/1681>

Suggested-by: Moritz Schulz <moritzleni@gmail.com>
2020-08-16 13:52:01 +02:00
Romain Vimont
6cc22e1c5b Reference --shortcut-mod from shortcuts list
Fixes #1681 <https://github.com/Genymobile/scrcpy/issues/1681>

Suggested-by: Moritz Schulz <moritzleni@gmail.com>
2020-08-16 13:44:42 +02:00
Romain Vimont
479d10dc22 Update links to v1.16 in README and BUILD 2020-08-10 20:34:51 +02:00
Romain Vimont
d7779d08e8 Bump version to 1.16 2020-08-10 20:09:28 +02:00
Romain Vimont
df4ba1b8b0 Merge branch 'master' into dev 2020-08-10 20:08:33 +02:00
Romain Vimont
198346d148 Add pinch-to-zoom simulation
If Ctrl is hold when the left-click button is pressed, enable
pinch-to-zoom to scale and rotate relative to the center of the screen.

Fixes #24 <https://github.com/Genymobile/scrcpy/issues/24>
2020-08-10 20:08:24 +02:00
Romain Vimont
95f1ea0d80 Fix clipboard paste condition
To avoid possible copy-paste loops between the computer and the device,
the device clipboard is not set if it already contains the expected
content.

But the condition was wrong: it was not set also if it was empty.

Refs 1223a72eb8
Fixes #1658 <https://github.com/Genymobile/scrcpy/issues/1658>
2020-08-10 14:37:32 +02:00
Romain Vimont
38940ffe89 Revert "Inject WAKEUP instead of POWER"
WAKEUP does not work on some devices.

Fixes #1655 <https://github.com/Genymobile/scrcpy/issues/1655>

This reverts commit 322f1512ea.
2020-08-09 17:10:27 +02:00
17 changed files with 156 additions and 58 deletions

View File

@@ -254,10 +254,10 @@ You can then [run](README.md#run) _scrcpy_.
## Prebuilt server ## Prebuilt server
- [`scrcpy-server-v1.15.1`][direct-scrcpy-server] - [`scrcpy-server-v1.16`][direct-scrcpy-server]
_(SHA-256: fe06bd6a30da8c89860bf5e16eecce2b5054d4644c84289670ce00ca5d1637c3)_ _(SHA-256: 94a79e05b4498d0460ab7bd9d12cbf05156e3a47bf0c5d1420cee1d4493b3832)_
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.15.1/scrcpy-server-v1.15.1 [direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.16/scrcpy-server-v1.16
Download the prebuilt server somewhere, and specify its path during the Meson Download the prebuilt server somewhere, and specify its path during the Meson
configuration: configuration:

2
FAQ.md
View File

@@ -37,6 +37,8 @@ Check [stackoverflow][device-unauthorized].
### Device not detected ### Device not detected
> adb: error: failed to get feature set: no devices/emulators found
If your device is not detected, you may need some [drivers] (on Windows). If your device is not detected, you may need some [drivers] (on Windows).
[drivers]: https://developer.android.com/studio/run/oem-usb.html [drivers]: https://developer.android.com/studio/run/oem-usb.html

View File

@@ -1,4 +1,4 @@
# scrcpy (v1.15.1) # scrcpy (v1.16)
[Read in another language](#translations) [Read in another language](#translations)
@@ -77,10 +77,10 @@ hard).
For Windows, for simplicity, a prebuilt archive with all the dependencies For Windows, for simplicity, a prebuilt archive with all the dependencies
(including `adb`) is available: (including `adb`) is available:
- [`scrcpy-win64-v1.15.1.zip`][direct-win64] - [`scrcpy-win64-v1.16.zip`][direct-win64]
_(SHA-256: 78fba4caad6328016ea93219254b5df391f24224c519a2c8e3f070695b8b38ff)_ _(SHA-256: 3f30dc5db1a2f95c2b40a0f5de91ec1642d9f53799250a8c529bc882bc0918f0)_
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.15.1/scrcpy-win64-v1.15.1.zip [direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.16/scrcpy-win64-v1.16.zip
It is also available in [Chocolatey]: It is also available in [Chocolatey]:
@@ -548,6 +548,19 @@ into the device clipboard. As a consequence, any Android application could read
its content. You should avoid to paste sensitive content (like passwords) that its content. You should avoid to paste sensitive content (like passwords) that
way. way.
#### Pinch-to-zoom
To simulate "pinch-to-zoom": <kbd>Ctrl</kbd>+_click-and-move_.
More precisely, hold <kbd>Ctrl</kbd> while pressing the left-click button. Until
the left-click button is released, all mouse movements scale and rotate the
content (if supported by the app) relative to the center of the screen.
Concretely, scrcpy generates additional touch events from a "virtual finger" at
a location inverted through the center of the screen.
#### Text injection preference #### Text injection preference
There are two kinds of [events][textevents] generated when typing text: There are two kinds of [events][textevents] generated when typing text:
@@ -661,6 +674,7 @@ _<kbd>[Super]</kbd> is typically the <kbd>Windows</kbd> or <kbd>Cmd</kbd> key._
| Synchronize clipboards and paste³ | <kbd>MOD</kbd>+<kbd>v</kbd> | Synchronize clipboards and paste³ | <kbd>MOD</kbd>+<kbd>v</kbd>
| Inject computer clipboard text | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>v</kbd> | Inject computer clipboard text | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>v</kbd>
| Enable/disable FPS counter (on stdout) | <kbd>MOD</kbd>+<kbd>i</kbd> | Enable/disable FPS counter (on stdout) | <kbd>MOD</kbd>+<kbd>i</kbd>
| Pinch-to-zoom | <kbd>Ctrl</kbd>+_click-and-move_
_¹Double-click on black borders to remove them._ _¹Double-click on black borders to remove them._
_²Right-click turns the screen on if it was off, presses BACK otherwise._ _²Right-click turns the screen on if it was off, presses BACK otherwise._

View File

@@ -222,7 +222,7 @@ Default is 0 (automatic).\n
.SH SHORTCUTS .SH SHORTCUTS
In the following list, MOD is the shortcut modifier. By default, it's (left) In the following list, MOD is the shortcut modifier. By default, it's (left)
Alt or (left) Super, but it can be configured by \-\-shortcut-mod. Alt or (left) Super, but it can be configured by \-\-shortcut-mod (see above).
.TP .TP
.B MOD+f .B MOD+f
@@ -316,6 +316,10 @@ Inject computer clipboard text as a sequence of key events
.B MOD+i .B MOD+i
Enable/disable FPS counter (print frames/second in logs) Enable/disable FPS counter (print frames/second in logs)
.TP
.B Ctrl+click-and-move
Pinch-to-zoom from the center of the screen
.TP .TP
.B Drag & drop APK file .B Drag & drop APK file
Install APK from computer Install APK from computer

View File

@@ -203,7 +203,7 @@ scrcpy_print_usage(const char *arg0) {
"\n" "\n"
" In the following list, MOD is the shortcut modifier. By default,\n" " In the following list, MOD is the shortcut modifier. By default,\n"
" it's (left) Alt or (left) Super, but it can be configured by\n" " it's (left) Alt or (left) Super, but it can be configured by\n"
" --shortcut-mod.\n" " --shortcut-mod (see above).\n"
"\n" "\n"
" MOD+f\n" " MOD+f\n"
" Switch fullscreen mode\n" " Switch fullscreen mode\n"
@@ -279,6 +279,9 @@ scrcpy_print_usage(const char *arg0) {
" MOD+i\n" " MOD+i\n"
" Enable/disable FPS counter (print frames/second in logs)\n" " Enable/disable FPS counter (print frames/second in logs)\n"
"\n" "\n"
" Ctrl+click-and-move\n"
" Pinch-to-zoom from the center of the screen\n"
"\n"
" Drag & drop APK file\n" " Drag & drop APK file\n"
" Install APK from computer\n" " Install APK from computer\n"
"\n", "\n",
@@ -529,7 +532,9 @@ parse_shortcut_mods_item(const char *item, size_t len) {
} else if (STREQ("rsuper", item, key_len)) { } else if (STREQ("rsuper", item, key_len)) {
mod |= SC_MOD_RSUPER; mod |= SC_MOD_RSUPER;
} else { } else {
LOGW("Unknown modifier key: %.*s", (int) key_len, item); LOGE("Unknown modifier key: %.*s "
"(must be one of: lctrl, rctrl, lalt, ralt, lsuper, rsuper)",
(int) key_len, item);
return 0; return 0;
} }
#undef STREQ #undef STREQ

View File

@@ -17,6 +17,7 @@
#define CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH (CONTROL_MSG_MAX_SIZE - 6) #define CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH (CONTROL_MSG_MAX_SIZE - 6)
#define POINTER_ID_MOUSE UINT64_C(-1); #define POINTER_ID_MOUSE UINT64_C(-1);
#define POINTER_ID_VIRTUAL_FINGER UINT64_C(-2);
enum control_msg_type { enum control_msg_type {
CONTROL_MSG_TYPE_INJECT_KEYCODE, CONTROL_MSG_TYPE_INJECT_KEYCODE,

View File

@@ -70,6 +70,8 @@ input_manager_init(struct input_manager *im,
im->sdl_shortcut_mods.data[i] = sdl_mod; im->sdl_shortcut_mods.data[i] = sdl_mod;
} }
im->sdl_shortcut_mods.count = shortcut_mods->count; im->sdl_shortcut_mods.count = shortcut_mods->count;
im->vfinger_down = false;
} }
static void static void
@@ -299,6 +301,36 @@ input_manager_process_text_input(struct input_manager *im,
} }
} }
static bool
simulate_virtual_finger(struct input_manager *im,
enum android_motionevent_action action,
struct point point) {
bool up = action == AMOTION_EVENT_ACTION_UP;
struct control_msg msg;
msg.type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
msg.inject_touch_event.action = action;
msg.inject_touch_event.position.screen_size = im->screen->frame_size;
msg.inject_touch_event.position.point = point;
msg.inject_touch_event.pointer_id = POINTER_ID_VIRTUAL_FINGER;
msg.inject_touch_event.pressure = up ? 0.0f : 1.0f;
msg.inject_touch_event.buttons = 0;
if (!controller_push_msg(im->controller, &msg)) {
LOGW("Could not request 'inject virtual finger event'");
return false;
}
return true;
}
static struct point
inverse_point(struct point point, struct size size) {
point.x = size.width - point.x;
point.y = size.height - point.y;
return point;
}
static bool static bool
convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to, convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to,
bool prefer_text, uint32_t repeat) { bool prefer_text, uint32_t repeat) {
@@ -512,10 +544,18 @@ input_manager_process_mouse_motion(struct input_manager *im,
return; return;
} }
struct control_msg msg; struct control_msg msg;
if (convert_mouse_motion(event, im->screen, &msg)) { if (!convert_mouse_motion(event, im->screen, &msg)) {
return;
}
if (!controller_push_msg(im->controller, &msg)) { if (!controller_push_msg(im->controller, &msg)) {
LOGW("Could not request 'inject mouse motion event'"); LOGW("Could not request 'inject mouse motion event'");
} }
if (im->vfinger_down) {
struct point mouse = msg.inject_touch_event.position.point;
struct point vfinger = inverse_point(mouse, im->screen->frame_size);
simulate_virtual_finger(im, AMOTION_EVENT_ACTION_MOVE, vfinger);
} }
} }
@@ -587,7 +627,9 @@ input_manager_process_mouse_button(struct input_manager *im,
// simulated from touch events, so it's a duplicate // simulated from touch events, so it's a duplicate
return; return;
} }
if (event->type == SDL_MOUSEBUTTONDOWN) {
bool down = event->type == SDL_MOUSEBUTTONDOWN;
if (down) {
if (control && event->button == SDL_BUTTON_RIGHT) { if (control && event->button == SDL_BUTTON_RIGHT) {
press_back_or_turn_screen_on(im->controller); press_back_or_turn_screen_on(im->controller);
return; return;
@@ -618,10 +660,36 @@ input_manager_process_mouse_button(struct input_manager *im,
} }
struct control_msg msg; struct control_msg msg;
if (convert_mouse_button(event, im->screen, &msg)) { if (!convert_mouse_button(event, im->screen, &msg)) {
return;
}
if (!controller_push_msg(im->controller, &msg)) { if (!controller_push_msg(im->controller, &msg)) {
LOGW("Could not request 'inject mouse button event'"); LOGW("Could not request 'inject mouse button event'");
return;
} }
// Pinch-to-zoom simulation.
//
// If Ctrl is hold when the left-click button is pressed, then
// pinch-to-zoom mode is enabled: on every mouse event until the left-click
// button is released, an additional "virtual finger" event is generated,
// having a position inverted through the center of the screen.
//
// In other words, the center of the rotation/scaling is the center of the
// screen.
#define CTRL_PRESSED (SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL))
if ((down && !im->vfinger_down && CTRL_PRESSED)
|| (!down && im->vfinger_down)) {
struct point mouse = msg.inject_touch_event.position.point;
struct point vfinger = inverse_point(mouse, im->screen->frame_size);
enum android_motionevent_action action = down
? AMOTION_EVENT_ACTION_DOWN
: AMOTION_EVENT_ACTION_UP;
if (!simulate_virtual_finger(im, action, vfinger)) {
return;
}
im->vfinger_down = down;
} }
} }

View File

@@ -30,6 +30,8 @@ struct input_manager {
unsigned data[SC_MAX_SHORTCUT_MODS]; unsigned data[SC_MAX_SHORTCUT_MODS];
unsigned count; unsigned count;
} sdl_shortcut_mods; } sdl_shortcut_mods;
bool vfinger_down;
}; };
void void

View File

@@ -201,7 +201,7 @@ enable_tunnel_forward_any_port(struct server *server,
if (port < port_range.last) { if (port < port_range.last) {
LOGW("Could not forward port %" PRIu16", retrying on %" PRIu16, LOGW("Could not forward port %" PRIu16", retrying on %" PRIu16,
port, port + 1); port, (uint16_t) (port + 1));
port++; port++;
continue; continue;
} }

View File

@@ -1,6 +1,6 @@
// for portability // for portability (kill, readlink, strdup, strtok_r)
#define _POSIX_SOURCE // for kill() #define _POSIX_C_SOURCE 200809L
#define _BSD_SOURCE // for readlink() #define _BSD_SOURCE
// modern glibc will complain without this // modern glibc will complain without this
#define _DEFAULT_SOURCE #define _DEFAULT_SOURCE

View File

@@ -1,5 +1,5 @@
project('scrcpy', 'c', project('scrcpy', 'c',
version: '1.15.1', version: '1.16',
meson_version: '>= 0.48', meson_version: '>= 0.48',
default_options: [ default_options: [
'c_std=c11', 'c_std=c11',

View File

@@ -6,8 +6,8 @@ android {
applicationId "com.genymobile.scrcpy" applicationId "com.genymobile.scrcpy"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 29 targetSdkVersion 29
versionCode 18 versionCode 19
versionName "1.15.1" versionName "1.16"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
} }
buildTypes { buildTypes {

View File

@@ -12,7 +12,7 @@
set -e set -e
SCRCPY_DEBUG=false SCRCPY_DEBUG=false
SCRCPY_VERSION_NAME=1.15.1 SCRCPY_VERSION_NAME=1.16
PLATFORM=${ANDROID_PLATFORM:-29} PLATFORM=${ANDROID_PLATFORM:-29}
BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-29.0.2} BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-29.0.2}

View File

@@ -78,7 +78,9 @@ public final class CleanUp {
if (restoreNormalPowerMode) { if (restoreNormalPowerMode) {
Ln.i("Restoring normal power mode"); Ln.i("Restoring normal power mode");
if (Device.isScreenOn()) {
Device.setScreenPowerMode(Device.POWER_MODE_NORMAL); Device.setScreenPowerMode(Device.POWER_MODE_NORMAL);
} }
} }
} }
}

View File

@@ -54,11 +54,11 @@ public class Controller {
public void control() throws IOException { public void control() throws IOException {
// on start, power on the device // on start, power on the device
if (!device.isScreenOn()) { if (!Device.isScreenOn()) {
device.injectKeycode(KeyEvent.KEYCODE_WAKEUP); device.injectKeycode(KeyEvent.KEYCODE_POWER);
// dirty hack // dirty hack
// After the keycode is injected, the device is powered on asynchronously. // After POWER is injected, the device is powered on asynchronously.
// To turn the device screen off while mirroring, the client will send a message that // To turn the device screen off while mirroring, the client will send a message that
// would be handled before the device is actually powered on, so its effect would // would be handled before the device is actually powered on, so its effect would
// be "canceled" once the device is turned back on. // be "canceled" once the device is turned back on.
@@ -105,13 +105,13 @@ public class Controller {
} }
break; break;
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL: case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
device.expandNotificationPanel(); Device.expandNotificationPanel();
break; break;
case ControlMessage.TYPE_COLLAPSE_NOTIFICATION_PANEL: case ControlMessage.TYPE_COLLAPSE_NOTIFICATION_PANEL:
device.collapsePanels(); Device.collapsePanels();
break; break;
case ControlMessage.TYPE_GET_CLIPBOARD: case ControlMessage.TYPE_GET_CLIPBOARD:
String clipboardText = device.getClipboardText(); String clipboardText = Device.getClipboardText();
if (clipboardText != null) { if (clipboardText != null) {
sender.pushClipboardText(clipboardText); sender.pushClipboardText(clipboardText);
} }
@@ -130,7 +130,7 @@ public class Controller {
} }
break; break;
case ControlMessage.TYPE_ROTATE_DEVICE: case ControlMessage.TYPE_ROTATE_DEVICE:
device.rotateDevice(); Device.rotateDevice();
break; break;
default: default:
// do nothing // do nothing
@@ -248,8 +248,8 @@ public class Controller {
} }
private boolean pressBackOrTurnScreenOn() { private boolean pressBackOrTurnScreenOn() {
int keycode = device.isScreenOn() ? KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_WAKEUP; int keycode = Device.isScreenOn() ? KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_POWER;
if (keepPowerModeOff && keycode == KeyEvent.KEYCODE_WAKEUP) { if (keepPowerModeOff && keycode == KeyEvent.KEYCODE_POWER) {
schedulePowerModeOff(); schedulePowerModeOff();
} }
return device.injectKeycode(keycode); return device.injectKeycode(keycode);

View File

@@ -25,6 +25,8 @@ public final class Device {
public static final int POWER_MODE_OFF = SurfaceControl.POWER_MODE_OFF; public static final int POWER_MODE_OFF = SurfaceControl.POWER_MODE_OFF;
public static final int POWER_MODE_NORMAL = SurfaceControl.POWER_MODE_NORMAL; public static final int POWER_MODE_NORMAL = SurfaceControl.POWER_MODE_NORMAL;
private static final ServiceManager SERVICE_MANAGER = new ServiceManager();
public interface RotationListener { public interface RotationListener {
void onRotationChanged(int rotation); void onRotationChanged(int rotation);
} }
@@ -33,8 +35,6 @@ public final class Device {
void onClipboardTextChanged(String text); void onClipboardTextChanged(String text);
} }
private final ServiceManager serviceManager = new ServiceManager();
private ScreenInfo screenInfo; private ScreenInfo screenInfo;
private RotationListener rotationListener; private RotationListener rotationListener;
private ClipboardListener clipboardListener; private ClipboardListener clipboardListener;
@@ -54,9 +54,9 @@ public final class Device {
public Device(Options options) { public Device(Options options) {
displayId = options.getDisplayId(); displayId = options.getDisplayId();
DisplayInfo displayInfo = serviceManager.getDisplayManager().getDisplayInfo(displayId); DisplayInfo displayInfo = SERVICE_MANAGER.getDisplayManager().getDisplayInfo(displayId);
if (displayInfo == null) { if (displayInfo == null) {
int[] displayIds = serviceManager.getDisplayManager().getDisplayIds(); int[] displayIds = SERVICE_MANAGER.getDisplayManager().getDisplayIds();
throw new InvalidDisplayIdException(displayId, displayIds); throw new InvalidDisplayIdException(displayId, displayIds);
} }
@@ -65,7 +65,7 @@ public final class Device {
screenInfo = ScreenInfo.computeScreenInfo(displayInfo, options.getCrop(), options.getMaxSize(), options.getLockedVideoOrientation()); screenInfo = ScreenInfo.computeScreenInfo(displayInfo, options.getCrop(), options.getMaxSize(), options.getLockedVideoOrientation());
layerStack = displayInfo.getLayerStack(); layerStack = displayInfo.getLayerStack();
serviceManager.getWindowManager().registerRotationWatcher(new IRotationWatcher.Stub() { SERVICE_MANAGER.getWindowManager().registerRotationWatcher(new IRotationWatcher.Stub() {
@Override @Override
public void onRotationChanged(int rotation) { public void onRotationChanged(int rotation) {
synchronized (Device.this) { synchronized (Device.this) {
@@ -81,7 +81,7 @@ public final class Device {
if (options.getControl()) { if (options.getControl()) {
// If control is enabled, synchronize Android clipboard to the computer automatically // If control is enabled, synchronize Android clipboard to the computer automatically
ClipboardManager clipboardManager = serviceManager.getClipboardManager(); ClipboardManager clipboardManager = SERVICE_MANAGER.getClipboardManager();
if (clipboardManager != null) { if (clipboardManager != null) {
clipboardManager.addPrimaryClipChangedListener(new IOnPrimaryClipChangedListener.Stub() { clipboardManager.addPrimaryClipChangedListener(new IOnPrimaryClipChangedListener.Stub() {
@Override @Override
@@ -166,7 +166,7 @@ public final class Device {
return false; return false;
} }
return serviceManager.getInputManager().injectInputEvent(inputEvent, mode); return SERVICE_MANAGER.getInputManager().injectInputEvent(inputEvent, mode);
} }
public boolean injectEvent(InputEvent event) { public boolean injectEvent(InputEvent event) {
@@ -184,8 +184,8 @@ public final class Device {
return injectKeyEvent(KeyEvent.ACTION_DOWN, keyCode, 0, 0) && injectKeyEvent(KeyEvent.ACTION_UP, keyCode, 0, 0); return injectKeyEvent(KeyEvent.ACTION_DOWN, keyCode, 0, 0) && injectKeyEvent(KeyEvent.ACTION_UP, keyCode, 0, 0);
} }
public boolean isScreenOn() { public static boolean isScreenOn() {
return serviceManager.getPowerManager().isScreenOn(); return SERVICE_MANAGER.getPowerManager().isScreenOn();
} }
public synchronized void setRotationListener(RotationListener rotationListener) { public synchronized void setRotationListener(RotationListener rotationListener) {
@@ -196,16 +196,16 @@ public final class Device {
this.clipboardListener = clipboardListener; this.clipboardListener = clipboardListener;
} }
public void expandNotificationPanel() { public static void expandNotificationPanel() {
serviceManager.getStatusBarManager().expandNotificationsPanel(); SERVICE_MANAGER.getStatusBarManager().expandNotificationsPanel();
} }
public void collapsePanels() { public static void collapsePanels() {
serviceManager.getStatusBarManager().collapsePanels(); SERVICE_MANAGER.getStatusBarManager().collapsePanels();
} }
public String getClipboardText() { public static String getClipboardText() {
ClipboardManager clipboardManager = serviceManager.getClipboardManager(); ClipboardManager clipboardManager = SERVICE_MANAGER.getClipboardManager();
if (clipboardManager == null) { if (clipboardManager == null) {
return null; return null;
} }
@@ -217,13 +217,13 @@ public final class Device {
} }
public boolean setClipboardText(String text) { public boolean setClipboardText(String text) {
ClipboardManager clipboardManager = serviceManager.getClipboardManager(); ClipboardManager clipboardManager = SERVICE_MANAGER.getClipboardManager();
if (clipboardManager == null) { if (clipboardManager == null) {
return false; return false;
} }
String currentClipboard = getClipboardText(); String currentClipboard = getClipboardText();
if (currentClipboard == null || currentClipboard.equals(text)) { if (currentClipboard != null && currentClipboard.equals(text)) {
// The clipboard already contains the requested text. // The clipboard already contains the requested text.
// Since pasting text from the computer involves setting the device clipboard, it could be set twice on a copy-paste. This would cause // Since pasting text from the computer involves setting the device clipboard, it could be set twice on a copy-paste. This would cause
// the clipboard listeners to be notified twice, and that would flood the Android keyboard clipboard history. To workaround this // the clipboard listeners to be notified twice, and that would flood the Android keyboard clipboard history. To workaround this
@@ -252,8 +252,8 @@ public final class Device {
/** /**
* Disable auto-rotation (if enabled), set the screen rotation and re-enable auto-rotation (if it was enabled). * Disable auto-rotation (if enabled), set the screen rotation and re-enable auto-rotation (if it was enabled).
*/ */
public void rotateDevice() { public static void rotateDevice() {
WindowManager wm = serviceManager.getWindowManager(); WindowManager wm = SERVICE_MANAGER.getWindowManager();
boolean accelerometerRotation = !wm.isRotationFrozen(); boolean accelerometerRotation = !wm.isRotationFrozen();
@@ -270,7 +270,7 @@ public final class Device {
} }
} }
public ContentProvider createSettingsProvider() { public static ContentProvider createSettingsProvider() {
return serviceManager.getActivityManager().createSettingsProvider(); return SERVICE_MANAGER.getActivityManager().createSettingsProvider();
} }
} }

View File

@@ -26,7 +26,7 @@ public final class Server {
boolean mustDisableShowTouchesOnCleanUp = false; boolean mustDisableShowTouchesOnCleanUp = false;
int restoreStayOn = -1; int restoreStayOn = -1;
if (options.getShowTouches() || options.getStayAwake()) { if (options.getShowTouches() || options.getStayAwake()) {
try (ContentProvider settings = device.createSettingsProvider()) { try (ContentProvider settings = Device.createSettingsProvider()) {
if (options.getShowTouches()) { if (options.getShowTouches()) {
String oldValue = settings.getAndPutValue(ContentProvider.TABLE_SYSTEM, "show_touches", "1"); String oldValue = settings.getAndPutValue(ContentProvider.TABLE_SYSTEM, "show_touches", "1");
// If "show touches" was disabled, it must be disabled back on clean up // If "show touches" was disabled, it must be disabled back on clean up