mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-29 13:26:50 +01:00
Zepp OS: Add screenshot support
This commit is contained in:
parent
531aef61fd
commit
b3db9b7768
@ -83,6 +83,11 @@ public abstract class Huami2021Coordinator extends HuamiCoordinator {
|
|||||||
return handler.isValid() ? handler : null;
|
return handler.isValid() ? handler : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsScreenshots() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsHeartRateMeasurement(final GBDevice device) {
|
public boolean supportsHeartRateMeasurement(final GBDevice device) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -61,6 +61,11 @@ public class MiBand7Coordinator extends Huami2021Coordinator {
|
|||||||
return new MiBand7FWInstallHandler(uri, context);
|
return new MiBand7FWInstallHandler(uri, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsScreenshots() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsBluetoothPhoneCalls(final GBDevice device) {
|
public boolean supportsBluetoothPhoneCalls(final GBDevice device) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -48,6 +48,8 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
@ -74,6 +76,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
|
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventScreenshot;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.Huami2021Coordinator;
|
import nodomain.freeyourgadget.gadgetbridge.devices.huami.Huami2021Coordinator;
|
||||||
@ -125,6 +128,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.service
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsWatchfaceService;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsWatchfaceService;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsWifiService;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsWifiService;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
|
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.MapUtils;
|
import nodomain.freeyourgadget.gadgetbridge.util.MapUtils;
|
||||||
@ -305,6 +309,11 @@ public abstract class Huami2021Support extends HuamiSupport implements ZeppOsFil
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScreenshotReq() {
|
||||||
|
appsService.requestScreenshot();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSetHeartRateMeasurementInterval(final int seconds) {
|
public void onSetHeartRateMeasurementInterval(final int seconds) {
|
||||||
try {
|
try {
|
||||||
@ -1883,6 +1892,32 @@ public abstract class Huami2021Support extends HuamiSupport implements ZeppOsFil
|
|||||||
@Override
|
@Override
|
||||||
public void onFileDownloadFinish(final String url, final String filename, final byte[] data) {
|
public void onFileDownloadFinish(final String url, final String filename, final byte[] data) {
|
||||||
LOG.info("File received: url={} filename={} length={}", url, filename, data.length);
|
LOG.info("File received: url={} filename={} length={}", url, filename, data.length);
|
||||||
|
|
||||||
|
if (filename.startsWith("screenshot-")) {
|
||||||
|
GBDeviceEventScreenshot gbDeviceEventScreenshot = new GBDeviceEventScreenshot(data);
|
||||||
|
evaluateGBDeviceEvent(gbDeviceEventScreenshot);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String fileDownloadsDir = "zepp-os-received-files";
|
||||||
|
final File targetFile;
|
||||||
|
try {
|
||||||
|
final String validFilename = FileUtils.makeValidFileName(filename);
|
||||||
|
final File targetFolder = new File(FileUtils.getExternalFilesDir(), fileDownloadsDir);
|
||||||
|
targetFolder.mkdirs();
|
||||||
|
targetFile = new File(targetFolder, validFilename);
|
||||||
|
} catch (final IOException e) {
|
||||||
|
LOG.error("Failed create folder to save file", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (FileOutputStream outputStream = new FileOutputStream(targetFile)) {
|
||||||
|
final File targetFolder = new File(FileUtils.getExternalFilesDir(), fileDownloadsDir);
|
||||||
|
targetFolder.mkdirs();
|
||||||
|
outputStream.write(data);
|
||||||
|
} catch (final IOException e) {
|
||||||
|
LOG.error("Failed to save file bytes", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte bool(final boolean b) {
|
private byte bool(final boolean b) {
|
||||||
|
@ -29,23 +29,27 @@ import java.util.UUID;
|
|||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Support;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Support;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.AbstractZeppOsService;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.AbstractZeppOsService;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
|
||||||
|
|
||||||
public class ZeppOsAppsService extends AbstractZeppOsService {
|
public class ZeppOsAppsService extends AbstractZeppOsService {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(ZeppOsAppsService.class);
|
private static final Logger LOG = LoggerFactory.getLogger(ZeppOsAppsService.class);
|
||||||
|
|
||||||
private static final short ENDPOINT = 0x00a0;
|
private static final short ENDPOINT = 0x00a0;
|
||||||
|
|
||||||
private static final byte CMD_BYTE = 0x02;
|
private static final byte CMD_JS = 0x01;
|
||||||
|
private static final byte CMD_APPS = 0x02;
|
||||||
|
private static final byte CMD_SCREENSHOT = 0x03;
|
||||||
|
|
||||||
private static final byte CMD_INCOMING = 0x00;
|
private static final byte CMD_INCOMING = 0x00;
|
||||||
private static final byte CMD_OUTGOING = 0x01;
|
private static final byte CMD_OUTGOING = 0x01;
|
||||||
|
|
||||||
private static final byte CMD_APP_LIST = 0x01;
|
private static final byte CMD_APPS_LIST = 0x01;
|
||||||
private static final byte CMD_APP_DELETE = 0x03;
|
private static final byte CMD_APPS_DELETE = 0x03;
|
||||||
private static final byte CMD_APP_DELETING = 0x04;
|
private static final byte CMD_APPS_DELETING = 0x04;
|
||||||
|
private static final byte CMD_APPS_API_LEVEL = 0x05;
|
||||||
|
private static final byte CMD_SCREENSHOT_REQUEST = 0x01;
|
||||||
|
|
||||||
public ZeppOsAppsService(final Huami2021Support support) {
|
public ZeppOsAppsService(final Huami2021Support support) {
|
||||||
super(support);
|
super(support);
|
||||||
@ -63,28 +67,62 @@ public class ZeppOsAppsService extends AbstractZeppOsService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handlePayload(final byte[] payload) {
|
public void handlePayload(final byte[] payload) {
|
||||||
if (payload[0] != CMD_BYTE) {
|
switch (payload[0]) {
|
||||||
LOG.warn("Unexpected apps byte {}", String.format("0x%02x", payload[0]));
|
case CMD_JS:
|
||||||
return;
|
handleJsPayload(payload);
|
||||||
|
return;
|
||||||
|
case CMD_APPS:
|
||||||
|
handleAppsPayload(payload);
|
||||||
|
return;
|
||||||
|
case CMD_SCREENSHOT:
|
||||||
|
handleScreenshotPayload(payload);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
LOG.warn("Unexpected apps byte {}", String.format("0x%02x", payload[0]));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleJsPayload(final byte[] payload) {
|
||||||
|
LOG.warn("Handling js payloads not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleAppsPayload(final byte[] payload) {
|
||||||
if (payload[1] != CMD_INCOMING) {
|
if (payload[1] != CMD_INCOMING) {
|
||||||
LOG.warn("Unexpected apps 2nd byte {}", String.format("0x%02x", payload[1]));
|
LOG.warn("Unexpected non-incoming payload ({})", String.format("0x%02x", payload[1]));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (payload[2]) {
|
switch (payload[2]) {
|
||||||
case CMD_APP_LIST:
|
case CMD_APPS_LIST:
|
||||||
parseAppList(payload);
|
parseAppList(payload);
|
||||||
return;
|
return;
|
||||||
case CMD_APP_DELETE:
|
case CMD_APPS_DELETE:
|
||||||
LOG.info("Got app delete");
|
LOG.info("Got app delete");
|
||||||
return;
|
return;
|
||||||
case CMD_APP_DELETING:
|
case CMD_APPS_DELETING:
|
||||||
LOG.info("Got app deleting");
|
LOG.info("Got app deleting");
|
||||||
return;
|
return;
|
||||||
|
case CMD_APPS_API_LEVEL:
|
||||||
|
final int apiLevel = payload[17] & 0xff;
|
||||||
|
LOG.info("Got API level: {}", apiLevel); // 200 = 2.0
|
||||||
|
return;
|
||||||
default:
|
default:
|
||||||
LOG.warn("Unexpected apps payload {}", GB.hexdump(payload));
|
LOG.warn("Unexpected apps payload byte {}", payload[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleScreenshotPayload(final byte[] payload) {
|
||||||
|
if (payload[1] != CMD_INCOMING) {
|
||||||
|
LOG.warn("Unexpected non-incoming payload ({})", String.format("0x%02x", payload[1]));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (payload[2]) {
|
||||||
|
case CMD_SCREENSHOT_REQUEST:
|
||||||
|
LOG.info("Got screenshot request ack, status={}", payload[16]); // 0 for success
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
LOG.warn("Unexpected screenshot payload byte {}", payload[2]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,22 +168,34 @@ public class ZeppOsAppsService extends AbstractZeppOsService {
|
|||||||
|
|
||||||
final ByteBuffer buf = ByteBuffer.allocate(16).order(ByteOrder.LITTLE_ENDIAN);
|
final ByteBuffer buf = ByteBuffer.allocate(16).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
|
||||||
buf.put(CMD_BYTE);
|
buf.put(CMD_APPS);
|
||||||
buf.put(CMD_OUTGOING);
|
buf.put(CMD_OUTGOING);
|
||||||
buf.put(CMD_APP_LIST);
|
buf.put(CMD_APPS_LIST);
|
||||||
buf.put((byte) 0x00);
|
buf.put((byte) 0x00);
|
||||||
|
|
||||||
write("request apps", buf.array());
|
write("request apps", buf.array());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void requestApilevel() {
|
||||||
|
LOG.info("Request api level");
|
||||||
|
|
||||||
|
final ByteBuffer buf = ByteBuffer.allocate(16).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
|
||||||
|
buf.put(CMD_APPS);
|
||||||
|
buf.put(CMD_OUTGOING);
|
||||||
|
buf.put(CMD_APPS_API_LEVEL);
|
||||||
|
|
||||||
|
write("request api level", buf.array());
|
||||||
|
}
|
||||||
|
|
||||||
public void deleteApp(final int appId) {
|
public void deleteApp(final int appId) {
|
||||||
LOG.info("Delete app {}", String.format("0x%08x", appId));
|
LOG.info("Delete app {}", String.format("0x%08x", appId));
|
||||||
|
|
||||||
final ByteBuffer buf = ByteBuffer.allocate(20).order(ByteOrder.LITTLE_ENDIAN);
|
final ByteBuffer buf = ByteBuffer.allocate(20).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
|
||||||
buf.put(CMD_BYTE);
|
buf.put(CMD_APPS);
|
||||||
buf.put(CMD_OUTGOING);
|
buf.put(CMD_OUTGOING);
|
||||||
buf.put(CMD_APP_DELETE);
|
buf.put(CMD_APPS_DELETE);
|
||||||
buf.put((byte) 0x00);
|
buf.put((byte) 0x00);
|
||||||
buf.putInt(0x00);
|
buf.putInt(0x00);
|
||||||
buf.putInt(0x00);
|
buf.putInt(0x00);
|
||||||
@ -154,4 +204,17 @@ public class ZeppOsAppsService extends AbstractZeppOsService {
|
|||||||
|
|
||||||
write("delete app", buf.array());
|
write("delete app", buf.array());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void requestScreenshot() {
|
||||||
|
LOG.info("Requesting screenshot");
|
||||||
|
|
||||||
|
final ByteBuffer buf = ByteBuffer.allocate(20).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
|
||||||
|
buf.put(CMD_SCREENSHOT);
|
||||||
|
buf.put(CMD_OUTGOING);
|
||||||
|
buf.put(CMD_SCREENSHOT_REQUEST);
|
||||||
|
buf.put((byte) 0x00);
|
||||||
|
|
||||||
|
write("request screenshot", buf.array());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user