Compare commits
3 Commits
master
...
scan_media
Author | SHA1 | Date | |
---|---|---|---|
|
190a90f45d | ||
|
cda4aa1623 | ||
|
310f5721d9 |
@ -80,6 +80,13 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
|
||||
case CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE:
|
||||
buf[1] = msg->set_screen_power_mode.mode;
|
||||
return 2;
|
||||
case CONTROL_MSG_TYPE_SCAN_MEDIA:
|
||||
{
|
||||
size_t len = write_string(msg->scan_media.path,
|
||||
CONTROL_MSG_SCAN_MEDIA_PATH_MAX_LENGTH,
|
||||
&buf[1]);
|
||||
return 1 + len;
|
||||
}
|
||||
case CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL:
|
||||
case CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL:
|
||||
case CONTROL_MSG_TYPE_COLLAPSE_PANELS:
|
||||
@ -102,6 +109,9 @@ control_msg_destroy(struct control_msg *msg) {
|
||||
case CONTROL_MSG_TYPE_SET_CLIPBOARD:
|
||||
free(msg->set_clipboard.text);
|
||||
break;
|
||||
case CONTROL_MSG_TYPE_SCAN_MEDIA:
|
||||
free(msg->scan_media.path);
|
||||
break;
|
||||
default:
|
||||
// do nothing
|
||||
break;
|
||||
|
@ -17,6 +17,8 @@
|
||||
// type: 1 byte; paste flag: 1 byte; length: 4 bytes
|
||||
#define CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH (CONTROL_MSG_MAX_SIZE - 6)
|
||||
|
||||
#define CONTROL_MSG_SCAN_MEDIA_PATH_MAX_LENGTH 256
|
||||
|
||||
#define POINTER_ID_MOUSE UINT64_C(-1);
|
||||
#define POINTER_ID_VIRTUAL_FINGER UINT64_C(-2);
|
||||
|
||||
@ -33,6 +35,7 @@ enum control_msg_type {
|
||||
CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
||||
CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE,
|
||||
CONTROL_MSG_TYPE_ROTATE_DEVICE,
|
||||
CONTROL_MSG_TYPE_SCAN_MEDIA,
|
||||
};
|
||||
|
||||
enum screen_power_mode {
|
||||
@ -76,6 +79,9 @@ struct control_msg {
|
||||
struct {
|
||||
enum screen_power_mode mode;
|
||||
} set_screen_power_mode;
|
||||
struct {
|
||||
char *path; // owned, to be freed by free()
|
||||
} scan_media;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "control_msg.h"
|
||||
#include "controller.h"
|
||||
#include "util/log.h"
|
||||
|
||||
#define DEFAULT_PUSH_TARGET "/sdcard/"
|
||||
@ -14,7 +16,8 @@ file_handler_request_destroy(struct file_handler_request *req) {
|
||||
}
|
||||
|
||||
bool
|
||||
file_handler_init(struct file_handler *file_handler, const char *serial,
|
||||
file_handler_init(struct file_handler *file_handler,
|
||||
struct controller *controller, const char *serial,
|
||||
const char *push_target) {
|
||||
|
||||
cbuf_init(&file_handler->queue);
|
||||
@ -50,6 +53,8 @@ file_handler_init(struct file_handler *file_handler, const char *serial,
|
||||
|
||||
file_handler->push_target = push_target ? push_target : DEFAULT_PUSH_TARGET;
|
||||
|
||||
file_handler->controller = controller;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -103,6 +108,24 @@ file_handler_request(struct file_handler *file_handler,
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool
|
||||
request_scan_media(struct file_handler *file_handler) {
|
||||
struct control_msg msg;
|
||||
msg.type = CONTROL_MSG_TYPE_SCAN_MEDIA;
|
||||
msg.scan_media.path = strdup(file_handler->push_target);
|
||||
if (!msg.scan_media.path) {
|
||||
LOGW("Could not strdup() media path");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!controller_push_msg(file_handler->controller, &msg)) {
|
||||
LOGW("Could not request 'scan media'");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int
|
||||
run_file_handler(void *data) {
|
||||
struct file_handler *file_handler = data;
|
||||
@ -145,6 +168,7 @@ run_file_handler(void *data) {
|
||||
if (process_check_success(process, "adb push", false)) {
|
||||
LOGI("%s successfully pushed to %s", req.file,
|
||||
file_handler->push_target);
|
||||
request_scan_media(file_handler);
|
||||
} else {
|
||||
LOGE("Failed to push %s to %s", req.file,
|
||||
file_handler->push_target);
|
||||
|
@ -22,6 +22,7 @@ struct file_handler_request {
|
||||
struct file_handler_request_queue CBUF(struct file_handler_request, 16);
|
||||
|
||||
struct file_handler {
|
||||
struct controller *controller;
|
||||
char *serial;
|
||||
const char *push_target;
|
||||
sc_thread thread;
|
||||
@ -34,7 +35,8 @@ struct file_handler {
|
||||
};
|
||||
|
||||
bool
|
||||
file_handler_init(struct file_handler *file_handler, const char *serial,
|
||||
file_handler_init(struct file_handler *file_handler,
|
||||
struct controller *controller, const char *serial,
|
||||
const char *push_target);
|
||||
|
||||
void
|
||||
|
@ -297,14 +297,6 @@ scrcpy(const struct scrcpy_options *options) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (options->display && options->control) {
|
||||
if (!file_handler_init(&s->file_handler, s->server.serial,
|
||||
options->push_target)) {
|
||||
goto end;
|
||||
}
|
||||
file_handler_initialized = true;
|
||||
}
|
||||
|
||||
struct decoder *dec = NULL;
|
||||
bool needs_decoder = options->display;
|
||||
#ifdef HAVE_V4L2
|
||||
@ -353,6 +345,12 @@ scrcpy(const struct scrcpy_options *options) {
|
||||
goto end;
|
||||
}
|
||||
controller_started = true;
|
||||
|
||||
if (!file_handler_init(&s->file_handler, &s->controller,
|
||||
s->server.serial, options->push_target)) {
|
||||
goto end;
|
||||
}
|
||||
file_handler_initialized = true;
|
||||
}
|
||||
|
||||
const char *window_title =
|
||||
|
@ -278,6 +278,28 @@ static void test_serialize_rotate_device(void) {
|
||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||
}
|
||||
|
||||
static void test_serialize_scan_media(void) {
|
||||
struct control_msg msg = {
|
||||
.type = CONTROL_MSG_TYPE_SCAN_MEDIA,
|
||||
.scan_media = {
|
||||
.path = "/sdcard/Download/",
|
||||
},
|
||||
};
|
||||
|
||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = control_msg_serialize(&msg, buf);
|
||||
assert(size == 22);
|
||||
|
||||
const unsigned char expected[] = {
|
||||
CONTROL_MSG_TYPE_SCAN_MEDIA,
|
||||
0x00, 0x00, 0x00, 0x11, // path length
|
||||
'/', 's', 'd', 'c', 'a', 'r', 'd', '/',
|
||||
'D', 'o', 'w', 'n', 'l', 'o', 'a', 'd',
|
||||
'/' // path
|
||||
};
|
||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
@ -295,5 +317,6 @@ int main(int argc, char *argv[]) {
|
||||
test_serialize_set_clipboard();
|
||||
test_serialize_set_screen_power_mode();
|
||||
test_serialize_rotate_device();
|
||||
test_serialize_scan_media();
|
||||
return 0;
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ public final class ControlMessage {
|
||||
public static final int TYPE_SET_CLIPBOARD = 9;
|
||||
public static final int TYPE_SET_SCREEN_POWER_MODE = 10;
|
||||
public static final int TYPE_ROTATE_DEVICE = 11;
|
||||
public static final int TYPE_SCAN_MEDIA = 12;
|
||||
|
||||
private int type;
|
||||
private String text;
|
||||
@ -97,6 +98,13 @@ public final class ControlMessage {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public static ControlMessage createScanMedia(String path) {
|
||||
ControlMessage msg = new ControlMessage();
|
||||
msg.type = TYPE_SCAN_MEDIA;
|
||||
msg.text = path;
|
||||
return msg;
|
||||
}
|
||||
|
||||
public static ControlMessage createEmpty(int type) {
|
||||
ControlMessage msg = new ControlMessage();
|
||||
msg.type = type;
|
||||
|
@ -76,6 +76,9 @@ public class ControlMessageReader {
|
||||
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
|
||||
msg = parseSetScreenPowerMode();
|
||||
break;
|
||||
case ControlMessage.TYPE_SCAN_MEDIA:
|
||||
msg = parseScanMedia();
|
||||
break;
|
||||
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
|
||||
case ControlMessage.TYPE_EXPAND_SETTINGS_PANEL:
|
||||
case ControlMessage.TYPE_COLLAPSE_PANELS:
|
||||
@ -182,6 +185,14 @@ public class ControlMessageReader {
|
||||
return ControlMessage.createSetScreenPowerMode(mode);
|
||||
}
|
||||
|
||||
private ControlMessage parseScanMedia() {
|
||||
String path = parseString();
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
return ControlMessage.createScanMedia(path);
|
||||
}
|
||||
|
||||
private static Position readPosition(ByteBuffer buffer) {
|
||||
int x = buffer.getInt();
|
||||
int y = buffer.getInt();
|
||||
|
@ -1,5 +1,7 @@
|
||||
package com.genymobile.scrcpy;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.SystemClock;
|
||||
import android.view.InputDevice;
|
||||
@ -7,6 +9,7 @@ import android.view.KeyCharacterMap;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
@ -135,6 +138,13 @@ public class Controller {
|
||||
case ControlMessage.TYPE_ROTATE_DEVICE:
|
||||
Device.rotateDevice();
|
||||
break;
|
||||
case ControlMessage.TYPE_SCAN_MEDIA:
|
||||
String path = msg.getText();
|
||||
@SuppressWarnings("deprecation")
|
||||
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
|
||||
intent.setData(Uri.fromFile(new File(path)));
|
||||
Device.sendBroadcast(intent);
|
||||
break;
|
||||
default:
|
||||
// do nothing
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import com.genymobile.scrcpy.wrappers.SurfaceControl;
|
||||
import com.genymobile.scrcpy.wrappers.WindowManager;
|
||||
|
||||
import android.content.IOnPrimaryClipChangedListener;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
@ -299,4 +300,8 @@ public final class Device {
|
||||
public static ContentProvider createSettingsProvider() {
|
||||
return SERVICE_MANAGER.getActivityManager().createSettingsProvider();
|
||||
}
|
||||
|
||||
public static void sendBroadcast(Intent intent) {
|
||||
SERVICE_MANAGER.getActivityManager().sendBroadcast(intent);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,9 @@ package com.genymobile.scrcpy.wrappers;
|
||||
|
||||
import com.genymobile.scrcpy.Ln;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.IInterface;
|
||||
|
||||
@ -16,6 +18,7 @@ public class ActivityManager {
|
||||
private Method getContentProviderExternalMethod;
|
||||
private boolean getContentProviderExternalMethodNewVersion = true;
|
||||
private Method removeContentProviderExternalMethod;
|
||||
private Method broadcastIntentMethod;
|
||||
|
||||
public ActivityManager(IInterface manager) {
|
||||
this.manager = manager;
|
||||
@ -42,6 +45,22 @@ public class ActivityManager {
|
||||
return removeContentProviderExternalMethod;
|
||||
}
|
||||
|
||||
private Method getBroadcastIntentMethod() throws NoSuchMethodException {
|
||||
if (broadcastIntentMethod == null) {
|
||||
try {
|
||||
Class<?> iApplicationThreadClass = Class.forName("android.app.IApplicationThread");
|
||||
Class<?> iIntentReceiverClass = Class.forName("android.content.IIntentReceiver");
|
||||
broadcastIntentMethod = manager.getClass()
|
||||
.getMethod("broadcastIntent", iApplicationThreadClass, Intent.class, String.class, iIntentReceiverClass, int.class,
|
||||
String.class, Bundle.class, String[].class, int.class, Bundle.class, boolean.class, boolean.class, int.class);
|
||||
return broadcastIntentMethod;
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
return broadcastIntentMethod;
|
||||
}
|
||||
|
||||
private ContentProvider getContentProviderExternal(String name, IBinder token) {
|
||||
try {
|
||||
Method method = getGetContentProviderExternalMethod();
|
||||
@ -84,4 +103,13 @@ public class ActivityManager {
|
||||
public ContentProvider createSettingsProvider() {
|
||||
return getContentProviderExternal("settings", new Binder());
|
||||
}
|
||||
|
||||
public void sendBroadcast(Intent intent) {
|
||||
try {
|
||||
Method method = getBroadcastIntentMethod();
|
||||
method.invoke(manager, null, intent, null, null, 0, null, null, null, -1, null, true, false, ServiceManager.USER_ID);
|
||||
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||
Ln.e("Could not invoke method", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -314,6 +314,26 @@ public class ControlMessageReaderTest {
|
||||
Assert.assertEquals(ControlMessage.TYPE_ROTATE_DEVICE, event.getType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScanMedia() throws IOException {
|
||||
ControlMessageReader reader = new ControlMessageReader();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
dos.writeByte(ControlMessage.TYPE_SCAN_MEDIA);
|
||||
byte[] text = "/sdcard/Download/".getBytes(StandardCharsets.UTF_8);
|
||||
dos.writeInt(text.length);
|
||||
dos.write(text);
|
||||
|
||||
byte[] packet = bos.toByteArray();
|
||||
|
||||
reader.readFrom(new ByteArrayInputStream(packet));
|
||||
ControlMessage event = reader.next();
|
||||
|
||||
Assert.assertEquals(ControlMessage.TYPE_SCAN_MEDIA, event.getType());
|
||||
Assert.assertEquals("/sdcard/Download/", event.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiEvents() throws IOException {
|
||||
ControlMessageReader reader = new ControlMessageReader();
|
||||
|
Loading…
x
Reference in New Issue
Block a user