mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
Zepp OS: Add voice memo download logic
This commit is contained in:
+4
@@ -74,6 +74,10 @@ public abstract class AbstractZeppOsService {
|
||||
// we will already know the capabilities
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
// Do nothing by default
|
||||
}
|
||||
|
||||
protected ZeppOsSupport getSupport() {
|
||||
return mSupport;
|
||||
}
|
||||
|
||||
+19
@@ -71,6 +71,7 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Executors;
|
||||
@@ -148,6 +149,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.service
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsFtpServerService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsMorningUpdatesService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsPhoneService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsVoiceMemosService;
|
||||
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.AlarmUtils;
|
||||
@@ -190,6 +192,7 @@ public class ZeppOsSupport extends HuamiSupport implements ZeppOsFileTransferSer
|
||||
private final ZeppOsHttpService httpService = new ZeppOsHttpService(this);
|
||||
private final ZeppOsRemindersService remindersService = new ZeppOsRemindersService(this);
|
||||
private final ZeppOsLoyaltyCardService loyaltyCardService = new ZeppOsLoyaltyCardService(this);
|
||||
private final ZeppOsVoiceMemosService voiceMemosService = new ZeppOsVoiceMemosService(this);
|
||||
private final ZeppOsMusicService musicService = new ZeppOsMusicService(this);
|
||||
|
||||
private final Set<Short> mSupportedServices = new HashSet<>();
|
||||
@@ -219,6 +222,7 @@ public class ZeppOsSupport extends HuamiSupport implements ZeppOsFileTransferSer
|
||||
put(remindersService.getEndpoint(), remindersService);
|
||||
put(loyaltyCardService.getEndpoint(), loyaltyCardService);
|
||||
put(musicService.getEndpoint(), musicService);
|
||||
put(voiceMemosService.getEndpoint(), voiceMemosService);
|
||||
}};
|
||||
|
||||
public ZeppOsSupport() {
|
||||
@@ -249,6 +253,17 @@ public class ZeppOsSupport extends HuamiSupport implements ZeppOsFileTransferSer
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
for (final Short endpoint : mSupportedServices) {
|
||||
if (mServiceMap.containsKey(endpoint)) {
|
||||
Objects.requireNonNull(mServiceMap.get(endpoint)).dispose();
|
||||
}
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendConfiguration(final String config) {
|
||||
final Prefs prefs = getDevicePrefs();
|
||||
@@ -1708,6 +1723,10 @@ public class ZeppOsSupport extends HuamiSupport implements ZeppOsFileTransferSer
|
||||
return;
|
||||
}
|
||||
|
||||
if (url.startsWith("voicememo://") && filename.endsWith(".opus")) {
|
||||
voiceMemosService.downloadFinish();
|
||||
}
|
||||
|
||||
final String fileDownloadsDir = "zepp-os-received-files";
|
||||
final File targetFile;
|
||||
try {
|
||||
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
/* Copyright (C) 2025 José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services;
|
||||
|
||||
import android.os.Handler;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Objects;
|
||||
import java.util.Queue;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.AbstractZeppOsService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.ZeppOsSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
|
||||
|
||||
public class ZeppOsVoiceMemosService extends AbstractZeppOsService {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ZeppOsVoiceMemosService.class);
|
||||
|
||||
private static final short ENDPOINT = 0x0033;
|
||||
|
||||
private static final byte CMD_LIST_REQUEST = 0x05;
|
||||
private static final byte CMD_LIST_RESPONSE = 0x06;
|
||||
private static final byte CMD_DOWNLOAD_START_REQUEST = 0x07;
|
||||
private static final byte CMD_DOWNLOAD_START_ACK = 0x08;
|
||||
private static final byte CMD_DOWNLOAD_FINISH_REQUEST = 0x0a;
|
||||
private static final byte CMD_DOWNLOAD_FINISH_ACK = 0x09;
|
||||
|
||||
private final Queue<String> downloadQueue = new LinkedList<>();
|
||||
private boolean downloading = false;
|
||||
private final Handler handler = new Handler();
|
||||
|
||||
public ZeppOsVoiceMemosService(final ZeppOsSupport support) {
|
||||
super(support, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getEndpoint() {
|
||||
return ENDPOINT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
handler.removeCallbacksAndMessages(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePayload(final byte[] payload) {
|
||||
final ByteBuffer buf = ByteBuffer.wrap(payload).order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
switch (buf.get()) {
|
||||
case CMD_LIST_RESPONSE:
|
||||
final short count = buf.getShort();
|
||||
LOG.info("Got list with {} voice memos", count);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
final String filename = StringUtils.untilNullTerminator(buf);
|
||||
final int size = buf.getInt();
|
||||
final int duration = buf.getInt();
|
||||
final long timestamp = buf.getLong();
|
||||
|
||||
LOG.debug(
|
||||
"Voice memo: filename={}, size={}b, duration={}ms, timestamp={}",
|
||||
filename, size, duration, DateTimeUtils.formatIso8601(new Date(timestamp))
|
||||
);
|
||||
|
||||
downloadStart(Objects.requireNonNull(filename));
|
||||
}
|
||||
|
||||
return;
|
||||
case CMD_DOWNLOAD_START_ACK:
|
||||
LOG.info("Download start ACK, status = {}", payload[1]);
|
||||
return;
|
||||
case CMD_DOWNLOAD_FINISH_ACK:
|
||||
LOG.info("Download finish ACK, status = {}", payload[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.warn("Unexpected voice memos byte {}", String.format("0x%02x", payload[0]));
|
||||
}
|
||||
|
||||
public void requestList() {
|
||||
write("get voice memos list", CMD_LIST_REQUEST);
|
||||
}
|
||||
|
||||
public void downloadStart(final String filename) {
|
||||
LOG.debug("Queuing voice memo download for {}", filename);
|
||||
|
||||
downloadQueue.add(filename);
|
||||
if (!downloading) {
|
||||
downloading = true;
|
||||
downloadNext();
|
||||
}
|
||||
}
|
||||
|
||||
public void downloadFinish() {
|
||||
downloadNext();
|
||||
}
|
||||
|
||||
private void downloadNext() {
|
||||
handler.removeCallbacksAndMessages(null);
|
||||
|
||||
final String filename = downloadQueue.poll();
|
||||
|
||||
if (filename != null) {
|
||||
// Timeout after a while so we do not get stuck
|
||||
handler.postDelayed(() -> {
|
||||
LOG.warn("Timed out waiting for voice memo download, triggering next");
|
||||
downloadNext();
|
||||
}, 5000L);
|
||||
|
||||
LOG.debug("Will download voice memo {}", filename);
|
||||
write(
|
||||
"voice memo download " + filename,
|
||||
ArrayUtils.addAll(new byte[]{CMD_DOWNLOAD_START_REQUEST}, filename.getBytes())
|
||||
);
|
||||
} else {
|
||||
LOG.debug("Voice memo downloads finished");
|
||||
downloading = false;
|
||||
write("voice memo download finish", CMD_DOWNLOAD_FINISH_REQUEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user