mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
igpsport: Install route files as apps
* works with cnx, gpx, fit, tcx and xml roue files * only extension checked, no real checks for valid file
This commit is contained in:
committed by
José Rebelo
parent
1d5a59974f
commit
601eca82ef
+34
-2
@@ -1,16 +1,20 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.igpsport;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import de.greenrobot.dao.query.QueryBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.appmanager.AppManagerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLEDeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.MakibesHR3ActivitySampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.igpsport.IGPSportDeviceSupport;
|
||||
@@ -63,4 +67,32 @@ public class IGPSportCoordinator extends AbstractBLEDeviceCoordinator {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAppsManagement(final GBDevice device) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Activity> getAppsManagementActivity() {
|
||||
return AppManagerActivity.class;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public InstallHandler findInstallHandler(final Uri uri, final Context context) {
|
||||
|
||||
// final IGPSportAgpsInstallHandler agpsInstallHandler = new IGPSportAgpsInstallHandler(uri, context);
|
||||
// if (agpsInstallHandler.isValid()) {
|
||||
// return agpsInstallHandler;
|
||||
// }
|
||||
|
||||
final IGPSportRouteInstallHandler routeInstallHandler = new IGPSportRouteInstallHandler(uri, context);
|
||||
if (routeInstallHandler.isValid()) {
|
||||
return routeInstallHandler;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.igpsport;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.GenericItem;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.UriHelper;
|
||||
|
||||
|
||||
public class IGPSportRouteInstallHandler implements InstallHandler {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(IGPSportRouteInstallHandler.class);
|
||||
|
||||
protected final Context mContext;
|
||||
private byte[] rawBytes = null;
|
||||
private String filename;
|
||||
private String extension;
|
||||
private List<String> routeExtensions = Arrays.asList("cnx", "gpx", "fit", "tcx", "xml");
|
||||
|
||||
public IGPSportRouteInstallHandler(final Uri uri, final Context context) {
|
||||
this.mContext = context;
|
||||
|
||||
final UriHelper uriHelper;
|
||||
try {
|
||||
uriHelper = UriHelper.get(uri, context);
|
||||
filename = uriHelper.getFileName();
|
||||
int strLength = filename.lastIndexOf(".");
|
||||
if(strLength > 0)
|
||||
extension = filename.substring(strLength + 1).toLowerCase();
|
||||
} catch (final IOException e) {
|
||||
LOG.error("Failed to get uri", e);
|
||||
return;
|
||||
}
|
||||
|
||||
try (InputStream in = new BufferedInputStream(uriHelper.openInputStream())) {
|
||||
rawBytes = FileUtils.readAll(in, 1024 * 1024); // 1MB
|
||||
|
||||
try (ByteArrayInputStream bais = new ByteArrayInputStream(rawBytes)) {
|
||||
|
||||
} catch (final IOException e) {
|
||||
LOG.error("Failed to read xml", e);
|
||||
}
|
||||
|
||||
} catch (final Exception e) {
|
||||
LOG.error("Failed to read file", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
if (routeExtensions.contains(extension))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateInstallation(InstallActivity installActivity, GBDevice device) {
|
||||
if (device.isBusy()) {
|
||||
installActivity.setInfoText(device.getBusyTask());
|
||||
installActivity.setInstallEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
final DeviceCoordinator coordinator = device.getDeviceCoordinator();
|
||||
if (!(coordinator instanceof IGPSportCoordinator)) {
|
||||
LOG.warn("Coordinator is not a IGPSportCoordinator: {}", coordinator.getClass());
|
||||
installActivity.setInfoText(mContext.getString(R.string.fwapp_install_device_not_supported));
|
||||
installActivity.setInstallEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!device.isInitialized()) {
|
||||
installActivity.setInfoText(mContext.getString(R.string.fwapp_install_device_not_ready));
|
||||
installActivity.setInstallEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
final GenericItem fwItem = createInstallItem(device);
|
||||
fwItem.setIcon(coordinator.getDefaultIconResource());
|
||||
|
||||
if (rawBytes == null) {
|
||||
fwItem.setDetails(mContext.getString(R.string.miband_fwinstaller_incompatible_version));
|
||||
installActivity.setInfoText(mContext.getString(R.string.fwinstaller_firmware_not_compatible_to_device));
|
||||
installActivity.setInstallEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
final String cnxRoute = mContext.getString(R.string.kind_gpx_route);
|
||||
builder.append(mContext.getString(R.string.fw_upgrade_notice, filename));
|
||||
installActivity.setInfoText(builder.toString());
|
||||
installActivity.setInstallItem(fwItem);
|
||||
installActivity.setInstallEnabled(true);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartInstall(GBDevice device) {
|
||||
|
||||
}
|
||||
|
||||
private GenericItem createInstallItem(final GBDevice device) {
|
||||
DeviceCoordinator coordinator = device.getDeviceCoordinator();
|
||||
final String firmwareName = mContext.getString(
|
||||
R.string.installhandler_firmware_name,
|
||||
mContext.getString(coordinator.getDeviceNameResource()),
|
||||
mContext.getString(R.string.kind_gpx_route),
|
||||
"test" //FIXME
|
||||
);
|
||||
return new GenericItem(firmwareName);
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return rawBytes;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return rawBytes.length;
|
||||
}
|
||||
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
public String getExtension() {
|
||||
return extension;
|
||||
}
|
||||
}
|
||||
+96
-23
@@ -4,6 +4,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.igpsport;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
@@ -14,17 +15,18 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.SettingsActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.igpsport.IGPSportRouteInstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.igpsport.IGPSportConstants;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
|
||||
@@ -35,7 +37,9 @@ import nodomain.freeyourgadget.gadgetbridge.proto.igpsport.Ble;
|
||||
import nodomain.freeyourgadget.gadgetbridge.proto.igpsport.Config;
|
||||
import nodomain.freeyourgadget.gadgetbridge.proto.igpsport.CyclingData;
|
||||
import nodomain.freeyourgadget.gadgetbridge.proto.igpsport.Firmware;
|
||||
import nodomain.freeyourgadget.gadgetbridge.proto.igpsport.GeneralFileOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.proto.igpsport.Ins;
|
||||
import nodomain.freeyourgadget.gadgetbridge.proto.igpsport.RoutePlan;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
@@ -56,6 +60,7 @@ public class IGPSportDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
public BluetoothGattCharacteristic readCharacteristic;
|
||||
public BluetoothGattCharacteristic writeCharacteristic;
|
||||
public BluetoothGattCharacteristic writeCharacteristicThird;
|
||||
public BluetoothGattCharacteristic writeCharacteristicFourth;
|
||||
public final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo();
|
||||
public final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo();
|
||||
public final DeviceInfoProfile<IGPSportDeviceSupport> deviceInfoProfile;
|
||||
@@ -128,6 +133,7 @@ public class IGPSportDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
readCharacteristic = getCharacteristic(IGPSportConstants.UUID_IGPSPORT_CHARACTERISTIC_FIRST_RX);
|
||||
writeCharacteristic = getCharacteristic(IGPSportConstants.UUID_IGPSPORT_CHARACTERISTIC_FIRST_TX);
|
||||
writeCharacteristicThird = getCharacteristic(IGPSportConstants.UUID_IGPSPORT_CHARACTERISTIC_THIRD_TX);
|
||||
writeCharacteristicFourth = getCharacteristic(IGPSportConstants.UUID_IGPSPORT_CHARACTERISTIC_FOURTH_TX);
|
||||
|
||||
builder.notify(getCharacteristic(IGPSportConstants.UUID_IGPSPORT_CHARACTERISTIC_FIRST_RX), true);
|
||||
builder.notify(getCharacteristic(IGPSportConstants.UUID_IGPSPORT_CHARACTERISTIC_SECOND_RX), true);
|
||||
@@ -187,31 +193,44 @@ public class IGPSportDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
LOG.info("Characteristic changed value: " + GB.hexdump(characteristic.getValue()));
|
||||
|
||||
byte[] data = characteristic.getValue();
|
||||
if (data[0] != IGPSportConstants.DATA_HEADER) {
|
||||
LOG.info("FitPro, packet not starting with 0x01: " + data[0] + "other message types not implemented yet");
|
||||
return false;
|
||||
if (data[0] == IGPSportConstants.DATA_HEADER) {
|
||||
|
||||
if (data != null && data.length > 20) {
|
||||
byte mainService = data[1];
|
||||
byte mainOperation = data[4];
|
||||
int dataSize = ByteBuffer.wrap(data, 7, 2).getShort();
|
||||
|
||||
byte[] pbData = new byte[dataSize];
|
||||
System.arraycopy(data, 20, pbData, 0, dataSize);
|
||||
|
||||
try {
|
||||
switch (mainService) {
|
||||
case Common.service_type_index.enum_SERVICE_TYPE_INDEX_FACTORY_VALUE:
|
||||
handleFactoryData(pbData);
|
||||
break;
|
||||
case Common.service_type_index.enum_SERVICE_TYPE_INDEX_FIRMWARE_VALUE:
|
||||
handleFirmwareData(pbData);
|
||||
break;
|
||||
}
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (data != null && data.length > 20) {
|
||||
|
||||
if (data[0] == 0x02) {
|
||||
//0215FFFF03FFFF00FFFFFFFFFFFFFFFFFFFFFF55
|
||||
byte mainService = data[1];
|
||||
byte mainOperation = data[4];
|
||||
int dataSize = ByteBuffer.wrap(data, 7,2).getShort();
|
||||
|
||||
byte[] pbData = new byte[dataSize];
|
||||
System.arraycopy(data, 20, pbData, 0, dataSize);
|
||||
|
||||
try {
|
||||
switch (mainService) {
|
||||
case Common.service_type_index.enum_SERVICE_TYPE_INDEX_FACTORY_VALUE:
|
||||
handleFactoryData(pbData);
|
||||
break;
|
||||
case Common.service_type_index.enum_SERVICE_TYPE_INDEX_FIRMWARE_VALUE:
|
||||
handleFirmwareData(pbData);
|
||||
break;
|
||||
}
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw new RuntimeException(e);
|
||||
byte result = data[7];
|
||||
switch (mainService) {
|
||||
case Common.service_type_index.enum_SERVICE_TYPE_INDEX_ROUTE_PLAN_VALUE:
|
||||
if(mainOperation == Common.SERVICE_OPERATE_TYPE.enum_SERVICE_OPERATE_TYPE_ADD_VALUE) {
|
||||
gbDevice.unsetBusyTask();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -432,4 +451,58 @@ public class IGPSportDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInstallApp(Uri uri) {
|
||||
//final IGPSportGpxRouteInstallHandler gpxRouteHandler = new IGPSportGpxRouteInstallHandler(uri, getContext());
|
||||
final IGPSportRouteInstallHandler routeHandler = new IGPSportRouteInstallHandler(uri, getContext());
|
||||
if (routeHandler.isValid()) {
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("prepare upload gpx");
|
||||
|
||||
// RoutePlan.route_plan_data_msg.Builder routePlanBuilder = RoutePlan.route_plan_data_msg.newBuilder();
|
||||
// routePlanBuilder.setServiceType(Common.service_type_index.enum_SERVICE_TYPE_INDEX_ROUTE_PLAN);
|
||||
// routePlanBuilder.setRoutePlanOperateType(RoutePlan.ROUTE_PLAN_OPERATE_TYPE.enum_ROUTE_PLAN_OPERATE_TYPE_LIST_NUM_GET);
|
||||
// byte[] routePlanBytes = craftData(routePlanBuilder.getServiceType().getNumber(), 0xff, routePlanBuilder.getRoutePlanOperateType().getNumber(), routePlanBuilder.build().toByteArray());
|
||||
// builder.write(writeCharacteristicFourth, routePlanBytes);
|
||||
// builder.wait(1000);
|
||||
//
|
||||
// RoutePlan.route_plan_data_msg.Builder routePlan2ndBuilder = RoutePlan.route_plan_data_msg.newBuilder();
|
||||
// routePlan2ndBuilder.setServiceType(Common.service_type_index.enum_SERVICE_TYPE_INDEX_ROUTE_PLAN);
|
||||
// routePlan2ndBuilder.setRoutePlanOperateType(RoutePlan.ROUTE_PLAN_OPERATE_TYPE.enum_ROUTE_PLAN_OPERATE_TYPE_LIST_GET);
|
||||
// routePlan2ndBuilder.setRouteListGetMsg(Common.file_list_get_message.newBuilder().setFileIndexEnd(0).setFileIndexEnd(0));
|
||||
// byte[] routePlan2ndBytes = craftData(routePlan2ndBuilder.getServiceType().getNumber(), 0xff, routePlan2ndBuilder.getRoutePlanOperateType().getNumber(), routePlan2ndBuilder.build().toByteArray());
|
||||
// builder.write(writeCharacteristicFourth, routePlan2ndBytes);
|
||||
// builder.wait(1000);
|
||||
//
|
||||
// builder.write(writeCharacteristicFourth, routePlanBytes);
|
||||
// builder.wait(1000);
|
||||
|
||||
Random random = new Random();
|
||||
int ran = random.nextInt();
|
||||
GeneralFileOperation.general_file_operation.Builder fileOperationbuilder = GeneralFileOperation.general_file_operation.newBuilder();
|
||||
fileOperationbuilder.setServiceType(Common.service_type_index.enum_SERVICE_TYPE_INDEX_FILE_OPERATION)
|
||||
.setOperateType(Common.SERVICE_OPERATE_TYPE.enum_SERVICE_OPERATE_TYPE_ADD)
|
||||
.setFileType(GeneralFileOperation.file_operation_type.enum_FILE_TYPE_ROUTE_PLAN)
|
||||
.setFileId(ran)
|
||||
.setFileExtension(routeHandler.getExtension())
|
||||
.setFileName(routeHandler.getFilename())
|
||||
.setFileSize(routeHandler.getSize());
|
||||
|
||||
byte[] fileOperationBytes = craftFileData(fileOperationbuilder.getServiceType().getNumber(),
|
||||
0xff,
|
||||
fileOperationbuilder.getOperateType().getNumber(),
|
||||
fileOperationbuilder.build().toByteArray(),
|
||||
routeHandler.getBytes());
|
||||
builder.writeChunkedData(writeCharacteristicFourth, fileOperationBytes, getMTU());
|
||||
gbDevice.setBusyTask("Installing route");
|
||||
builder.queue(getQueue());
|
||||
|
||||
} catch (final Exception e) {
|
||||
GB.toast(getContext(), "Gpx install error: " + e.getMessage(), Toast.LENGTH_LONG, GB.ERROR, e);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user