mirror of
https://github.com/Genymobile/scrcpy.git
synced 2026-04-21 01:33:36 +00:00
hook up device events
This commit is contained in:
parent
68b064703e
commit
d0eb577079
13 changed files with 438 additions and 18 deletions
|
|
@ -43,6 +43,14 @@ enum sc_control_msg_type {
|
|||
SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS,
|
||||
SC_CONTROL_MSG_TYPE_START_APP,
|
||||
SC_CONTROL_MSG_TYPE_RESET_VIDEO,
|
||||
SC_CONTROL_MSG_TYPE_MEDIA_STATE,
|
||||
SC_CONTROL_MSG_TYPE_MEDIA_SEEK,
|
||||
};
|
||||
|
||||
enum sc_screen_power_mode {
|
||||
// see <https://android.googlesource.com/platform/frameworks/base.git/+/pie-release-2/core/java/android/view/SurfaceControl.java#305>
|
||||
SC_SCREEN_POWER_MODE_OFF = 0,
|
||||
SC_SCREEN_POWER_MODE_NORMAL = 2,
|
||||
};
|
||||
|
||||
enum sc_copy_key {
|
||||
|
|
@ -111,6 +119,14 @@ struct sc_control_msg {
|
|||
struct {
|
||||
char *name;
|
||||
} start_app;
|
||||
struct {
|
||||
uint16_t player_id;
|
||||
uint64_t position;
|
||||
} media_seek;
|
||||
struct {
|
||||
uint16_t player_id;
|
||||
uint8_t state;
|
||||
} media_state;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,20 @@
|
|||
#include "util/binary.h"
|
||||
#include "util/log.h"
|
||||
|
||||
static int read_message(uint8_t **target, const uint8_t *src, const uint16_t size) {
|
||||
uint8_t *data = malloc(size + 1);
|
||||
if (!data) {
|
||||
LOG_OOM();
|
||||
return -1;
|
||||
}
|
||||
if (size) {
|
||||
data[size] = '\0';
|
||||
memcpy(data, src, size);
|
||||
}
|
||||
*target = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
sc_device_msg_deserialize(const uint8_t *buf, size_t len,
|
||||
struct sc_device_msg *msg) {
|
||||
|
|
@ -25,17 +39,10 @@ sc_device_msg_deserialize(const uint8_t *buf, size_t len,
|
|||
if (clipboard_len > len - 5) {
|
||||
return 0; // no complete message
|
||||
}
|
||||
char *text = malloc(clipboard_len + 1);
|
||||
if (!text) {
|
||||
LOG_OOM();
|
||||
if (read_message((uint8_t **)&msg->clipboard.text, &buf[5], clipboard_len) == -1) {
|
||||
return -1;
|
||||
}
|
||||
if (clipboard_len) {
|
||||
memcpy(text, &buf[5], clipboard_len);
|
||||
}
|
||||
text[clipboard_len] = '\0';
|
||||
|
||||
msg->clipboard.text = text;
|
||||
return 5 + clipboard_len;
|
||||
}
|
||||
case DEVICE_MSG_TYPE_ACK_CLIPBOARD: {
|
||||
|
|
@ -56,20 +63,42 @@ sc_device_msg_deserialize(const uint8_t *buf, size_t len,
|
|||
if (size < len - 5) {
|
||||
return 0; // not available
|
||||
}
|
||||
uint8_t *data = malloc(size);
|
||||
if (!data) {
|
||||
LOG_OOM();
|
||||
return -1;
|
||||
}
|
||||
if (size) {
|
||||
memcpy(data, &buf[5], size);
|
||||
}
|
||||
|
||||
msg->uhid_output.id = id;
|
||||
msg->uhid_output.size = size;
|
||||
msg->uhid_output.data = data;
|
||||
if (read_message(&msg->uhid_output.data, &buf[5], size) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 5 + size;
|
||||
case DEVICE_MSG_TYPE_MEDIA_UPDATE: {
|
||||
if (len < 5) {
|
||||
// at least id + size
|
||||
return 0; // not available
|
||||
}
|
||||
uint16_t id = sc_read16be(&buf[1]);
|
||||
size_t size = sc_read16be(&buf[3]);
|
||||
if (size < len - 5) {
|
||||
return 0; // not available
|
||||
}
|
||||
|
||||
msg->media_update.id = id;
|
||||
msg->media_update.size = size;
|
||||
if (read_message(&msg->media_update.data, &buf[5], size) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 5 + size;
|
||||
}
|
||||
case DEVICE_MSG_TYPE_MEDIA_REMOVE: {
|
||||
if (len < 3) {
|
||||
// at least id
|
||||
return 0; // not available
|
||||
}
|
||||
uint16_t id = sc_read16be(&buf[1]);
|
||||
msg->media_remove.id = id;
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
default:
|
||||
LOGW("Unknown device message type: %d", (int) msg->type);
|
||||
|
|
@ -86,6 +115,9 @@ sc_device_msg_destroy(struct sc_device_msg *msg) {
|
|||
case DEVICE_MSG_TYPE_UHID_OUTPUT:
|
||||
free(msg->uhid_output.data);
|
||||
break;
|
||||
case DEVICE_MSG_TYPE_MEDIA_UPDATE:
|
||||
free(msg->media_update.data);
|
||||
break;
|
||||
default:
|
||||
// nothing to do
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ enum sc_device_msg_type {
|
|||
DEVICE_MSG_TYPE_CLIPBOARD,
|
||||
DEVICE_MSG_TYPE_ACK_CLIPBOARD,
|
||||
DEVICE_MSG_TYPE_UHID_OUTPUT,
|
||||
DEVICE_MSG_TYPE_MEDIA_UPDATE,
|
||||
DEVICE_MSG_TYPE_MEDIA_REMOVE,
|
||||
};
|
||||
|
||||
struct sc_device_msg {
|
||||
|
|
@ -31,6 +33,14 @@ struct sc_device_msg {
|
|||
uint16_t size;
|
||||
uint8_t *data; // owned, to be freed by free()
|
||||
} uhid_output;
|
||||
struct {
|
||||
uint16_t id;
|
||||
uint16_t size;
|
||||
uint8_t *data; // owned, to be freed by free()
|
||||
} media_update;
|
||||
struct {
|
||||
uint16_t id;
|
||||
} media_remove;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -73,6 +73,22 @@ task_uhid_output(void *userdata) {
|
|||
free(data);
|
||||
}
|
||||
|
||||
static void
|
||||
dump_media_update(const struct sc_device_msg* msg) {
|
||||
uint8_t msg_type = 0;
|
||||
uint8_t *msg_ptr = NULL;
|
||||
for (int i = 0; i < msg->media_update.size; i++) {
|
||||
if (msg_ptr == NULL) {
|
||||
msg_type = msg->media_update.data[i];
|
||||
msg_ptr = &msg->media_update.data[i + 1];
|
||||
} else if (msg->media_update.data[i] == 0) {
|
||||
LOGI("Media update: %i, %s", (int)msg_type, msg_ptr);
|
||||
msg_ptr = NULL;
|
||||
msg_type = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
process_msg(struct sc_receiver *receiver, struct sc_device_msg *msg) {
|
||||
switch (msg->type) {
|
||||
|
|
@ -149,6 +165,12 @@ process_msg(struct sc_receiver *receiver, struct sc_device_msg *msg) {
|
|||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
case DEVICE_MSG_TYPE_MEDIA_UPDATE:
|
||||
dump_media_update(msg);
|
||||
break;
|
||||
case DEVICE_MSG_TYPE_MEDIA_REMOVE:
|
||||
LOGI("Media remove: %i", msg->media_remove.id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ public class Options {
|
|||
private int scid = -1; // 31-bit non-negative value, or -1
|
||||
private boolean video = true;
|
||||
private boolean audio = true;
|
||||
private boolean mediaControl = true;
|
||||
private int maxSize;
|
||||
private VideoCodec videoCodec = VideoCodec.H264;
|
||||
private AudioCodec audioCodec = AudioCodec.OPUS;
|
||||
|
|
@ -96,6 +97,10 @@ public class Options {
|
|||
return audio;
|
||||
}
|
||||
|
||||
public boolean getMediaControls() {
|
||||
return mediaControl;
|
||||
}
|
||||
|
||||
public int getMaxSize() {
|
||||
return maxSize;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,12 @@ import com.genymobile.scrcpy.video.ScreenCapture;
|
|||
import com.genymobile.scrcpy.video.SurfaceCapture;
|
||||
import com.genymobile.scrcpy.video.SurfaceEncoder;
|
||||
import com.genymobile.scrcpy.video.VideoSource;
|
||||
import com.genymobile.scrcpy.wrappers.MediaManager;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.media.MediaMetadata;
|
||||
import android.media.session.PlaybackState;
|
||||
import android.os.BatteryManager;
|
||||
import android.os.Build;
|
||||
import android.os.Looper;
|
||||
|
||||
|
|
@ -95,6 +99,7 @@ public final class Server {
|
|||
boolean control = options.getControl();
|
||||
boolean video = options.getVideo();
|
||||
boolean audio = options.getAudio();
|
||||
boolean media = options.getMediaControls();
|
||||
boolean sendDummyByte = options.getSendDummyByte();
|
||||
|
||||
Workarounds.apply();
|
||||
|
|
@ -113,6 +118,40 @@ public final class Server {
|
|||
ControlChannel controlChannel = connection.getControlChannel();
|
||||
controller = new Controller(controlChannel, cleanUp, options);
|
||||
asyncProcessors.add(controller);
|
||||
|
||||
if (media) {
|
||||
MediaManager mediaManager = MediaManager.create();
|
||||
|
||||
mediaManager.setMediaChangeListener(new MediaManager.MediaChange() {
|
||||
@Override
|
||||
public void onMetadataChange(int id, MediaMetadata metadata) {
|
||||
Ln.i("onMetadataChange " + id);
|
||||
byte[] data = MediaManager.mediaMetadataSerialize(metadata);
|
||||
DeviceMessage msg = DeviceMessage.createMediaUpdate(id, data);
|
||||
controller.getSender().send(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackStateChange(int id, PlaybackState playbackState) {
|
||||
Ln.i("onPlaybackStateChange " + id);
|
||||
int state = MediaManager.create().playbackStateSerialize(playbackState);
|
||||
if(state < 0) {
|
||||
return;
|
||||
}
|
||||
DeviceMessage msg = DeviceMessage.createMediaState(id, state);
|
||||
controller.getSender().send(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemove(int id) {
|
||||
Ln.i("onRemove " + id);
|
||||
DeviceMessage msg = DeviceMessage.createMediaRemove(id);
|
||||
controller.getSender().send(msg);
|
||||
}
|
||||
});
|
||||
|
||||
mediaManager.start();
|
||||
}
|
||||
}
|
||||
|
||||
if (audio) {
|
||||
|
|
@ -158,6 +197,9 @@ public final class Server {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Completion completion = new Completion(asyncProcessors.size());
|
||||
for (AsyncProcessor asyncProcessor : asyncProcessors) {
|
||||
asyncProcessor.start((fatalError) -> {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ public final class ControlMessage {
|
|||
public static final int TYPE_OPEN_HARD_KEYBOARD_SETTINGS = 15;
|
||||
public static final int TYPE_START_APP = 16;
|
||||
public static final int TYPE_RESET_VIDEO = 17;
|
||||
public static final int TYPE_MEDIA_STATE = 18;
|
||||
public static final int TYPE_MEDIA_SEEK = 19;
|
||||
|
||||
public static final long SEQUENCE_INVALID = 0;
|
||||
|
||||
|
|
@ -50,9 +52,15 @@ public final class ControlMessage {
|
|||
private long sequence;
|
||||
private int id;
|
||||
private byte[] data;
|
||||
<<<<<<< HEAD
|
||||
private boolean on;
|
||||
private int vendorId;
|
||||
private int productId;
|
||||
=======
|
||||
private int mediaState;
|
||||
private long mediaSeek;
|
||||
|
||||
>>>>>>> 38b99700 (hook up device events)
|
||||
|
||||
private ControlMessage() {
|
||||
}
|
||||
|
|
@ -159,10 +167,26 @@ public final class ControlMessage {
|
|||
return msg;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
public static ControlMessage createStartApp(String name) {
|
||||
ControlMessage msg = new ControlMessage();
|
||||
msg.type = TYPE_START_APP;
|
||||
msg.text = name;
|
||||
=======
|
||||
public static ControlMessage createMediaState(int receiverId, byte state) {
|
||||
ControlMessage msg = new ControlMessage();
|
||||
msg.type = TYPE_MEDIA_STATE;
|
||||
msg.id = receiverId;
|
||||
msg.mediaState = state;
|
||||
return msg;
|
||||
}
|
||||
|
||||
public static ControlMessage createMediaSeek(int receiverId, long position) {
|
||||
ControlMessage msg = new ControlMessage();
|
||||
msg.type = TYPE_MEDIA_STATE;
|
||||
msg.id = receiverId;
|
||||
msg.mediaSeek = position;
|
||||
>>>>>>> 38b99700 (hook up device events)
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
|
@ -238,6 +262,7 @@ public final class ControlMessage {
|
|||
return data;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
public boolean getOn() {
|
||||
return on;
|
||||
}
|
||||
|
|
@ -248,5 +273,13 @@ public final class ControlMessage {
|
|||
|
||||
public int getProductId() {
|
||||
return productId;
|
||||
=======
|
||||
public long getMediaSeek() {
|
||||
return mediaSeek;
|
||||
}
|
||||
|
||||
public int getMediaState() {
|
||||
return mediaState;
|
||||
>>>>>>> 38b99700 (hook up device events)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,11 +56,27 @@ public class ControlMessageReader {
|
|||
return parseUhidDestroy();
|
||||
case ControlMessage.TYPE_START_APP:
|
||||
return parseStartApp();
|
||||
case ControlMessage.TYPE_MEDIA_STATE:
|
||||
return parseMediaPlayStateRequest();
|
||||
case ControlMessage.TYPE_MEDIA_SEEK:
|
||||
return parseMediaSeekRequest();
|
||||
default:
|
||||
throw new ControlProtocolException("Unknown event type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
private ControlMessage parseMediaSeekRequest() throws IOException {
|
||||
int receiverId = dis.readUnsignedShort();
|
||||
long position = dis.readLong();
|
||||
return ControlMessage.createMediaSeek(receiverId, position);
|
||||
}
|
||||
|
||||
private ControlMessage parseMediaPlayStateRequest() throws IOException {
|
||||
int receiverId = dis.readUnsignedShort();
|
||||
byte state = dis.readByte();
|
||||
return ControlMessage.createMediaState(receiverId, state);
|
||||
}
|
||||
|
||||
private ControlMessage parseInjectKeycode() throws IOException {
|
||||
int action = dis.readUnsignedByte();
|
||||
int keycode = dis.readInt();
|
||||
|
|
|
|||
|
|
@ -330,6 +330,11 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||
break;
|
||||
case ControlMessage.TYPE_RESET_VIDEO:
|
||||
resetVideo();
|
||||
case ControlMessage.TYPE_MEDIA_STATE:
|
||||
mediaUpdateState(msg.getId(), msg.getMediaState());
|
||||
break;
|
||||
case ControlMessage.TYPE_MEDIA_SEEK:
|
||||
mediaSeek(msg.getId(), msg.getMediaSeek());
|
||||
break;
|
||||
default:
|
||||
// do nothing
|
||||
|
|
@ -338,6 +343,14 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||
return true;
|
||||
}
|
||||
|
||||
private void mediaUpdateState(int id, int mediaState) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
private void mediaSeek(int id, long mediaSeek) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
private boolean injectKeycode(int action, int keycode, int repeat, int metaState) {
|
||||
if (keepDisplayPowerOff && action == KeyEvent.ACTION_UP && (keycode == KeyEvent.KEYCODE_POWER || keycode == KeyEvent.KEYCODE_WAKEUP)) {
|
||||
assert displayId != Device.DISPLAY_ID_NONE;
|
||||
|
|
|
|||
|
|
@ -5,12 +5,16 @@ public final class DeviceMessage {
|
|||
public static final int TYPE_CLIPBOARD = 0;
|
||||
public static final int TYPE_ACK_CLIPBOARD = 1;
|
||||
public static final int TYPE_UHID_OUTPUT = 2;
|
||||
public static final int TYPE_MEDIA_UPDATE = 3;
|
||||
public static final int TYPE_MEDIA_REMOVE = 4;
|
||||
public static final int TYPE_MEDIA_STATE = 4;
|
||||
|
||||
private int type;
|
||||
private String text;
|
||||
private long sequence;
|
||||
private int id;
|
||||
private byte[] data;
|
||||
private int mediaState;
|
||||
|
||||
private DeviceMessage() {
|
||||
}
|
||||
|
|
@ -37,6 +41,29 @@ public final class DeviceMessage {
|
|||
return event;
|
||||
}
|
||||
|
||||
public static DeviceMessage createMediaUpdate(int id, byte[] data) {
|
||||
DeviceMessage event = new DeviceMessage();
|
||||
event.type = TYPE_MEDIA_UPDATE;
|
||||
event.id = id;
|
||||
event.data = data;
|
||||
return event;
|
||||
}
|
||||
|
||||
public static DeviceMessage createMediaState(int id, int state) {
|
||||
DeviceMessage event = new DeviceMessage();
|
||||
event.type = TYPE_MEDIA_STATE;
|
||||
event.id = id;
|
||||
event.mediaState = state;
|
||||
return event;
|
||||
}
|
||||
|
||||
public static DeviceMessage createMediaRemove(int id) {
|
||||
DeviceMessage event = new DeviceMessage();
|
||||
event.type = TYPE_MEDIA_REMOVE;
|
||||
event.id = id;
|
||||
return event;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ public class DeviceMessageWriter {
|
|||
public void write(DeviceMessage msg) throws IOException {
|
||||
int type = msg.getType();
|
||||
dos.writeByte(type);
|
||||
byte[] data;
|
||||
switch (type) {
|
||||
case DeviceMessage.TYPE_CLIPBOARD:
|
||||
String text = msg.getText();
|
||||
|
|
@ -35,10 +36,19 @@ public class DeviceMessageWriter {
|
|||
break;
|
||||
case DeviceMessage.TYPE_UHID_OUTPUT:
|
||||
dos.writeShort(msg.getId());
|
||||
byte[] data = msg.getData();
|
||||
data = msg.getData();
|
||||
dos.writeShort(data.length);
|
||||
dos.write(data);
|
||||
break;
|
||||
case DeviceMessage.TYPE_MEDIA_UPDATE:
|
||||
dos.writeShort(msg.getId());
|
||||
data = msg.getData();
|
||||
dos.writeShort(data.length);
|
||||
dos.write(data);
|
||||
break;
|
||||
case DeviceMessage.TYPE_MEDIA_REMOVE:
|
||||
dos.writeShort(msg.getId());
|
||||
break;
|
||||
default:
|
||||
throw new ControlProtocolException("Unknown event type: " + type);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,186 @@
|
|||
package com.genymobile.scrcpy.wrappers;
|
||||
|
||||
import android.media.MediaMetadata;
|
||||
import android.media.session.MediaController;
|
||||
import android.media.session.MediaSessionManager;
|
||||
import android.media.session.PlaybackState;
|
||||
|
||||
import com.genymobile.scrcpy.FakeContext;
|
||||
import com.genymobile.scrcpy.util.Ln;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class MediaManager {
|
||||
final static byte KEY_DURATION = 0;
|
||||
final static byte KEY_ALBUM = 1;
|
||||
final static byte KEY_ARTIST = 2;
|
||||
final static byte KEY_TITLE = 3;
|
||||
|
||||
final static byte STATE_PLAYING = 0;
|
||||
final static byte STATE_STOPPED = 1;
|
||||
final static byte STATE_PAUSED = 2;
|
||||
|
||||
MediaSessionManager sessionManager;
|
||||
private MediaChange mediaChangeListener;
|
||||
boolean started = false;
|
||||
|
||||
public interface MediaChange {
|
||||
void onMetadataChange(int id, MediaMetadata metadata);
|
||||
void onPlaybackStateChange(int id, PlaybackState playbackState);
|
||||
void onRemove(int id);
|
||||
}
|
||||
public static MediaManager create() {
|
||||
MediaSessionManager manager = FakeContext.get().getSystemService(MediaSessionManager.class);
|
||||
return new MediaManager(manager);
|
||||
}
|
||||
|
||||
int nextId = 0;
|
||||
HashMap<String, Integer> idMap = new HashMap<>();
|
||||
List<MediaController> mediaControllers = Collections.emptyList();
|
||||
|
||||
|
||||
private MediaManager(MediaSessionManager sessionManager) {
|
||||
this.sessionManager = sessionManager;
|
||||
|
||||
}
|
||||
|
||||
public void setMediaChangeListener(MediaChange listener) {
|
||||
this.mediaChangeListener = listener;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (started) {
|
||||
return;
|
||||
}
|
||||
|
||||
sessionManager.addOnActiveSessionsChangedListener(new MediaSessionManager.OnActiveSessionsChangedListener() {
|
||||
@Override
|
||||
public void onActiveSessionsChanged(List<MediaController> controllers) {
|
||||
Ln.i("MediaManager: Active Sessions changed");
|
||||
if (controllers == null) {
|
||||
controllers = Collections.emptyList();
|
||||
}
|
||||
// add
|
||||
for(MediaController controller : controllers) {
|
||||
if (!mediaControllers.contains(controller)) {
|
||||
addMediaController(controller);
|
||||
}
|
||||
}
|
||||
for(MediaController controller: mediaControllers) {
|
||||
if (!controllers.contains(controller)) {
|
||||
removeMediaController(controller);
|
||||
}
|
||||
}
|
||||
|
||||
mediaControllers = new ArrayList<>(controllers);
|
||||
}
|
||||
}, null);
|
||||
|
||||
mediaControllers = sessionManager.getActiveSessions(null);
|
||||
for (MediaController controller : mediaControllers) {
|
||||
addMediaController(controller);
|
||||
}
|
||||
|
||||
started = true;
|
||||
}
|
||||
|
||||
public static byte[] mediaMetadataSerialize(MediaMetadata metadata) {
|
||||
ArrayList<Byte> payload = new ArrayList<Byte>();
|
||||
for (String key : metadata.keySet()) {
|
||||
byte field_id;
|
||||
byte[] field_value;
|
||||
switch(key) {
|
||||
case MediaMetadata.METADATA_KEY_DURATION:
|
||||
field_id = KEY_DURATION;
|
||||
field_value = (""+metadata.getLong(key)).getBytes();
|
||||
break;
|
||||
case MediaMetadata.METADATA_KEY_ALBUM:
|
||||
field_id = KEY_ALBUM;
|
||||
field_value = metadata.getString(key).getBytes();
|
||||
break;
|
||||
case MediaMetadata.METADATA_KEY_ARTIST:
|
||||
field_id = KEY_ARTIST;
|
||||
field_value = metadata.getString(key).getBytes();
|
||||
break;
|
||||
case MediaMetadata.METADATA_KEY_TITLE:
|
||||
field_id = KEY_TITLE;
|
||||
field_value = metadata.getString(key).getBytes();
|
||||
break;
|
||||
default:
|
||||
field_id = 0;
|
||||
field_value = null;
|
||||
}
|
||||
|
||||
if (field_value != null) {
|
||||
payload.add(field_id);
|
||||
for (byte b : field_value) {
|
||||
payload.add(b);
|
||||
}
|
||||
payload.add((byte)0);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
byte[] result = new byte[payload.size()];
|
||||
for (int i = 0; i < payload.size(); i++) {
|
||||
result[i] = payload.get(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public int playbackStateSerialize(PlaybackState state) {
|
||||
switch(state.getState()) {
|
||||
case PlaybackState.STATE_PLAYING:
|
||||
return STATE_PLAYING;
|
||||
case PlaybackState.STATE_STOPPED:
|
||||
return STATE_STOPPED;
|
||||
case PlaybackState.STATE_PAUSED:
|
||||
return STATE_PAUSED;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private void removeMediaController(MediaController controller) {
|
||||
int controllerId = findId(controller);
|
||||
Ln.i("Remove MediaController ID:" + controllerId + " pkg:" + controller.getPackageName());
|
||||
mediaChangeListener.onRemove(controllerId);
|
||||
}
|
||||
|
||||
private int findId(MediaController controller) {
|
||||
String packageName = controller.getPackageName();
|
||||
Integer id = this.idMap.get(packageName);
|
||||
if (id == null) {
|
||||
id = nextId;
|
||||
nextId++;
|
||||
this.idMap.put(packageName, id);
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
private void addMediaController(MediaController controller) {
|
||||
final int controllerId = findId(controller);
|
||||
Ln.i("New MediaController ID:" + controllerId + " pkg:" + controller.getPackageName());
|
||||
controller.registerCallback(new MediaController.Callback() {
|
||||
@Override
|
||||
public void onMetadataChanged(MediaMetadata metadata) {
|
||||
super.onMetadataChanged(metadata);
|
||||
Ln.i("MediaController metadata change " + controllerId);
|
||||
mediaChangeListener.onMetadataChange(controllerId, metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackStateChanged(PlaybackState state) {
|
||||
super.onPlaybackStateChanged(state);
|
||||
Ln.i("MediaController playstate change " + controllerId);
|
||||
mediaChangeListener.onPlaybackStateChange(controllerId, state);
|
||||
}
|
||||
});
|
||||
mediaChangeListener.onMetadataChange(controllerId, controller.getMetadata());
|
||||
mediaChangeListener.onPlaybackStateChange(controllerId, controller.getPlaybackState());
|
||||
}
|
||||
}
|
||||
|
|
@ -32,6 +32,7 @@ public final class ServiceManager {
|
|||
private static ClipboardManager clipboardManager;
|
||||
private static ActivityManager activityManager;
|
||||
private static CameraManager cameraManager;
|
||||
private static MediaManager mediaManager;
|
||||
|
||||
private ServiceManager() {
|
||||
/* not instantiable */
|
||||
|
|
@ -109,4 +110,11 @@ public final class ServiceManager {
|
|||
}
|
||||
return cameraManager;
|
||||
}
|
||||
|
||||
public static MediaManager getMediaManager() {
|
||||
if (mediaManager == null) {
|
||||
mediaManager = MediaManager.create();
|
||||
}
|
||||
return mediaManager;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue