mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
igsport: rebase device code to master ad34fec706
This commit is contained in:
committed by
José Rebelo
parent
39cf720fdd
commit
014b180a30
+19
-3
@@ -1,3 +1,19 @@
|
|||||||
|
/* Copyright (C) 2025 Vitaliy Tomin, Thomas Kuehne
|
||||||
|
|
||||||
|
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.devices.igpsport;
|
package nodomain.freeyourgadget.gadgetbridge.devices.igpsport;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
@@ -49,7 +65,7 @@ public abstract class IGPSportAbstractCoordinator extends AbstractBLEDeviceCoord
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsActivityTracks() {
|
public boolean supportsActivityTracks(final GBDevice device) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,12 +75,12 @@ public abstract class IGPSportAbstractCoordinator extends AbstractBLEDeviceCoord
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsAppListFetching() {
|
public boolean supportsAppListFetching(final GBDevice device) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<? extends Activity> getAppsManagementActivity() {
|
public Class<? extends Activity> getAppsManagementActivity(final GBDevice device) {
|
||||||
return AppManagerActivity.class;
|
return AppManagerActivity.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+26
@@ -1,8 +1,27 @@
|
|||||||
|
/* Copyright (C) 2025 Vitaliy Tomin, Thomas Kuehne
|
||||||
|
|
||||||
|
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.devices.igpsport;
|
package nodomain.freeyourgadget.gadgetbridge.devices.igpsport;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -14,6 +33,7 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.FwAppInstallerActivity;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
|
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||||
@@ -62,6 +82,12 @@ public class IGPSportRouteInstallHandler implements InstallHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Class<? extends Activity> getInstallActivity() {
|
||||||
|
return FwAppInstallerActivity.class;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValid() {
|
public boolean isValid() {
|
||||||
if (routeExtensions.contains(extension))
|
if (routeExtensions.contains(extension))
|
||||||
|
|||||||
+18
-1
@@ -1,3 +1,19 @@
|
|||||||
|
/* Copyright (C) 2025 Vitaliy Tomin, Thomas Kuehne
|
||||||
|
|
||||||
|
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.devices.igpsport;
|
package nodomain.freeyourgadget.gadgetbridge.devices.igpsport;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -6,6 +22,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
|
||||||
public class IGPSportiGS630Coordinator extends IGPSportAbstractCoordinator {
|
public class IGPSportiGS630Coordinator extends IGPSportAbstractCoordinator {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(IGPSportiGS630Coordinator.class);
|
private static final Logger LOG = LoggerFactory.getLogger(IGPSportiGS630Coordinator.class);
|
||||||
@@ -21,7 +38,7 @@ public class IGPSportiGS630Coordinator extends IGPSportAbstractCoordinator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsWeather() {
|
public boolean supportsWeather(final GBDevice device) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -316,6 +316,8 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huawei.huaweiwatchultimate.H
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.huaweiwatchultimate2.HuaweiWatchUltimate2Coordinator;
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.huaweiwatchultimate2.HuaweiWatchUltimate2Coordinator;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.id115.ID115Coordinator;
|
import nodomain.freeyourgadget.gadgetbridge.devices.id115.ID115Coordinator;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.idasen.IdasenCoordinator;
|
import nodomain.freeyourgadget.gadgetbridge.devices.idasen.IdasenCoordinator;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.igpsport.IGPSportBSC200Coordinator;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.igpsport.IGPSportiGS630Coordinator;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.itag.ITagCoordinator;
|
import nodomain.freeyourgadget.gadgetbridge.devices.itag.ITagCoordinator;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.jyou.BFH16DeviceCoordinator;
|
import nodomain.freeyourgadget.gadgetbridge.devices.jyou.BFH16DeviceCoordinator;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.jyou.TeclastH30.TeclastH30Coordinator;
|
import nodomain.freeyourgadget.gadgetbridge.devices.jyou.TeclastH30.TeclastH30Coordinator;
|
||||||
@@ -941,6 +943,8 @@ public enum DeviceType {
|
|||||||
SANITAS_SBM_67(SanitasSBM67Coordinator.class),
|
SANITAS_SBM_67(SanitasSBM67Coordinator.class),
|
||||||
SILVERCREST_SBM_67(SilverCrestSBM67Coordinator.class),
|
SILVERCREST_SBM_67(SilverCrestSBM67Coordinator.class),
|
||||||
BRAUN_BPW4500(BraunBPW4500DeviceCoordinator.class),
|
BRAUN_BPW4500(BraunBPW4500DeviceCoordinator.class),
|
||||||
|
IGPSPORT_BSC200(IGPSportBSC200Coordinator.class),
|
||||||
|
IGPSPORT_IGS630(IGPSportiGS630Coordinator.class),
|
||||||
TEST(TestDeviceCoordinator.class);
|
TEST(TestDeviceCoordinator.class);
|
||||||
|
|
||||||
private DeviceCoordinator coordinator;
|
private DeviceCoordinator coordinator;
|
||||||
|
|||||||
+35
-28
@@ -1,3 +1,19 @@
|
|||||||
|
/* Copyright (C) 2025 Vitaliy Tomin, Thomas Kuehne
|
||||||
|
|
||||||
|
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.igpsport;
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.igpsport;
|
||||||
|
|
||||||
|
|
||||||
@@ -8,8 +24,11 @@ import android.bluetooth.BluetoothGatt;
|
|||||||
import android.bluetooth.BluetoothGattCharacteristic;
|
import android.bluetooth.BluetoothGattCharacteristic;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
|
|
||||||
@@ -47,11 +66,9 @@ import nodomain.freeyourgadget.gadgetbridge.proto.igpsport.Firmware;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.proto.igpsport.Ins;
|
import nodomain.freeyourgadget.gadgetbridge.proto.igpsport.Ins;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.proto.igpsport.PeripheralCommon;
|
import nodomain.freeyourgadget.gadgetbridge.proto.igpsport.PeripheralCommon;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.proto.igpsport.RoutePlan;
|
import nodomain.freeyourgadget.gadgetbridge.proto.igpsport.RoutePlan;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLESingleDeviceSupport;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetProgressAction;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.IntentListener;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.IntentListener;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.battery.BatteryInfo;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.battery.BatteryInfo;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.battery.BatteryInfoProfile;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.battery.BatteryInfoProfile;
|
||||||
@@ -63,7 +80,7 @@ import nodomain.freeyourgadget.gadgetbridge.proto.igpsport.Common;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.proto.igpsport.Factory;
|
import nodomain.freeyourgadget.gadgetbridge.proto.igpsport.Factory;
|
||||||
|
|
||||||
|
|
||||||
public class IGPSportDeviceSupport extends AbstractBTLEDeviceSupport {
|
public class IGPSportDeviceSupport extends AbstractBTLESingleDeviceSupport {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(IGPSportDeviceSupport.class);
|
private static final Logger LOG = LoggerFactory.getLogger(IGPSportDeviceSupport.class);
|
||||||
public BluetoothGattCharacteristic readCharacteristic;
|
public BluetoothGattCharacteristic readCharacteristic;
|
||||||
@@ -147,7 +164,7 @@ public class IGPSportDeviceSupport extends AbstractBTLEDeviceSupport {
|
|||||||
@Override
|
@Override
|
||||||
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
|
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
|
||||||
// mark the device as initializing
|
// mark the device as initializing
|
||||||
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
|
builder.setDeviceState(GBDevice.State.INITIALIZING);
|
||||||
readCharacteristic = getCharacteristic(IGPSportConstants.UUID_IGPSPORT_CHARACTERISTIC_FIRST_RX);
|
readCharacteristic = getCharacteristic(IGPSportConstants.UUID_IGPSPORT_CHARACTERISTIC_FIRST_RX);
|
||||||
writeCharacteristic = getCharacteristic(IGPSportConstants.UUID_IGPSPORT_CHARACTERISTIC_FIRST_TX);
|
writeCharacteristic = getCharacteristic(IGPSportConstants.UUID_IGPSPORT_CHARACTERISTIC_FIRST_TX);
|
||||||
writeCharacteristicThird = getCharacteristic(IGPSportConstants.UUID_IGPSPORT_CHARACTERISTIC_THIRD_TX);
|
writeCharacteristicThird = getCharacteristic(IGPSportConstants.UUID_IGPSPORT_CHARACTERISTIC_THIRD_TX);
|
||||||
@@ -201,7 +218,7 @@ public class IGPSportDeviceSupport extends AbstractBTLEDeviceSupport {
|
|||||||
getDevice().setFirmwareVersion2("N/A");
|
getDevice().setFirmwareVersion2("N/A");
|
||||||
|
|
||||||
// mark the device as initialized
|
// mark the device as initialized
|
||||||
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext()));
|
builder.setDeviceState(GBDevice.State.INITIALIZED);
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,25 +340,15 @@ public class IGPSportDeviceSupport extends AbstractBTLEDeviceSupport {
|
|||||||
if (mainOperation == Common.SERVICE_OPERATE_TYPE.enum_SERVICE_OPERATE_TYPE_ADD_VALUE) {
|
if (mainOperation == Common.SERVICE_OPERATE_TYPE.enum_SERVICE_OPERATE_TYPE_ADD_VALUE) {
|
||||||
gbDevice.unsetBusyTask();
|
gbDevice.unsetBusyTask();
|
||||||
gbDevice.sendDeviceUpdateIntent(getContext());
|
gbDevice.sendDeviceUpdateIntent(getContext());
|
||||||
TransactionBuilder builder = new TransactionBuilder("Route upload finished");
|
TransactionBuilder builder = createTransactionBuilder("Route upload finished");
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
builder.add(new SetProgressAction(
|
builder.setProgress(R.string.route_upload_completed, false, 100);
|
||||||
"Route upload completed",
|
|
||||||
false,
|
|
||||||
100,
|
|
||||||
getContext()
|
|
||||||
));
|
|
||||||
handleGBDeviceEvent(new GBDeviceEventDisplayMessage("Route upload completed", Toast.LENGTH_LONG, GB.INFO));
|
handleGBDeviceEvent(new GBDeviceEventDisplayMessage("Route upload completed", Toast.LENGTH_LONG, GB.INFO));
|
||||||
} else {
|
} else {
|
||||||
builder.add(new SetProgressAction(
|
builder.setProgress(R.string.route_upload_failed, false, 0);
|
||||||
"Route upload failed",
|
|
||||||
false,
|
|
||||||
0,
|
|
||||||
getContext()
|
|
||||||
));
|
|
||||||
handleGBDeviceEvent(new GBDeviceEventDisplayMessage("Failed to upload route", Toast.LENGTH_LONG, GB.ERROR));
|
handleGBDeviceEvent(new GBDeviceEventDisplayMessage("Failed to upload route", Toast.LENGTH_LONG, GB.ERROR));
|
||||||
}
|
}
|
||||||
builder.queue(getQueue());
|
builder.queue();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -476,7 +483,7 @@ public class IGPSportDeviceSupport extends AbstractBTLEDeviceSupport {
|
|||||||
@Override
|
@Override
|
||||||
public void onNotification(NotificationSpec notificationSpec) {
|
public void onNotification(NotificationSpec notificationSpec) {
|
||||||
LOG.debug("iGPSport notification: " + notificationSpec.type);
|
LOG.debug("iGPSport notification: " + notificationSpec.type);
|
||||||
TransactionBuilder builder = new TransactionBuilder("notification");
|
TransactionBuilder builder = createTransactionBuilder("notification");
|
||||||
|
|
||||||
Ins.ins_msg.Builder insMsgBuilder = Ins.ins_msg.newBuilder();
|
Ins.ins_msg.Builder insMsgBuilder = Ins.ins_msg.newBuilder();
|
||||||
insMsgBuilder.setServiceType(Common.service_type_index.enum_SERVICE_TYPE_INDEX_INS);
|
insMsgBuilder.setServiceType(Common.service_type_index.enum_SERVICE_TYPE_INDEX_INS);
|
||||||
@@ -501,13 +508,13 @@ public class IGPSportDeviceSupport extends AbstractBTLEDeviceSupport {
|
|||||||
insMsgBuilder.setInsDataMsg(insDataMsgBuilder);
|
insMsgBuilder.setInsDataMsg(insDataMsgBuilder);
|
||||||
byte[] callData = craftData(insMsgBuilder.getServiceType().getNumber(), insMsgBuilder.getInsServiceType().getNumber(), insMsgBuilder.getInsOperateType().getNumber(), insMsgBuilder.build().toByteArray());
|
byte[] callData = craftData(insMsgBuilder.getServiceType().getNumber(), insMsgBuilder.getInsServiceType().getNumber(), insMsgBuilder.getInsOperateType().getNumber(), insMsgBuilder.build().toByteArray());
|
||||||
builder.write(writeCharacteristic, callData);
|
builder.write(writeCharacteristic, callData);
|
||||||
builder.queue(getQueue());
|
builder.queue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSetCallState(CallSpec callSpec) {
|
public void onSetCallState(CallSpec callSpec) {
|
||||||
LOG.debug("iGPSport send call notification");
|
LOG.debug("iGPSport send call notification");
|
||||||
TransactionBuilder builder = new TransactionBuilder("CALL");
|
TransactionBuilder builder = createTransactionBuilder("CALL");
|
||||||
Ins.ins_msg.Builder insMsgBuilder = Ins.ins_msg.newBuilder();
|
Ins.ins_msg.Builder insMsgBuilder = Ins.ins_msg.newBuilder();
|
||||||
insMsgBuilder.setServiceType(Common.service_type_index.enum_SERVICE_TYPE_INDEX_INS);
|
insMsgBuilder.setServiceType(Common.service_type_index.enum_SERVICE_TYPE_INDEX_INS);
|
||||||
insMsgBuilder.setInsServiceType(Ins.INS_SERVICE_TYPE.enum_INS_SERVICE_TYPE_CALL);
|
insMsgBuilder.setInsServiceType(Ins.INS_SERVICE_TYPE.enum_INS_SERVICE_TYPE_CALL);
|
||||||
@@ -529,7 +536,7 @@ public class IGPSportDeviceSupport extends AbstractBTLEDeviceSupport {
|
|||||||
insMsgBuilder.setInsDataMsg(insDataMsgBuilder);
|
insMsgBuilder.setInsDataMsg(insDataMsgBuilder);
|
||||||
byte[] callData = craftData(insMsgBuilder.getServiceType().getNumber(), insMsgBuilder.getInsServiceType().getNumber(), insMsgBuilder.getInsOperateType().getNumber(), insMsgBuilder.build().toByteArray());
|
byte[] callData = craftData(insMsgBuilder.getServiceType().getNumber(), insMsgBuilder.getInsServiceType().getNumber(), insMsgBuilder.getInsOperateType().getNumber(), insMsgBuilder.build().toByteArray());
|
||||||
builder.write(writeCharacteristic, callData);
|
builder.write(writeCharacteristic, callData);
|
||||||
builder.queue(getQueue());
|
builder.queue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -545,7 +552,7 @@ public class IGPSportDeviceSupport extends AbstractBTLEDeviceSupport {
|
|||||||
byte[] cycleDataMsgBytes = craftData(cycleDataMsg.getServiceType().getNumber(), 0xff, cycleDataMsg.getCyclingDataOperateType().getNumber(), cycleDataMsg.build().toByteArray(), true);
|
byte[] cycleDataMsgBytes = craftData(cycleDataMsg.getServiceType().getNumber(), 0xff, cycleDataMsg.getCyclingDataOperateType().getNumber(), cycleDataMsg.build().toByteArray(), true);
|
||||||
|
|
||||||
builder.write(writeCharacteristicThird, cycleDataMsgBytes);
|
builder.write(writeCharacteristicThird, cycleDataMsgBytes);
|
||||||
builder.queue(getQueue());
|
builder.queue();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -566,7 +573,7 @@ public class IGPSportDeviceSupport extends AbstractBTLEDeviceSupport {
|
|||||||
setMeasurementSystem(builder);
|
setMeasurementSystem(builder);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
builder.queue(getQueue());
|
builder.queue();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
GB.toast(getContext(), "Error sending configuration: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
|
GB.toast(getContext(), "Error sending configuration: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
|
||||||
}
|
}
|
||||||
@@ -627,7 +634,7 @@ public class IGPSportDeviceSupport extends AbstractBTLEDeviceSupport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInstallApp(Uri uri) {
|
public void onInstallApp(Uri uri, @NonNull final Bundle options) {
|
||||||
//final IGPSportGpxRouteInstallHandler gpxRouteHandler = new IGPSportGpxRouteInstallHandler(uri, getContext());
|
//final IGPSportGpxRouteInstallHandler gpxRouteHandler = new IGPSportGpxRouteInstallHandler(uri, getContext());
|
||||||
final IGPSportRouteInstallHandler routeHandler = new IGPSportRouteInstallHandler(uri, getContext());
|
final IGPSportRouteInstallHandler routeHandler = new IGPSportRouteInstallHandler(uri, getContext());
|
||||||
if (routeHandler.isValid()) {
|
if (routeHandler.isValid()) {
|
||||||
@@ -665,7 +672,7 @@ public class IGPSportDeviceSupport extends AbstractBTLEDeviceSupport {
|
|||||||
|
|
||||||
private void handleWeather() {
|
private void handleWeather() {
|
||||||
// Send weather
|
// Send weather
|
||||||
final ArrayList<WeatherSpec> specs = new ArrayList<>(nodomain.freeyourgadget.gadgetbridge.model.Weather.getInstance().getWeatherSpecs());
|
final ArrayList<WeatherSpec> specs = new ArrayList<>(nodomain.freeyourgadget.gadgetbridge.model.weather.Weather.getWeatherSpecs());
|
||||||
if (!specs.isEmpty()) {
|
if (!specs.isEmpty()) {
|
||||||
weatherManager.handleWeather(specs.get(0));
|
weatherManager.handleWeather(specs.get(0));
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-1
@@ -1,3 +1,19 @@
|
|||||||
|
/* Copyright (C) 2025 Vitaliy Tomin, Thomas Kuehne
|
||||||
|
|
||||||
|
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.igpsport;
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.igpsport;
|
||||||
|
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
@@ -68,7 +84,7 @@ public class IGPSportDownloadManager {
|
|||||||
byte[] cycleDataMsgBytes = IGPSportDeviceSupport.craftData(cycleDataMsg.getServiceType().getNumber(), 0xff, cycleDataMsg.getCyclingDataOperateType().getNumber(), cycleDataMsg.build().toByteArray(), true);
|
byte[] cycleDataMsgBytes = IGPSportDeviceSupport.craftData(cycleDataMsg.getServiceType().getNumber(), 0xff, cycleDataMsg.getCyclingDataOperateType().getNumber(), cycleDataMsg.build().toByteArray(), true);
|
||||||
|
|
||||||
builder.write(support.writeCharacteristicThird, cycleDataMsgBytes);
|
builder.write(support.writeCharacteristicThird, cycleDataMsgBytes);
|
||||||
builder.queue(support.getQueue());
|
builder.queue();
|
||||||
// } else {
|
// } else {
|
||||||
// syncNextFile();
|
// syncNextFile();
|
||||||
// }
|
// }
|
||||||
|
|||||||
+24
-8
@@ -1,3 +1,19 @@
|
|||||||
|
/* Copyright (C) 2025 Vitaliy Tomin, Thomas Kuehne
|
||||||
|
|
||||||
|
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.igpsport;
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.igpsport;
|
||||||
|
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
@@ -14,6 +30,7 @@ import java.util.List;
|
|||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.igpsport.IGPSportRouteInstallHandler;
|
import nodomain.freeyourgadget.gadgetbridge.devices.igpsport.IGPSportRouteInstallHandler;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
|
||||||
@@ -109,9 +126,8 @@ public class IGPSportRoutesManager {
|
|||||||
fileOperationbuilder.build().toByteArray(),
|
fileOperationbuilder.build().toByteArray(),
|
||||||
handler.getBytes());
|
handler.getBytes());
|
||||||
builder.writeChunkedData(support.writeCharacteristicFourth, fileOperationBytes, support.getMTU());
|
builder.writeChunkedData(support.writeCharacteristicFourth, fileOperationBytes, support.getMTU());
|
||||||
support.getDevice().setBusyTask("Installing route");
|
builder.setBusyTask(R.string.task_installing_route);
|
||||||
support.getDevice().sendDeviceUpdateIntent(support.getContext());
|
builder.queue();
|
||||||
builder.queue(support.getQueue());
|
|
||||||
|
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
GB.toast(support.getContext(), "Gpx install error: " + e.getMessage(), Toast.LENGTH_LONG, GB.ERROR, e);
|
GB.toast(support.getContext(), "Gpx install error: " + e.getMessage(), Toast.LENGTH_LONG, GB.ERROR, e);
|
||||||
@@ -129,7 +145,7 @@ public class IGPSportRoutesManager {
|
|||||||
byte[] routePlanBytes = support.craftData(routePlanBuilder.getServiceType().getNumber(), 0xff, routePlanBuilder.getRoutePlanOperateType().getNumber(), routePlanBuilder.build().toByteArray());
|
byte[] routePlanBytes = support.craftData(routePlanBuilder.getServiceType().getNumber(), 0xff, routePlanBuilder.getRoutePlanOperateType().getNumber(), routePlanBuilder.build().toByteArray());
|
||||||
builder.write(support.writeCharacteristicFourth, routePlanBytes);
|
builder.write(support.writeCharacteristicFourth, routePlanBytes);
|
||||||
|
|
||||||
builder.queue(support.getQueue());
|
builder.queue();
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
GB.toast(support.getContext(), "Gpx get list error: " + e.getMessage(), Toast.LENGTH_LONG, GB.ERROR, e);
|
GB.toast(support.getContext(), "Gpx get list error: " + e.getMessage(), Toast.LENGTH_LONG, GB.ERROR, e);
|
||||||
@@ -191,7 +207,7 @@ public class IGPSportRoutesManager {
|
|||||||
private void requestFiles() {
|
private void requestFiles() {
|
||||||
|
|
||||||
int endFile = (fileNumber > fileListSupportNumMax ? fileListSupportNumMax - 1 : fileNumber)+startFile;
|
int endFile = (fileNumber > fileListSupportNumMax ? fileListSupportNumMax - 1 : fileNumber)+startFile;
|
||||||
TransactionBuilder builder = new TransactionBuilder("get files list");
|
TransactionBuilder builder = support.createTransactionBuilder("get files list");
|
||||||
|
|
||||||
|
|
||||||
RoutePlan.route_plan_data_msg.Builder routePlan2ndBuilder = RoutePlan.route_plan_data_msg.newBuilder();
|
RoutePlan.route_plan_data_msg.Builder routePlan2ndBuilder = RoutePlan.route_plan_data_msg.newBuilder();
|
||||||
@@ -205,7 +221,7 @@ public class IGPSportRoutesManager {
|
|||||||
fileNumber -= fileListSupportNumMax;
|
fileNumber -= fileListSupportNumMax;
|
||||||
|
|
||||||
builder.write(support.writeCharacteristicFourth, routePlan2ndBytes);
|
builder.write(support.writeCharacteristicFourth, routePlan2ndBytes);
|
||||||
builder.queue(support.getQueue());
|
builder.queue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleRouteNumber(byte[] pbData) throws InvalidProtocolBufferException {
|
public void handleRouteNumber(byte[] pbData) throws InvalidProtocolBufferException {
|
||||||
@@ -235,7 +251,7 @@ public class IGPSportRoutesManager {
|
|||||||
routePlanBuilder.addRoutePlanInfoMsg(installedRouteHash.get(uuid).toMessage());
|
routePlanBuilder.addRoutePlanInfoMsg(installedRouteHash.get(uuid).toMessage());
|
||||||
byte[] routePlanBytes = support.craftData(routePlanBuilder.getServiceType().getNumber(), 0xff, routePlanBuilder.getRoutePlanOperateType().getNumber(), routePlanBuilder.build().toByteArray());
|
byte[] routePlanBytes = support.craftData(routePlanBuilder.getServiceType().getNumber(), 0xff, routePlanBuilder.getRoutePlanOperateType().getNumber(), routePlanBuilder.build().toByteArray());
|
||||||
builder.write(support.writeCharacteristicFourth, routePlanBytes);
|
builder.write(support.writeCharacteristicFourth, routePlanBytes);
|
||||||
builder.queue(support.getQueue());
|
builder.queue();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
GB.toast(support.getContext(), "Failed to start gpx navigation: " + e.getMessage(), Toast.LENGTH_LONG, GB.ERROR, e);
|
GB.toast(support.getContext(), "Failed to start gpx navigation: " + e.getMessage(), Toast.LENGTH_LONG, GB.ERROR, e);
|
||||||
}
|
}
|
||||||
@@ -250,7 +266,7 @@ public class IGPSportRoutesManager {
|
|||||||
routePlanBuilder.addRoutePlanInfoMsg(installedRouteHash.get(uuid).toMessage());
|
routePlanBuilder.addRoutePlanInfoMsg(installedRouteHash.get(uuid).toMessage());
|
||||||
byte[] routePlanBytes = support.craftData(routePlanBuilder.getServiceType().getNumber(), 0xff, routePlanBuilder.getRoutePlanOperateType().getNumber(), routePlanBuilder.build().toByteArray());
|
byte[] routePlanBytes = support.craftData(routePlanBuilder.getServiceType().getNumber(), 0xff, routePlanBuilder.getRoutePlanOperateType().getNumber(), routePlanBuilder.build().toByteArray());
|
||||||
builder.write(support.writeCharacteristicFourth, routePlanBytes);
|
builder.write(support.writeCharacteristicFourth, routePlanBytes);
|
||||||
builder.queue(support.getQueue());
|
builder.queue();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
GB.toast(support.getContext(), "Failed to delete gpx navigation: " + e.getMessage(), Toast.LENGTH_LONG, GB.ERROR, e);
|
GB.toast(support.getContext(), "Failed to delete gpx navigation: " + e.getMessage(), Toast.LENGTH_LONG, GB.ERROR, e);
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-19
@@ -1,3 +1,19 @@
|
|||||||
|
/* Copyright (C) 2025 Vitaliy Tomin, Thomas Kuehne
|
||||||
|
|
||||||
|
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.igpsport;
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.igpsport;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -119,27 +135,27 @@ public class IGPSportWeather {
|
|||||||
TransactionBuilder builder = support.performInitialized("set weather");
|
TransactionBuilder builder = support.performInitialized("set weather");
|
||||||
Back.back_msg.Builder weatherMsg = Back.back_msg.newBuilder();
|
Back.back_msg.Builder weatherMsg = Back.back_msg.newBuilder();
|
||||||
Back.weather_current_data_message.Builder currentWeatherMsg =Back.weather_current_data_message.newBuilder();
|
Back.weather_current_data_message.Builder currentWeatherMsg =Back.weather_current_data_message.newBuilder();
|
||||||
currentWeatherMsg.setCurDayMinTemp(weatherSpec.todayMinTemp-273);
|
currentWeatherMsg.setCurDayMinTemp(weatherSpec.getTodayMinTemp()-273);
|
||||||
currentWeatherMsg.setCurDayMaxTemp(weatherSpec.todayMaxTemp-273);
|
currentWeatherMsg.setCurDayMaxTemp(weatherSpec.getTodayMaxTemp()-273);
|
||||||
currentWeatherMsg.setCurTemperature(weatherSpec.currentTemp-273);
|
currentWeatherMsg.setCurTemperature(weatherSpec.getCurrentTemp()-273);
|
||||||
currentWeatherMsg.setCurWeather(IGPSportWeather.convertOWMtoQWeather(weatherSpec.currentConditionCode));
|
currentWeatherMsg.setCurWeather(IGPSportWeather.convertOWMtoQWeather(weatherSpec.getCurrentConditionCode()));
|
||||||
currentWeatherMsg.setWindDeg(String.valueOf(weatherSpec.windDirection));
|
currentWeatherMsg.setWindDeg(String.valueOf(weatherSpec.getWindDirection()));
|
||||||
currentWeatherMsg.setWindSpd(String.valueOf(Math.round(weatherSpec.windSpeed)));
|
currentWeatherMsg.setWindSpd(String.valueOf(Math.round(weatherSpec.getWindSpeed())));
|
||||||
currentWeatherMsg.setTime(formatterHourly.format(currentTime));
|
currentWeatherMsg.setTime(formatterHourly.format(currentTime));
|
||||||
|
|
||||||
int currentDay=0;
|
int currentDay=0;
|
||||||
|
|
||||||
for (final WeatherSpec.Daily forecast : weatherSpec.forecasts) {
|
for (final WeatherSpec.Daily forecast : weatherSpec.getForecasts()) {
|
||||||
|
|
||||||
LocalDateTime now = LocalDateTime.now();
|
LocalDateTime now = LocalDateTime.now();
|
||||||
currentDay++;
|
currentDay++;
|
||||||
LocalDateTime tomorrow = now.plusDays(currentDay);
|
LocalDateTime tomorrow = now.plusDays(currentDay);
|
||||||
DateTimeFormatter formatterDaily = DateTimeFormatter.ISO_LOCAL_DATE; //"yyyy-MM-dd"
|
DateTimeFormatter formatterDaily = DateTimeFormatter.ISO_LOCAL_DATE; //"yyyy-MM-dd"
|
||||||
weatherMsg.addThreeDaysMsg(Back.weather_three_days_data_message.newBuilder()
|
weatherMsg.addThreeDaysMsg(Back.weather_three_days_data_message.newBuilder()
|
||||||
.setWeatherIndex(IGPSportWeather.convertOWMtoQWeather(forecast.conditionCode))
|
.setWeatherIndex(IGPSportWeather.convertOWMtoQWeather(forecast.getConditionCode()))
|
||||||
.setRainProb(forecast.precipProbability)
|
.setRainProb(forecast.getPrecipProbability())
|
||||||
.setMaxTemp(forecast.maxTemp-273)
|
.setMaxTemp(forecast.getMaxTemp()-273)
|
||||||
.setMinTemp(forecast.minTemp-273)
|
.setMinTemp(forecast.getMinTemp()-273)
|
||||||
.setDate(tomorrow.format(formatterDaily)).build());
|
.setDate(tomorrow.format(formatterDaily)).build());
|
||||||
|
|
||||||
if (currentDay > 2) //we only need 3 days
|
if (currentDay > 2) //we only need 3 days
|
||||||
@@ -147,14 +163,14 @@ public class IGPSportWeather {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int currentHour=0;
|
int currentHour=0;
|
||||||
for (final WeatherSpec.Hourly hourly : weatherSpec.hourly) {
|
for (final WeatherSpec.Hourly hourly : weatherSpec.getHourly()) {
|
||||||
weatherMsg.addThreeHoursMsg(Back.weather_three_hour_data_memsage.newBuilder()
|
weatherMsg.addThreeHoursMsg(Back.weather_three_hour_data_memsage.newBuilder()
|
||||||
.setWatherIndex(IGPSportWeather.convertOWMtoQWeather(hourly.conditionCode))
|
.setWatherIndex(IGPSportWeather.convertOWMtoQWeather(hourly.getConditionCode()))
|
||||||
.setRainProb(hourly.precipProbability)
|
.setRainProb(hourly.getPrecipProbability())
|
||||||
.setTemp(hourly.temp-273)
|
.setTemp(hourly.getTemp()-273)
|
||||||
.setTime(formatterHourly.format(Instant.ofEpochMilli(hourly.timestamp * 1000L)))
|
.setTime(formatterHourly.format(Instant.ofEpochMilli(hourly.getTimestamp() * 1000L)))
|
||||||
.setWindDeg(String.valueOf(hourly.windDirection))
|
.setWindDeg(String.valueOf(hourly.getWindDirection()))
|
||||||
.setWindSpd(String.valueOf(Math.round(hourly.windSpeed))).build());
|
.setWindSpd(String.valueOf(Math.round(hourly.getWindSpeed()))).build());
|
||||||
|
|
||||||
currentHour++;
|
currentHour++;
|
||||||
if (currentHour > 3 ) // its called three hour weather but shows actually 4 entries
|
if (currentHour > 3 ) // its called three hour weather but shows actually 4 entries
|
||||||
@@ -174,7 +190,7 @@ public class IGPSportWeather {
|
|||||||
weatherMsg.getBackOperateType().getNumber(),
|
weatherMsg.getBackOperateType().getNumber(),
|
||||||
weatherMsg.build().toByteArray());
|
weatherMsg.build().toByteArray());
|
||||||
builder.writeChunkedData(support.writeCharacteristicFourth, weatherBytes, support.getMTU());
|
builder.writeChunkedData(support.writeCharacteristicFourth, weatherBytes, support.getMTU());
|
||||||
builder.queue(support.getQueue());
|
builder.queue();
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
|||||||
@@ -2250,6 +2250,11 @@
|
|||||||
<string name="devicetype_earfun_air_s" translatable="false">EarFun Air S</string>
|
<string name="devicetype_earfun_air_s" translatable="false">EarFun Air S</string>
|
||||||
<string name="devicetype_earfun_air_pro_4" translatable="false">EarFun Air Pro 4</string>
|
<string name="devicetype_earfun_air_pro_4" translatable="false">EarFun Air Pro 4</string>
|
||||||
<string name="devicetype_atc_ble_oepl" translatable="false">ATC_BLE_OEPL</string>
|
<string name="devicetype_atc_ble_oepl" translatable="false">ATC_BLE_OEPL</string>
|
||||||
|
<string name="devicetype_igpsport_bsc200">IGPSPORT BSC200</string>
|
||||||
|
<string name="devicetype_igpsport_igs630">IGPSPORT iGS630</string>
|
||||||
|
<string name="task_installing_route">Installing route</string>
|
||||||
|
<string name="route_upload_completed">Route upload completed</string>
|
||||||
|
<string name="route_upload_failed">Route upload failed</string>
|
||||||
<string name="choose_auto_export_location">Choose export location</string>
|
<string name="choose_auto_export_location">Choose export location</string>
|
||||||
<string name="notification_channel_name">General</string>
|
<string name="notification_channel_name">General</string>
|
||||||
<string name="notification_channel_high_priority_name">High-priority</string>
|
<string name="notification_channel_high_priority_name">High-priority</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user