Compare commits
6 Commits
tcpip_anyp
...
fix3568
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7b1d0ea9a | ||
|
|
18082f6069 | ||
|
|
8b38b11875 | ||
|
|
64821466a1 | ||
|
|
82cb8ab870 | ||
|
|
b51841e85d |
4
BUILD.md
4
BUILD.md
@@ -260,7 +260,7 @@ set ANDROID_SDK_ROOT=%LOCALAPPDATA%\Android\sdk
|
|||||||
Then, build:
|
Then, build:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
meson x --buildtype=release --strip -Db_lto=true
|
meson setup x --buildtype=release --strip -Db_lto=true
|
||||||
ninja -Cx # DO NOT RUN AS ROOT
|
ninja -Cx # DO NOT RUN AS ROOT
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -281,7 +281,7 @@ Download the prebuilt server somewhere, and specify its path during the Meson
|
|||||||
configuration:
|
configuration:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
meson x --buildtype=release --strip -Db_lto=true \
|
meson setup x --buildtype=release --strip -Db_lto=true \
|
||||||
-Dprebuilt_server=/path/to/scrcpy-server
|
-Dprebuilt_server=/path/to/scrcpy-server
|
||||||
ninja -Cx # DO NOT RUN AS ROOT
|
ninja -Cx # DO NOT RUN AS ROOT
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -277,7 +277,7 @@ The server is pushed to the device by the client on startup.
|
|||||||
To debug it, enable the server debugger during configuration:
|
To debug it, enable the server debugger during configuration:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
meson x -Dserver_debugger=true
|
meson setup x -Dserver_debugger=true
|
||||||
# or, if x is already configured
|
# or, if x is already configured
|
||||||
meson configure x -Dserver_debugger=true
|
meson configure x -Dserver_debugger=true
|
||||||
```
|
```
|
||||||
@@ -286,7 +286,7 @@ If your device runs Android 8 or below, set the `server_debugger_method` to
|
|||||||
`old` in addition:
|
`old` in addition:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
meson x -Dserver_debugger=true -Dserver_debugger_method=old
|
meson setup x -Dserver_debugger=true -Dserver_debugger_method=old
|
||||||
# or, if x is already configured
|
# or, if x is already configured
|
||||||
meson configure x -Dserver_debugger=true -Dserver_debugger_method=old
|
meson configure x -Dserver_debugger=true -Dserver_debugger_method=old
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -61,6 +61,22 @@ static const char *const copy_key_labels[] = {
|
|||||||
"cut",
|
"cut",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline const char *
|
||||||
|
get_well_known_pointer_id_name(uint64_t pointer_id) {
|
||||||
|
switch (pointer_id) {
|
||||||
|
case POINTER_ID_MOUSE:
|
||||||
|
return "mouse";
|
||||||
|
case POINTER_ID_GENERIC_FINGER:
|
||||||
|
return "finger";
|
||||||
|
case POINTER_ID_VIRTUAL_MOUSE:
|
||||||
|
return "vmouse";
|
||||||
|
case POINTER_ID_VIRTUAL_FINGER:
|
||||||
|
return "vfinger";
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
write_position(uint8_t *buf, const struct sc_position *position) {
|
write_position(uint8_t *buf, const struct sc_position *position) {
|
||||||
sc_write32be(&buf[0], position->point.x);
|
sc_write32be(&buf[0], position->point.x);
|
||||||
@@ -159,11 +175,12 @@ sc_control_msg_log(const struct sc_control_msg *msg) {
|
|||||||
int action = msg->inject_touch_event.action
|
int action = msg->inject_touch_event.action
|
||||||
& AMOTION_EVENT_ACTION_MASK;
|
& AMOTION_EVENT_ACTION_MASK;
|
||||||
uint64_t id = msg->inject_touch_event.pointer_id;
|
uint64_t id = msg->inject_touch_event.pointer_id;
|
||||||
if (id == POINTER_ID_MOUSE || id == POINTER_ID_VIRTUAL_FINGER) {
|
const char *pointer_name = get_well_known_pointer_id_name(id);
|
||||||
|
if (pointer_name) {
|
||||||
// string pointer id
|
// string pointer id
|
||||||
LOG_CMSG("touch [id=%s] %-4s position=%" PRIi32 ",%" PRIi32
|
LOG_CMSG("touch [id=%s] %-4s position=%" PRIi32 ",%" PRIi32
|
||||||
" pressure=%f buttons=%06lx",
|
" pressure=%f buttons=%06lx",
|
||||||
id == POINTER_ID_MOUSE ? "mouse" : "vfinger",
|
pointer_name,
|
||||||
MOTIONEVENT_ACTION_LABEL(action),
|
MOTIONEVENT_ACTION_LABEL(action),
|
||||||
msg->inject_touch_event.position.point.x,
|
msg->inject_touch_event.position.point.x,
|
||||||
msg->inject_touch_event.position.point.y,
|
msg->inject_touch_event.position.point.y,
|
||||||
|
|||||||
@@ -18,7 +18,11 @@
|
|||||||
#define SC_CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH (SC_CONTROL_MSG_MAX_SIZE - 14)
|
#define SC_CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH (SC_CONTROL_MSG_MAX_SIZE - 14)
|
||||||
|
|
||||||
#define POINTER_ID_MOUSE UINT64_C(-1)
|
#define POINTER_ID_MOUSE UINT64_C(-1)
|
||||||
#define POINTER_ID_VIRTUAL_FINGER UINT64_C(-2)
|
#define POINTER_ID_GENERIC_FINGER UINT64_C(-2)
|
||||||
|
|
||||||
|
// Used for injecting an additional virtual pointer for pinch-to-zoom
|
||||||
|
#define POINTER_ID_VIRTUAL_MOUSE UINT64_C(-3)
|
||||||
|
#define POINTER_ID_VIRTUAL_FINGER UINT64_C(-4)
|
||||||
|
|
||||||
enum sc_control_msg_type {
|
enum sc_control_msg_type {
|
||||||
SC_CONTROL_MSG_TYPE_INJECT_KEYCODE,
|
SC_CONTROL_MSG_TYPE_INJECT_KEYCODE,
|
||||||
|
|||||||
@@ -353,6 +353,7 @@ struct sc_mouse_click_event {
|
|||||||
struct sc_position position;
|
struct sc_position position;
|
||||||
enum sc_action action;
|
enum sc_action action;
|
||||||
enum sc_mouse_button button;
|
enum sc_mouse_button button;
|
||||||
|
uint64_t pointer_id;
|
||||||
uint8_t buttons_state; // bitwise-OR of sc_mouse_button values
|
uint8_t buttons_state; // bitwise-OR of sc_mouse_button values
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -365,6 +366,7 @@ struct sc_mouse_scroll_event {
|
|||||||
|
|
||||||
struct sc_mouse_motion_event {
|
struct sc_mouse_motion_event {
|
||||||
struct sc_position position;
|
struct sc_position position;
|
||||||
|
uint64_t pointer_id;
|
||||||
int32_t xrel;
|
int32_t xrel;
|
||||||
int32_t yrel;
|
int32_t yrel;
|
||||||
uint8_t buttons_state; // bitwise-OR of sc_mouse_button values
|
uint8_t buttons_state; // bitwise-OR of sc_mouse_button values
|
||||||
|
|||||||
@@ -335,7 +335,9 @@ simulate_virtual_finger(struct sc_input_manager *im,
|
|||||||
msg.inject_touch_event.action = action;
|
msg.inject_touch_event.action = action;
|
||||||
msg.inject_touch_event.position.screen_size = im->screen->frame_size;
|
msg.inject_touch_event.position.screen_size = im->screen->frame_size;
|
||||||
msg.inject_touch_event.position.point = point;
|
msg.inject_touch_event.position.point = point;
|
||||||
msg.inject_touch_event.pointer_id = POINTER_ID_VIRTUAL_FINGER;
|
msg.inject_touch_event.pointer_id =
|
||||||
|
im->forward_all_clicks ? POINTER_ID_VIRTUAL_MOUSE
|
||||||
|
: POINTER_ID_VIRTUAL_FINGER;
|
||||||
msg.inject_touch_event.pressure = up ? 0.0f : 1.0f;
|
msg.inject_touch_event.pressure = up ? 0.0f : 1.0f;
|
||||||
msg.inject_touch_event.buttons = 0;
|
msg.inject_touch_event.buttons = 0;
|
||||||
|
|
||||||
@@ -564,6 +566,8 @@ sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
|
|||||||
event->x,
|
event->x,
|
||||||
event->y),
|
event->y),
|
||||||
},
|
},
|
||||||
|
.pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE
|
||||||
|
: POINTER_ID_GENERIC_FINGER,
|
||||||
.xrel = event->xrel,
|
.xrel = event->xrel,
|
||||||
.yrel = event->yrel,
|
.yrel = event->yrel,
|
||||||
.buttons_state =
|
.buttons_state =
|
||||||
@@ -687,6 +691,8 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
|
|||||||
},
|
},
|
||||||
.action = sc_action_from_sdl_mousebutton_type(event->type),
|
.action = sc_action_from_sdl_mousebutton_type(event->type),
|
||||||
.button = sc_mouse_button_from_sdl(event->button),
|
.button = sc_mouse_button_from_sdl(event->button),
|
||||||
|
.pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE
|
||||||
|
: POINTER_ID_GENERIC_FINGER,
|
||||||
.buttons_state =
|
.buttons_state =
|
||||||
sc_mouse_buttons_state_from_sdl(sdl_buttons_state,
|
sc_mouse_buttons_state_from_sdl(sdl_buttons_state,
|
||||||
im->forward_all_clicks),
|
im->forward_all_clicks),
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ sc_mouse_processor_process_mouse_motion(struct sc_mouse_processor *mp,
|
|||||||
.type = SC_CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
.type = SC_CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
||||||
.inject_touch_event = {
|
.inject_touch_event = {
|
||||||
.action = AMOTION_EVENT_ACTION_MOVE,
|
.action = AMOTION_EVENT_ACTION_MOVE,
|
||||||
.pointer_id = POINTER_ID_MOUSE,
|
.pointer_id = event->pointer_id,
|
||||||
.position = event->position,
|
.position = event->position,
|
||||||
.pressure = 1.f,
|
.pressure = 1.f,
|
||||||
.buttons = convert_mouse_buttons(event->buttons_state),
|
.buttons = convert_mouse_buttons(event->buttons_state),
|
||||||
@@ -90,7 +90,7 @@ sc_mouse_processor_process_mouse_click(struct sc_mouse_processor *mp,
|
|||||||
.type = SC_CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
.type = SC_CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
||||||
.inject_touch_event = {
|
.inject_touch_event = {
|
||||||
.action = convert_mouse_action(event->action),
|
.action = convert_mouse_action(event->action),
|
||||||
.pointer_id = POINTER_ID_MOUSE,
|
.pointer_id = event->pointer_id,
|
||||||
.position = event->position,
|
.position = event->position,
|
||||||
.pressure = event->action == SC_ACTION_DOWN ? 1.f : 0.f,
|
.pressure = event->action == SC_ACTION_DOWN ? 1.f : 0.f,
|
||||||
.buttons = convert_mouse_buttons(event->buttons_state),
|
.buttons = convert_mouse_buttons(event->buttons_state),
|
||||||
|
|||||||
@@ -306,13 +306,14 @@ sc_screen_render(struct sc_screen *screen, bool update_content_rect) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if defined(__APPLE__) || defined(__WINDOWS__)
|
#if defined(__APPLE__)
|
||||||
# define CONTINUOUS_RESIZING_WORKAROUND
|
# define CONTINUOUS_RESIZING_WORKAROUND
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONTINUOUS_RESIZING_WORKAROUND
|
#ifdef CONTINUOUS_RESIZING_WORKAROUND
|
||||||
// On Windows and MacOS, resizing blocks the event loop, so resizing events are
|
// On Windows and MacOS, resizing blocks the event loop, so resizing events are
|
||||||
// not triggered. As a workaround, handle them in an event handler.
|
// not triggered. On MacOS, as a workaround, handle them in an event handler
|
||||||
|
// (it does not work for Windows unfortunately).
|
||||||
//
|
//
|
||||||
// <https://bugzilla.libsdl.org/show_bug.cgi?id=2077>
|
// <https://bugzilla.libsdl.org/show_bug.cgi?id=2077>
|
||||||
// <https://stackoverflow.com/a/40693139/1987178>
|
// <https://stackoverflow.com/a/40693139/1987178>
|
||||||
|
|||||||
28
release.mk
28
release.mk
@@ -24,13 +24,13 @@ SERVER_BUILD_DIR := build-server
|
|||||||
WIN32_BUILD_DIR := build-win32
|
WIN32_BUILD_DIR := build-win32
|
||||||
WIN64_BUILD_DIR := build-win64
|
WIN64_BUILD_DIR := build-win64
|
||||||
|
|
||||||
DIST := dist
|
|
||||||
WIN32_TARGET_DIR := scrcpy-win32
|
|
||||||
WIN64_TARGET_DIR := scrcpy-win64
|
|
||||||
|
|
||||||
VERSION := $(shell git describe --tags --always)
|
VERSION := $(shell git describe --tags --always)
|
||||||
WIN32_TARGET := $(WIN32_TARGET_DIR)-$(VERSION).zip
|
|
||||||
WIN64_TARGET := $(WIN64_TARGET_DIR)-$(VERSION).zip
|
DIST := dist
|
||||||
|
WIN32_TARGET_DIR := scrcpy-win32-$(VERSION)
|
||||||
|
WIN64_TARGET_DIR := scrcpy-win64-$(VERSION)
|
||||||
|
WIN32_TARGET := $(WIN32_TARGET_DIR).zip
|
||||||
|
WIN64_TARGET := $(WIN64_TARGET_DIR).zip
|
||||||
|
|
||||||
RELEASE_DIR := release-$(VERSION)
|
RELEASE_DIR := release-$(VERSION)
|
||||||
|
|
||||||
@@ -53,13 +53,13 @@ clean:
|
|||||||
|
|
||||||
test:
|
test:
|
||||||
[ -d "$(TEST_BUILD_DIR)" ] || ( mkdir "$(TEST_BUILD_DIR)" && \
|
[ -d "$(TEST_BUILD_DIR)" ] || ( mkdir "$(TEST_BUILD_DIR)" && \
|
||||||
meson "$(TEST_BUILD_DIR)" -Db_sanitize=address )
|
meson setup "$(TEST_BUILD_DIR)" -Db_sanitize=address )
|
||||||
ninja -C "$(TEST_BUILD_DIR)"
|
ninja -C "$(TEST_BUILD_DIR)"
|
||||||
$(GRADLE) -p server check
|
$(GRADLE) -p server check
|
||||||
|
|
||||||
build-server:
|
build-server:
|
||||||
[ -d "$(SERVER_BUILD_DIR)" ] || ( mkdir "$(SERVER_BUILD_DIR)" && \
|
[ -d "$(SERVER_BUILD_DIR)" ] || ( mkdir "$(SERVER_BUILD_DIR)" && \
|
||||||
meson "$(SERVER_BUILD_DIR)" --buildtype release -Dcompile_app=false )
|
meson setup "$(SERVER_BUILD_DIR)" --buildtype release -Dcompile_app=false )
|
||||||
ninja -C "$(SERVER_BUILD_DIR)"
|
ninja -C "$(SERVER_BUILD_DIR)"
|
||||||
|
|
||||||
prepare-deps-win32:
|
prepare-deps-win32:
|
||||||
@@ -76,7 +76,7 @@ prepare-deps-win64:
|
|||||||
|
|
||||||
build-win32: prepare-deps-win32
|
build-win32: prepare-deps-win32
|
||||||
[ -d "$(WIN32_BUILD_DIR)" ] || ( mkdir "$(WIN32_BUILD_DIR)" && \
|
[ -d "$(WIN32_BUILD_DIR)" ] || ( mkdir "$(WIN32_BUILD_DIR)" && \
|
||||||
meson "$(WIN32_BUILD_DIR)" \
|
meson setup "$(WIN32_BUILD_DIR)" \
|
||||||
--cross-file cross_win32.txt \
|
--cross-file cross_win32.txt \
|
||||||
--buildtype release --strip -Db_lto=true \
|
--buildtype release --strip -Db_lto=true \
|
||||||
-Dcompile_server=false \
|
-Dcompile_server=false \
|
||||||
@@ -85,7 +85,7 @@ build-win32: prepare-deps-win32
|
|||||||
|
|
||||||
build-win64: prepare-deps-win64
|
build-win64: prepare-deps-win64
|
||||||
[ -d "$(WIN64_BUILD_DIR)" ] || ( mkdir "$(WIN64_BUILD_DIR)" && \
|
[ -d "$(WIN64_BUILD_DIR)" ] || ( mkdir "$(WIN64_BUILD_DIR)" && \
|
||||||
meson "$(WIN64_BUILD_DIR)" \
|
meson setup "$(WIN64_BUILD_DIR)" \
|
||||||
--cross-file cross_win64.txt \
|
--cross-file cross_win64.txt \
|
||||||
--buildtype release --strip -Db_lto=true \
|
--buildtype release --strip -Db_lto=true \
|
||||||
-Dcompile_server=false \
|
-Dcompile_server=false \
|
||||||
@@ -131,9 +131,9 @@ dist-win64: build-server build-win64
|
|||||||
cp app/prebuilt-deps/data/libusb-1.0.26/MinGW-x64/msys-usb-1.0.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
cp app/prebuilt-deps/data/libusb-1.0.26/MinGW-x64/msys-usb-1.0.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||||
|
|
||||||
zip-win32: dist-win32
|
zip-win32: dist-win32
|
||||||
cd "$(DIST)/$(WIN32_TARGET_DIR)"; \
|
cd "$(DIST)"; \
|
||||||
zip -r "../$(WIN32_TARGET)" .
|
zip -r "$(WIN32_TARGET)" "$(WIN32_TARGET_DIR)"
|
||||||
|
|
||||||
zip-win64: dist-win64
|
zip-win64: dist-win64
|
||||||
cd "$(DIST)/$(WIN64_TARGET_DIR)"; \
|
cd "$(DIST)"; \
|
||||||
zip -r "../$(WIN64_TARGET)" .
|
zip -r "$(WIN64_TARGET)" "$(WIN64_TARGET_DIR)"
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testImplementation 'junit:junit:4.13.1'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$project.rootDir/config/android-checkstyle.gradle"
|
apply from: "$project.rootDir/config/android-checkstyle.gradle"
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ public class Controller {
|
|||||||
|
|
||||||
private static final int DEFAULT_DEVICE_ID = 0;
|
private static final int DEFAULT_DEVICE_ID = 0;
|
||||||
|
|
||||||
|
// control_msg.h values of the pointerId field in inject_touch_event message
|
||||||
|
private static final int POINTER_ID_MOUSE = -1;
|
||||||
|
private static final int POINTER_ID_VIRTUAL_MOUSE = -3;
|
||||||
|
|
||||||
private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor();
|
private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor();
|
||||||
|
|
||||||
private final Device device;
|
private final Device device;
|
||||||
@@ -194,7 +198,19 @@ public class Controller {
|
|||||||
pointer.setPressure(pressure);
|
pointer.setPressure(pressure);
|
||||||
pointer.setUp(action == MotionEvent.ACTION_UP);
|
pointer.setUp(action == MotionEvent.ACTION_UP);
|
||||||
|
|
||||||
|
int source;
|
||||||
int pointerCount = pointersState.update(pointerProperties, pointerCoords);
|
int pointerCount = pointersState.update(pointerProperties, pointerCoords);
|
||||||
|
if (pointerId == POINTER_ID_MOUSE || pointerId == POINTER_ID_VIRTUAL_MOUSE) {
|
||||||
|
// real mouse event (forced by the client when --forward-on-click)
|
||||||
|
pointerProperties[pointerIndex].toolType = MotionEvent.TOOL_TYPE_MOUSE;
|
||||||
|
source = InputDevice.SOURCE_MOUSE;
|
||||||
|
} else {
|
||||||
|
// POINTER_ID_GENERIC_FINGER, POINTER_ID_VIRTUAL_FINGER or real touch from device
|
||||||
|
pointerProperties[pointerIndex].toolType = MotionEvent.TOOL_TYPE_FINGER;
|
||||||
|
source = InputDevice.SOURCE_TOUCHSCREEN;
|
||||||
|
// Buttons must not be set for touch events
|
||||||
|
buttons = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (pointerCount == 1) {
|
if (pointerCount == 1) {
|
||||||
if (action == MotionEvent.ACTION_DOWN) {
|
if (action == MotionEvent.ACTION_DOWN) {
|
||||||
@@ -209,14 +225,6 @@ public class Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right-click and middle-click only work if the source is a mouse
|
|
||||||
boolean nonPrimaryButtonPressed = (buttons & ~MotionEvent.BUTTON_PRIMARY) != 0;
|
|
||||||
int source = nonPrimaryButtonPressed ? InputDevice.SOURCE_MOUSE : InputDevice.SOURCE_TOUCHSCREEN;
|
|
||||||
if (source != InputDevice.SOURCE_MOUSE) {
|
|
||||||
// Buttons must not be set for touch events
|
|
||||||
buttons = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
MotionEvent event = MotionEvent
|
MotionEvent event = MotionEvent
|
||||||
.obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, DEFAULT_DEVICE_ID, 0, source,
|
.obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, DEFAULT_DEVICE_ID, 0, source,
|
||||||
0);
|
0);
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ public class ClipboardManager {
|
|||||||
private Method getPrimaryClipMethod;
|
private Method getPrimaryClipMethod;
|
||||||
private Method setPrimaryClipMethod;
|
private Method setPrimaryClipMethod;
|
||||||
private Method addPrimaryClipChangedListener;
|
private Method addPrimaryClipChangedListener;
|
||||||
|
private boolean alternativeGetMethod;
|
||||||
|
private boolean alternativeSetMethod;
|
||||||
|
private boolean alternativeAddListenerMethod;
|
||||||
|
|
||||||
public ClipboardManager(IInterface manager) {
|
public ClipboardManager(IInterface manager) {
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
@@ -25,7 +28,12 @@ public class ClipboardManager {
|
|||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||||
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class);
|
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class);
|
||||||
} else {
|
} else {
|
||||||
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, int.class);
|
try {
|
||||||
|
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, int.class);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, String.class, int.class);
|
||||||
|
alternativeGetMethod = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return getPrimaryClipMethod;
|
return getPrimaryClipMethod;
|
||||||
@@ -36,23 +44,34 @@ public class ClipboardManager {
|
|||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||||
setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class);
|
setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class);
|
||||||
} else {
|
} else {
|
||||||
setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class, int.class);
|
try {
|
||||||
|
setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class, int.class);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class, String.class, int.class);
|
||||||
|
alternativeSetMethod = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return setPrimaryClipMethod;
|
return setPrimaryClipMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ClipData getPrimaryClip(Method method, IInterface manager) throws InvocationTargetException, IllegalAccessException {
|
private static ClipData getPrimaryClip(Method method, boolean alternativeMethod, IInterface manager)
|
||||||
|
throws InvocationTargetException, IllegalAccessException {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||||
return (ClipData) method.invoke(manager, ServiceManager.PACKAGE_NAME);
|
return (ClipData) method.invoke(manager, ServiceManager.PACKAGE_NAME);
|
||||||
}
|
}
|
||||||
|
if (alternativeMethod) {
|
||||||
|
return (ClipData) method.invoke(manager, ServiceManager.PACKAGE_NAME, null, ServiceManager.USER_ID);
|
||||||
|
}
|
||||||
return (ClipData) method.invoke(manager, ServiceManager.PACKAGE_NAME, ServiceManager.USER_ID);
|
return (ClipData) method.invoke(manager, ServiceManager.PACKAGE_NAME, ServiceManager.USER_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setPrimaryClip(Method method, IInterface manager, ClipData clipData)
|
private static void setPrimaryClip(Method method, boolean alternativeMethod, IInterface manager, ClipData clipData)
|
||||||
throws InvocationTargetException, IllegalAccessException {
|
throws InvocationTargetException, IllegalAccessException {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||||
method.invoke(manager, clipData, ServiceManager.PACKAGE_NAME);
|
method.invoke(manager, clipData, ServiceManager.PACKAGE_NAME);
|
||||||
|
} else if (alternativeMethod) {
|
||||||
|
method.invoke(manager, clipData, ServiceManager.PACKAGE_NAME, null, ServiceManager.USER_ID);
|
||||||
} else {
|
} else {
|
||||||
method.invoke(manager, clipData, ServiceManager.PACKAGE_NAME, ServiceManager.USER_ID);
|
method.invoke(manager, clipData, ServiceManager.PACKAGE_NAME, ServiceManager.USER_ID);
|
||||||
}
|
}
|
||||||
@@ -61,7 +80,7 @@ public class ClipboardManager {
|
|||||||
public CharSequence getText() {
|
public CharSequence getText() {
|
||||||
try {
|
try {
|
||||||
Method method = getGetPrimaryClipMethod();
|
Method method = getGetPrimaryClipMethod();
|
||||||
ClipData clipData = getPrimaryClip(method, manager);
|
ClipData clipData = getPrimaryClip(method, alternativeGetMethod, manager);
|
||||||
if (clipData == null || clipData.getItemCount() == 0) {
|
if (clipData == null || clipData.getItemCount() == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -76,7 +95,7 @@ public class ClipboardManager {
|
|||||||
try {
|
try {
|
||||||
Method method = getSetPrimaryClipMethod();
|
Method method = getSetPrimaryClipMethod();
|
||||||
ClipData clipData = ClipData.newPlainText(null, text);
|
ClipData clipData = ClipData.newPlainText(null, text);
|
||||||
setPrimaryClip(method, manager, clipData);
|
setPrimaryClip(method, alternativeSetMethod, manager, clipData);
|
||||||
return true;
|
return true;
|
||||||
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||||
Ln.e("Could not invoke method", e);
|
Ln.e("Could not invoke method", e);
|
||||||
@@ -84,10 +103,12 @@ public class ClipboardManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addPrimaryClipChangedListener(Method method, IInterface manager, IOnPrimaryClipChangedListener listener)
|
private static void addPrimaryClipChangedListener(Method method, boolean alternativeMethod, IInterface manager,
|
||||||
throws InvocationTargetException, IllegalAccessException {
|
IOnPrimaryClipChangedListener listener) throws InvocationTargetException, IllegalAccessException {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||||
method.invoke(manager, listener, ServiceManager.PACKAGE_NAME);
|
method.invoke(manager, listener, ServiceManager.PACKAGE_NAME);
|
||||||
|
} else if (alternativeMethod) {
|
||||||
|
method.invoke(manager, listener, ServiceManager.PACKAGE_NAME, null, ServiceManager.USER_ID);
|
||||||
} else {
|
} else {
|
||||||
method.invoke(manager, listener, ServiceManager.PACKAGE_NAME, ServiceManager.USER_ID);
|
method.invoke(manager, listener, ServiceManager.PACKAGE_NAME, ServiceManager.USER_ID);
|
||||||
}
|
}
|
||||||
@@ -99,8 +120,14 @@ public class ClipboardManager {
|
|||||||
addPrimaryClipChangedListener = manager.getClass()
|
addPrimaryClipChangedListener = manager.getClass()
|
||||||
.getMethod("addPrimaryClipChangedListener", IOnPrimaryClipChangedListener.class, String.class);
|
.getMethod("addPrimaryClipChangedListener", IOnPrimaryClipChangedListener.class, String.class);
|
||||||
} else {
|
} else {
|
||||||
addPrimaryClipChangedListener = manager.getClass()
|
try {
|
||||||
.getMethod("addPrimaryClipChangedListener", IOnPrimaryClipChangedListener.class, String.class, int.class);
|
addPrimaryClipChangedListener = manager.getClass()
|
||||||
|
.getMethod("addPrimaryClipChangedListener", IOnPrimaryClipChangedListener.class, String.class, int.class);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
addPrimaryClipChangedListener = manager.getClass()
|
||||||
|
.getMethod("addPrimaryClipChangedListener", IOnPrimaryClipChangedListener.class, String.class, String.class, int.class);
|
||||||
|
alternativeAddListenerMethod = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return addPrimaryClipChangedListener;
|
return addPrimaryClipChangedListener;
|
||||||
@@ -109,7 +136,7 @@ public class ClipboardManager {
|
|||||||
public boolean addPrimaryClipChangedListener(IOnPrimaryClipChangedListener listener) {
|
public boolean addPrimaryClipChangedListener(IOnPrimaryClipChangedListener listener) {
|
||||||
try {
|
try {
|
||||||
Method method = getAddPrimaryClipChangedListener();
|
Method method = getAddPrimaryClipChangedListener();
|
||||||
addPrimaryClipChangedListener(method, manager, listener);
|
addPrimaryClipChangedListener(method, alternativeAddListenerMethod, manager, listener);
|
||||||
return true;
|
return true;
|
||||||
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||||
Ln.e("Could not invoke method", e);
|
Ln.e("Could not invoke method", e);
|
||||||
|
|||||||
Reference in New Issue
Block a user