1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-12-27 19:15:50 +01:00

Zepp OS: Add screenshot support

This commit is contained in:
José Rebelo 2023-06-11 16:55:01 +01:00
parent 531aef61fd
commit b3db9b7768
4 changed files with 125 additions and 17 deletions

View File

@ -83,6 +83,11 @@ public abstract class Huami2021Coordinator extends HuamiCoordinator {
return handler.isValid() ? handler : null;
}
@Override
public boolean supportsScreenshots() {
return true;
}
@Override
public boolean supportsHeartRateMeasurement(final GBDevice device) {
return true;

View File

@ -61,6 +61,11 @@ public class MiBand7Coordinator extends Huami2021Coordinator {
return new MiBand7FWInstallHandler(uri, context);
}
@Override
public boolean supportsScreenshots() {
return false;
}
@Override
public boolean supportsBluetoothPhoneCalls(final GBDevice device) {
return false;

View File

@ -48,6 +48,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
@ -74,6 +76,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventScreenshot;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
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.ZeppOsWifiService;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
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
public void onSetHeartRateMeasurementInterval(final int seconds) {
try {
@ -1883,6 +1892,32 @@ public abstract class Huami2021Support extends HuamiSupport implements ZeppOsFil
@Override
public void onFileDownloadFinish(final String url, final String filename, final byte[] data) {
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) {

View File

@ -29,23 +29,27 @@ import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo;
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.zeppos.AbstractZeppOsService;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class ZeppOsAppsService extends AbstractZeppOsService {
private static final Logger LOG = LoggerFactory.getLogger(ZeppOsAppsService.class);
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_OUTGOING = 0x01;
private static final byte CMD_APP_LIST = 0x01;
private static final byte CMD_APP_DELETE = 0x03;
private static final byte CMD_APP_DELETING = 0x04;
private static final byte CMD_APPS_LIST = 0x01;
private static final byte CMD_APPS_DELETE = 0x03;
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) {
super(support);
@ -63,28 +67,62 @@ public class ZeppOsAppsService extends AbstractZeppOsService {
@Override
public void handlePayload(final byte[] payload) {
if (payload[0] != CMD_BYTE) {
LOG.warn("Unexpected apps byte {}", String.format("0x%02x", payload[0]));
return;
switch (payload[0]) {
case CMD_JS:
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) {
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;
}
switch (payload[2]) {
case CMD_APP_LIST:
case CMD_APPS_LIST:
parseAppList(payload);
return;
case CMD_APP_DELETE:
case CMD_APPS_DELETE:
LOG.info("Got app delete");
return;
case CMD_APP_DELETING:
case CMD_APPS_DELETING:
LOG.info("Got app deleting");
return;
case CMD_APPS_API_LEVEL:
final int apiLevel = payload[17] & 0xff;
LOG.info("Got API level: {}", apiLevel); // 200 = 2.0
return;
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);
buf.put(CMD_BYTE);
buf.put(CMD_APPS);
buf.put(CMD_OUTGOING);
buf.put(CMD_APP_LIST);
buf.put(CMD_APPS_LIST);
buf.put((byte) 0x00);
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) {
LOG.info("Delete app {}", String.format("0x%08x", appId));
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_APP_DELETE);
buf.put(CMD_APPS_DELETE);
buf.put((byte) 0x00);
buf.putInt(0x00);
buf.putInt(0x00);
@ -154,4 +204,17 @@ public class ZeppOsAppsService extends AbstractZeppOsService {
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());
}
}