diff --git a/app/src/control_msg.c b/app/src/control_msg.c index 8908c546..7ed3f40e 100644 --- a/app/src/control_msg.c +++ b/app/src/control_msg.c @@ -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; diff --git a/app/src/control_msg.h b/app/src/control_msg.h index c1099c79..e63c30fa 100644 --- a/app/src/control_msg.h +++ b/app/src/control_msg.h @@ -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; }; }; diff --git a/app/tests/test_control_msg_serialize.c b/app/tests/test_control_msg_serialize.c index ef9247ca..650eda34 100644 --- a/app/tests/test_control_msg_serialize.c +++ b/app/tests/test_control_msg_serialize.c @@ -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; } diff --git a/server/src/main/java/com/genymobile/scrcpy/ControlMessage.java b/server/src/main/java/com/genymobile/scrcpy/ControlMessage.java index f8edd53c..b7b3f53b 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ControlMessage.java +++ b/server/src/main/java/com/genymobile/scrcpy/ControlMessage.java @@ -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; diff --git a/server/src/main/java/com/genymobile/scrcpy/ControlMessageReader.java b/server/src/main/java/com/genymobile/scrcpy/ControlMessageReader.java index e4ab8402..2f993695 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ControlMessageReader.java +++ b/server/src/main/java/com/genymobile/scrcpy/ControlMessageReader.java @@ -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(); diff --git a/server/src/main/java/com/genymobile/scrcpy/Controller.java b/server/src/main/java/com/genymobile/scrcpy/Controller.java index 92986241..6eba17a2 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Controller.java +++ b/server/src/main/java/com/genymobile/scrcpy/Controller.java @@ -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,12 @@ public class Controller { case ControlMessage.TYPE_ROTATE_DEVICE: Device.rotateDevice(); break; + case ControlMessage.TYPE_SCAN_MEDIA: + String path = msg.getText(); + Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); + intent.setData(Uri.fromFile(new File(path))); + Device.sendBroadcast(intent); + break; default: // do nothing } diff --git a/server/src/test/java/com/genymobile/scrcpy/ControlMessageReaderTest.java b/server/src/test/java/com/genymobile/scrcpy/ControlMessageReaderTest.java index da568486..6c0901a0 100644 --- a/server/src/test/java/com/genymobile/scrcpy/ControlMessageReaderTest.java +++ b/server/src/test/java/com/genymobile/scrcpy/ControlMessageReaderTest.java @@ -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();