mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2025-06-05 21:49:48 +02:00
Even Realities G1: Support more things
Add support for: - Charging status - Battery level of the case - Fetching Serial Number - Parsing hardware information from serial number - Add Hard Reset button - Toggle for device level debug logging - Bug Fix: kill heartbeat worker on disconnect - Weather - Toggle for 12H/24H time - Set the dashboard to minimal on connection
This commit is contained in:
@@ -324,6 +324,7 @@ public class DeviceSettingsPreferenceConst {
|
||||
|
||||
public static final String PREF_APP_LOGS_START = "pref_app_logs_start";
|
||||
public static final String PREF_APP_LOGS_STOP = "pref_app_logs_stop";
|
||||
public static final String PREF_DEVICE_LOGS_TOGGLE = "device_logs_enabled";
|
||||
|
||||
public static final String MORNING_UPDATES_ENABLED = "morning_updates_enabled";
|
||||
public static final String MORNING_UPDATES_CATEGORIES_SORTABLE = "morning_updates_categories";
|
||||
|
@@ -830,6 +830,8 @@ public class DeviceSpecificSettingsFragment extends AbstractPreferenceFragment i
|
||||
|
||||
addPreferenceHandlerFor(PREF_DUAL_DEVICE_SUPPORT);
|
||||
|
||||
addPreferenceHandlerFor(PREF_DEVICE_LOGS_TOGGLE);
|
||||
|
||||
addPreferenceHandlerFor(PREF_USER_FITNESS_GOAL);
|
||||
addPreferenceHandlerFor(PREF_USER_FITNESS_GOAL_NOTIFICATION);
|
||||
addPreferenceHandlerFor(PREF_USER_FITNESS_GOAL_SECONDARY);
|
||||
|
@@ -370,6 +370,7 @@ public class GBDeviceAdapterv2 extends ListAdapter<GBDevice, GBDeviceAdapterv2.V
|
||||
int batteryIcon = device.getBatteryIcon(batteryIndex);
|
||||
int batteryLabel = device.getBatteryLabel(batteryIndex); //unused for now
|
||||
batteryIcons[batteryIndex].setImageResource(R.drawable.level_list_battery);
|
||||
batteryStatusLabels[batteryIndex].setAlpha(1.0f);
|
||||
|
||||
if (batteryIcon != GBDevice.BATTERY_ICON_DEFAULT){
|
||||
batteryIcons[batteryIndex].setImageResource(batteryIcon);
|
||||
@@ -381,7 +382,16 @@ public class GBDeviceAdapterv2 extends ListAdapter<GBDevice, GBDeviceAdapterv2.V
|
||||
BatteryState.BATTERY_CHARGING_FULL.equals(batteryState)) {
|
||||
batteryIcons[batteryIndex].setImageLevel(device.getBatteryLevel(batteryIndex) + 100);
|
||||
} else {
|
||||
batteryIcons[batteryIndex].setImageLevel(device.getBatteryLevel(batteryIndex));
|
||||
if (BatteryState.NO_BATTERY.equals(batteryState)) {
|
||||
// There is a level to show, but the device has indicated that it does not
|
||||
// know the status of the battery. This can be used to indicate the last
|
||||
// known state of charge for things like a headphones case that is not
|
||||
// actively connected but there is a previously known level.
|
||||
batteryStatusLabels[batteryIndex].setAlpha(0.3f);
|
||||
batteryIcons[batteryIndex].setImageLevel(300);
|
||||
} else {
|
||||
batteryIcons[batteryIndex].setImageLevel(device.getBatteryLevel(batteryIndex));
|
||||
}
|
||||
}
|
||||
} else if (BatteryState.NO_BATTERY.equals(batteryState) && batteryVoltage != GBDevice.BATTERY_UNKNOWN) {
|
||||
batteryStatusLabels[batteryIndex].setText(String.format(Locale.getDefault(), "%.2f", batteryVoltage));
|
||||
|
@@ -0,0 +1,71 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.deviceevents;
|
||||
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
|
||||
|
||||
/** This event is basically the same as GBDeviceEventBatteryInfo, but it will only update the fields
|
||||
* that are explicitly set. This can be used when the caller that is sending the event doesn't have
|
||||
* all of the battery state available to make the call. For example the device has a sent an event
|
||||
* that indicates the device has started charging, but does not send the current level. If that
|
||||
* caller tries to fetch the current state and resend it, there is a race condition that may cause a
|
||||
* concurrent update to be dropped.
|
||||
*/
|
||||
public class GBDeviceEventBatteryIncrementalInfo extends GBDeviceEventBatteryInfo {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GBDeviceEventBatteryIncrementalInfo.class);
|
||||
|
||||
private enum UpdateType {
|
||||
LAST_CHARGE_TIME,
|
||||
STATE,
|
||||
LEVEL,
|
||||
VOLTAGE;
|
||||
}
|
||||
|
||||
private final UpdateType updateType;
|
||||
|
||||
public GBDeviceEventBatteryIncrementalInfo(int batteryIndex, GregorianCalendar lastChargeTime) {
|
||||
super();
|
||||
super.batteryIndex = batteryIndex;
|
||||
super.lastChargeTime = lastChargeTime;
|
||||
super.level = GBDevice.BATTERY_UNKNOWN;
|
||||
this.updateType = UpdateType.LAST_CHARGE_TIME;
|
||||
}
|
||||
|
||||
public GBDeviceEventBatteryIncrementalInfo(int batteryIndex, BatteryState state) {
|
||||
super();
|
||||
super.batteryIndex = batteryIndex;
|
||||
super.state = state;
|
||||
super.level = GBDevice.BATTERY_UNKNOWN;
|
||||
this.updateType = UpdateType.STATE;
|
||||
|
||||
}
|
||||
|
||||
public GBDeviceEventBatteryIncrementalInfo(int batteryIndex, int level) {
|
||||
super();
|
||||
super.batteryIndex = batteryIndex;
|
||||
super.level = level;
|
||||
this.updateType = UpdateType.LEVEL;
|
||||
}
|
||||
|
||||
public GBDeviceEventBatteryIncrementalInfo(int batteryIndex, float voltage) {
|
||||
super();
|
||||
super.batteryIndex = batteryIndex;
|
||||
super.level = GBDevice.BATTERY_UNKNOWN;
|
||||
super.voltage = voltage;
|
||||
this.updateType = UpdateType.VOLTAGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setDeviceValues(final GBDevice device) {
|
||||
switch (updateType) {
|
||||
case STATE -> device.setBatteryState(super.state, this.batteryIndex);
|
||||
case LEVEL -> device.setBatteryLevel(super.level, this.batteryIndex);
|
||||
case VOLTAGE -> device.setBatteryVoltage(super.voltage, this.batteryIndex);
|
||||
}
|
||||
}
|
||||
}
|
@@ -62,6 +62,12 @@ public class GBDeviceEventBatteryInfo extends GBDeviceEvent {
|
||||
return super.toString() + "index: " + batteryIndex + ", level: " + level;
|
||||
}
|
||||
|
||||
protected void setDeviceValues(final GBDevice device) {
|
||||
device.setBatteryLevel(this.level, this.batteryIndex);
|
||||
device.setBatteryState(this.state, this.batteryIndex);
|
||||
device.setBatteryVoltage(this.voltage, this.batteryIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate(final Context context, final GBDevice device) {
|
||||
if ((level < 0 || level > 100) && level != GBDevice.BATTERY_UNKNOWN) {
|
||||
@@ -74,9 +80,7 @@ public class GBDeviceEventBatteryInfo extends GBDeviceEvent {
|
||||
this.level != GBDevice.BATTERY_UNKNOWN &&
|
||||
this.level > device.getBatteryLevel(this.batteryIndex);
|
||||
|
||||
device.setBatteryLevel(this.level, this.batteryIndex);
|
||||
device.setBatteryState(this.state, this.batteryIndex);
|
||||
device.setBatteryVoltage(this.voltage, this.batteryIndex);
|
||||
setDeviceValues(device);
|
||||
|
||||
final DevicePrefs devicePrefs = GBApplication.getDevicePrefs(device);
|
||||
final BatteryConfig batteryConfig = device.getDeviceCoordinator().getBatteryConfig(device)[this.batteryIndex];
|
||||
|
@@ -60,7 +60,9 @@ public class GBDeviceEventVersionInfo extends GBDeviceEvent {
|
||||
if (fwVersion2 != null) {
|
||||
device.setFirmwareVersion2(fwVersion2);
|
||||
}
|
||||
device.setModel(hwVersion);
|
||||
if (hwVersion != null) {
|
||||
device.setModel(hwVersion);
|
||||
}
|
||||
device.sendDeviceUpdateIntent(context);
|
||||
}
|
||||
}
|
||||
|
@@ -18,6 +18,8 @@ import java.util.regex.Pattern;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettings;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsScreen;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLEDeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCardAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
@@ -168,16 +170,15 @@ public class G1DeviceCoordinator extends AbstractBLEDeviceCoordinator {
|
||||
|
||||
@Override
|
||||
public int getBatteryCount(final GBDevice device) {
|
||||
ItemWithDetails right_name =
|
||||
device.getDeviceInfo(G1Constants.Side.RIGHT.getNameKey());
|
||||
ItemWithDetails right_address =
|
||||
device.getDeviceInfo(G1Constants.Side.RIGHT.getAddressKey());
|
||||
if (right_name != null && !right_name.getDetails().isEmpty() && right_address != null &&
|
||||
!right_address.getDetails().isEmpty()) {
|
||||
return 2;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BatteryConfig[] getBatteryConfig(final GBDevice device) {
|
||||
BatteryConfig battery1 = new BatteryConfig(0, GBDevice.BATTERY_ICON_DEFAULT, R.string.even_realities_left_lens);
|
||||
BatteryConfig battery2 = new BatteryConfig(1, GBDevice.BATTERY_ICON_DEFAULT, R.string.even_realities_right_lens);
|
||||
BatteryConfig battery3 = new BatteryConfig(2, R.drawable.level_list_even_realities_g1_case_battery, R.string.battery_case);
|
||||
return new BatteryConfig[]{battery1, battery2, battery3};
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -203,12 +204,26 @@ public class G1DeviceCoordinator extends AbstractBLEDeviceCoordinator {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int[] getSupportedDeviceSpecificSettings(GBDevice device) {
|
||||
public DeviceSpecificSettings getDeviceSpecificSettings(final GBDevice device) {
|
||||
final DeviceSpecificSettings deviceSpecificSettings = new DeviceSpecificSettings();
|
||||
if (device.isConnected()) {
|
||||
return new int[]{R.xml.devicesettings_even_realities_g1_display};
|
||||
} else {
|
||||
return new int[]{};
|
||||
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_even_realities_g1_display);
|
||||
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_timeformat);
|
||||
final List<Integer> developer =
|
||||
deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.DEVELOPER);
|
||||
developer.add(R.xml.devicesettings_header_system);
|
||||
developer.add(R.xml.devicesettings_debug_logs_toggle);
|
||||
}
|
||||
return deviceSpecificSettings;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// Gadget bridge feature support declarations //
|
||||
////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public boolean supportsWeather() { return true; }
|
||||
|
||||
}
|
||||
|
@@ -3,8 +3,10 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.evenrealities;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.function.Function;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
|
||||
|
||||
@@ -116,6 +118,30 @@ public class G1Communications {
|
||||
}
|
||||
}
|
||||
|
||||
public static class CommandSendReset extends CommandHandler {
|
||||
public CommandSendReset() {
|
||||
super(false, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serialize() {
|
||||
return new byte[] {
|
||||
G1Constants.CommandId.SYSTEM.id,
|
||||
G1Constants.SystemSubCommand.RESET.id
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean responseMatches(byte[] payload) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "send_reset";
|
||||
}
|
||||
}
|
||||
|
||||
public static class CommandGetFirmwareInfo extends CommandHandler {
|
||||
public CommandGetFirmwareInfo(Function<byte[], Boolean> callback) {
|
||||
super(true, callback);
|
||||
@@ -123,7 +149,10 @@ public class G1Communications {
|
||||
|
||||
@Override
|
||||
public byte[] serialize() {
|
||||
return new byte[] { G1Constants.CommandId.FW_INFO_REQUEST.id, 0x74 };
|
||||
return new byte[] {
|
||||
G1Constants.CommandId.SYSTEM.id,
|
||||
G1Constants.SystemSubCommand.GET_FW_INFO.id
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -161,6 +190,10 @@ public class G1Communications {
|
||||
public String getName() {
|
||||
return "get_battery_info";
|
||||
}
|
||||
|
||||
public static byte getBatteryPercent(byte[] payload) {
|
||||
return payload[2];
|
||||
}
|
||||
}
|
||||
|
||||
public static class CommandSendHeartBeat extends CommandHandler {
|
||||
@@ -204,9 +237,14 @@ public class G1Communications {
|
||||
this.timeMilliseconds = timeMilliseconds;
|
||||
this.use12HourFormat = use12HourFormat;
|
||||
if (weatherInfo != null) {
|
||||
// TODO need to convert the weather spec enums to the ER enums.
|
||||
this.weatherIcon = 0x01;
|
||||
this.tempInCelsius = (byte) weatherInfo.currentTemp;
|
||||
this.weatherIcon = G1Constants.fromOpenWeatherCondition(weatherInfo.currentConditionCode);
|
||||
// Convert sunny to a moon if the current time stamp is between sunrise and sunset.
|
||||
if (timeMilliseconds / 1000 >= weatherInfo.sunSet &&
|
||||
this.weatherIcon == G1Constants.WeatherId.SUNNY) {
|
||||
this.weatherIcon = G1Constants.WeatherId.NIGHT;
|
||||
}
|
||||
// Convert Kelvin -> Celsius.
|
||||
this.tempInCelsius = (byte) (weatherInfo.currentTemp - 273);
|
||||
} else {
|
||||
this.weatherIcon = 0x00;
|
||||
this.tempInCelsius = 0x00;
|
||||
@@ -224,11 +262,11 @@ public class G1Communications {
|
||||
public byte[] serialize() {
|
||||
byte[] packet = new byte[] {
|
||||
G1Constants.CommandId.DASHBOARD_CONFIG.id,
|
||||
G1Constants.DashboardConfigSubCommand.SET_TIME_AND_WEATHER.id,
|
||||
0x15, // Length = 21 bytes
|
||||
0x00,
|
||||
sequence,
|
||||
// Magic number?
|
||||
0x01,
|
||||
// Subcommand
|
||||
G1Constants.DashboardConfig.SUB_COMMAND_SET_TIME_AND_WEATHER,
|
||||
// Time 32bit place holders
|
||||
(byte) 0x00,
|
||||
(byte) 0x00,
|
||||
@@ -246,10 +284,10 @@ public class G1Communications {
|
||||
// Weather info
|
||||
this.weatherIcon,
|
||||
tempInCelsius,
|
||||
// F/C
|
||||
(byte)(useFahrenheit ? 0x01 : 0x00),
|
||||
// 24H/12H
|
||||
(byte)(use12HourFormat ? 0x01 : 0x00)
|
||||
useFahrenheit ? G1Constants.TemperatureUnit.FAHRENHEIT
|
||||
: G1Constants.TemperatureUnit.CELSIUS,
|
||||
use12HourFormat ? G1Constants.TimeFormat.TWELVE_HOUR
|
||||
: G1Constants.TimeFormat.TWENTY_FOUR_HOUR
|
||||
};
|
||||
BLETypeConversions.writeUint32(packet, 5, (int)(timeMilliseconds / 1000));
|
||||
BLETypeConversions.writeUint64(packet, 9, timeMilliseconds);
|
||||
@@ -259,14 +297,11 @@ public class G1Communications {
|
||||
|
||||
@Override
|
||||
public boolean responseMatches(byte[] payload) {
|
||||
if (payload.length < 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Command should match and the sequence should match.
|
||||
return payload[0] == G1Constants.CommandId.DASHBOARD_CONFIG.id &&
|
||||
payload[1] == G1Constants.DashboardConfigSubCommand.SET_TIME_AND_WEATHER.id &&
|
||||
payload[3] == sequence;
|
||||
return payload.length >= 5 &&
|
||||
payload[0] == G1Constants.CommandId.DASHBOARD_CONFIG.id &&
|
||||
payload[3] == sequence &&
|
||||
payload[4] == G1Constants.DashboardConfig.SUB_COMMAND_SET_TIME_AND_WEATHER;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -275,6 +310,46 @@ public class G1Communications {
|
||||
}
|
||||
}
|
||||
|
||||
public static class CommandSetDashboardModeSettings extends CommandHandler {
|
||||
byte mode;
|
||||
byte secondaryPaneMode;
|
||||
public CommandSetDashboardModeSettings(byte mode, byte secondaryPaneMode) {
|
||||
super(true, null);
|
||||
this.mode = mode;
|
||||
this.secondaryPaneMode = secondaryPaneMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsGlobalSequence() { return true; }
|
||||
|
||||
@Override
|
||||
public byte[] serialize() {
|
||||
return new byte[]{
|
||||
G1Constants.CommandId.DASHBOARD_CONFIG.id,
|
||||
0x07, // Length
|
||||
0x00, // pad
|
||||
sequence,
|
||||
G1Constants.DashboardConfig.SUB_COMMAND_SET_MODE,
|
||||
mode,
|
||||
secondaryPaneMode
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean responseMatches(byte[] payload) {
|
||||
// Command should match and the sequence should match.
|
||||
return payload.length >= 5 &&
|
||||
payload[0] == G1Constants.CommandId.DASHBOARD_CONFIG.id &&
|
||||
payload[3] == sequence &&
|
||||
payload[4] == G1Constants.DashboardConfig.SUB_COMMAND_SET_MODE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "set_dashboard_mode_settings";
|
||||
}
|
||||
}
|
||||
|
||||
public static class CommandGetSilentModeSettings extends CommandHandler {
|
||||
public CommandGetSilentModeSettings(Function<byte[], Boolean> callback) {
|
||||
super(true, callback);
|
||||
@@ -373,7 +448,7 @@ public class G1Communications {
|
||||
public byte[] serialize() {
|
||||
return new byte[] {
|
||||
G1Constants.CommandId.SET_DISPLAY_SETTINGS.id,
|
||||
0x08, // Subcommand?
|
||||
0x08, // Length
|
||||
0x00,
|
||||
sequence,
|
||||
0x02, // Seems to be a magic number?
|
||||
@@ -560,4 +635,109 @@ public class G1Communications {
|
||||
return "set_wear_detection_settings_" + (enable ? "enabled" : "disabled");
|
||||
}
|
||||
}
|
||||
|
||||
public static class CommandGetSerialNumber extends CommandHandler {
|
||||
public CommandGetSerialNumber(Function<byte[], Boolean> callback) {
|
||||
super(true, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serialize() {
|
||||
return new byte[] { G1Constants.CommandId.GET_SERIAL_NUMBER.id };
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean responseMatches(byte[] payload) {
|
||||
return payload.length >= 16 && payload[0] == G1Constants.CommandId.GET_SERIAL_NUMBER.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "get_serial_number";
|
||||
}
|
||||
|
||||
public static int getFrameType(byte[] payload) {
|
||||
String serialNumber = getSerialNumber(payload);
|
||||
if (serialNumber.length() < 7) return -1;
|
||||
switch(serialNumber.substring(4, 7)) {
|
||||
case G1Constants.HardwareDescriptionKey.COLOR_GREY:
|
||||
return R.string.even_realities_frame_color_grey;
|
||||
case G1Constants.HardwareDescriptionKey.COLOR_BROWN:
|
||||
return R.string.even_realities_frame_color_brown;
|
||||
case G1Constants.HardwareDescriptionKey.COLOR_GREEN:
|
||||
return R.string.even_realities_frame_color_green;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public static int getFrameColor(byte[] payload) {
|
||||
String serialNumber = getSerialNumber(payload);
|
||||
if (serialNumber.length() < 4) return -1;
|
||||
switch(serialNumber.substring(0, 4)) {
|
||||
case G1Constants.HardwareDescriptionKey.FRAME_ROUND:
|
||||
return R.string.even_realities_frame_shape_G1A;
|
||||
case G1Constants.HardwareDescriptionKey.FRAME_SQUARE:
|
||||
return R.string.even_realities_frame_shape_G1B;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getSerialNumber(byte[] payload) {
|
||||
return new String(payload, 2, 16, StandardCharsets.US_ASCII);
|
||||
}
|
||||
}
|
||||
|
||||
public static class CommandSetDebugLogSettings extends CommandHandler {
|
||||
private final boolean enable;
|
||||
public CommandSetDebugLogSettings(boolean enable) {
|
||||
super(false, null);
|
||||
this.enable = enable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serialize() {
|
||||
return new byte[]{
|
||||
G1Constants.CommandId.SYSTEM.id,
|
||||
G1Constants.SystemSubCommand.SET_DEBUG_LOGGING.id,
|
||||
enable ? G1Constants.DebugLoggingStatus.ENABLE
|
||||
: G1Constants.DebugLoggingStatus.DISABLE
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean responseMatches(byte[] payload) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "set_debug_mode_settings_" + (enable ? "enabled" : "disabled");
|
||||
}
|
||||
}
|
||||
|
||||
public static class DebugLog {
|
||||
public static boolean messageMatches(byte[] payload) {
|
||||
return payload.length >= 1 && payload[0] == G1Constants.CommandId.DEBUG_LOG.id;
|
||||
}
|
||||
|
||||
public static String getMessage(byte[] payload) {
|
||||
return new String(payload, 1, payload.length-2, StandardCharsets.US_ASCII);
|
||||
}
|
||||
}
|
||||
|
||||
public static class DeviceEvent {
|
||||
public static boolean messageMatches(byte[] payload) {
|
||||
return payload.length >= 2 && payload[0] == G1Constants.CommandId.DEVICE_EVENT.id;
|
||||
}
|
||||
|
||||
public static byte getEventId(byte[] payload) {
|
||||
return payload[1];
|
||||
}
|
||||
|
||||
public static byte getValue(byte[] payload) {
|
||||
return payload[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@ public class G1Constants {
|
||||
public static final int DEFAULT_COMMAND_TIMEOUT_MS = 5000;
|
||||
public static final int DISPLAY_SETTINGS_PREVIEW_DELAY = 3000;
|
||||
public static final int DEFAULT_RETRY_COUNT = 5;
|
||||
public static final int CASE_BATTERY_INDEX = 2;
|
||||
public static final String INTENT_TOGGLE_SILENT_MODE = "nodomain.freeyourgadget.gadgetbridge.evenrealities.silent_mode";
|
||||
|
||||
// Extract the L or R at the end of the device prefix.
|
||||
@@ -73,6 +74,14 @@ public class G1Constants {
|
||||
}
|
||||
}
|
||||
|
||||
public static class HardwareDescriptionKey {
|
||||
public static final String FRAME_ROUND = "S100";
|
||||
public static final String FRAME_SQUARE = "S110";
|
||||
public static final String COLOR_GREY = "LAA";
|
||||
public static final String COLOR_BROWN = "LBB";
|
||||
public static final String COLOR_GREEN = "LCC";
|
||||
}
|
||||
|
||||
public static class CommandStatus {
|
||||
public static final byte FAILED = (byte)0xCA;
|
||||
public static final byte DATA_CONTINUES = (byte)0xCA;
|
||||
@@ -83,14 +92,16 @@ public class G1Constants {
|
||||
public enum CommandId {
|
||||
NOTIFICATION_CONFIG((byte) 0x04),
|
||||
DASHBOARD_CONFIG((byte) 0x06),
|
||||
DASHBOARD((byte) 0x22),
|
||||
FW_INFO_REQUEST((byte) 0x23),
|
||||
SYNC_SEQUENCE((byte) 0x22), // 0x05
|
||||
DASHBOARD_SHOWN((byte) 0x22), // 0x0A
|
||||
SYSTEM((byte) 0x23),
|
||||
HEARTBEAT((byte) 0x25),
|
||||
BATTERY_LEVEL((byte) 0x2C),
|
||||
INIT((byte) 0x4D),
|
||||
NOTIFICATION((byte) 0x4B),
|
||||
FW_INFO_RESPONSE((byte) 0x6E),
|
||||
DEVICE_ACTION((byte) 0xF5),
|
||||
DEBUG_LOG((byte) 0xF4),
|
||||
DEVICE_EVENT((byte) 0xF5),
|
||||
GET_SILENT_MODE_SETTINGS((byte) 0x2B), // There is more info in this one
|
||||
SET_SILENT_MODE_SETTINGS((byte) 0x03),
|
||||
GET_DISPLAY_SETTINGS((byte) 0x3B),
|
||||
@@ -100,7 +111,8 @@ public class G1Constants {
|
||||
GET_BRIGHTNESS_SETTINGS((byte) 0x29),
|
||||
SET_BRIGHTNESS_SETTINGS((byte) 0x01),
|
||||
GET_WEAR_DETECTION_SETTINGS((byte) 0x3A),
|
||||
SET_WEAR_DETECTION_SETTINGS((byte) 0x27);
|
||||
SET_WEAR_DETECTION_SETTINGS((byte) 0x27),
|
||||
GET_SERIAL_NUMBER((byte) 0x34);
|
||||
|
||||
final public byte id;
|
||||
|
||||
@@ -109,16 +121,31 @@ public class G1Constants {
|
||||
}
|
||||
}
|
||||
|
||||
public enum DashboardConfigSubCommand {
|
||||
SET_MODE((byte) 0x07),
|
||||
UNKNOWN_1((byte) 0x0C),
|
||||
SET_TIME_AND_WEATHER((byte) 0x15),
|
||||
// Not sure why they use this one sometimes.
|
||||
SET_TIME_AND_WEATHER_ALSO((byte) 0x16);
|
||||
public static class DashboardConfig {
|
||||
public static final byte SUB_COMMAND_SET_TIME_AND_WEATHER = 0x01;
|
||||
public static final byte SUB_COMMAND_SET_MODE = 0x06;
|
||||
|
||||
public static final byte MODE_FULL = 0x00;
|
||||
public static final byte MODE_DUAL = 0x01;
|
||||
public static final byte MODE_MINIMAl = 0x02;
|
||||
|
||||
public static final byte PANE_NOTES = 0x00;
|
||||
public static final byte PANE_STOCKS = 0x01;
|
||||
public static final byte PANE_NEWS = 0x02;
|
||||
public static final byte PANE_CALENDAR = 0x03;
|
||||
public static final byte PANE_NAVIGATION = 0x04;
|
||||
public static final byte PANE_EMPTY = 0x05;
|
||||
|
||||
}
|
||||
|
||||
public enum SystemSubCommand {
|
||||
RESET((byte) 0x72),
|
||||
GET_FW_INFO((byte) 0x74),
|
||||
SET_DEBUG_LOGGING((byte) 0x6C);
|
||||
|
||||
final public byte id;
|
||||
|
||||
DashboardConfigSubCommand(byte id) {
|
||||
SystemSubCommand(byte id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
@@ -127,4 +154,185 @@ public class G1Constants {
|
||||
public static final byte ENABLE = 0x0C;
|
||||
public static final byte DISABLE = 0x0A;
|
||||
}
|
||||
}
|
||||
|
||||
public static class DebugLoggingStatus {
|
||||
public static final byte ENABLE = 0x00;
|
||||
public static final byte DISABLE = (byte)0x31;
|
||||
}
|
||||
|
||||
public static class DeviceEventId {
|
||||
// Used to indicate a double tap, but it was used to close the dashboard.
|
||||
public static final byte DOUBLE_TAP_FOR_EXIT = 0x00;
|
||||
public static final byte UNKNOWN_1 = 0x01;
|
||||
public static final byte HEAD_UP = 0x02;
|
||||
public static final byte HEAD_DOWN = 0x03;
|
||||
public static final byte SILENT_MODE_ENABLED = 0x04;
|
||||
public static final byte SILENT_MODE_DISABLED = 0x05;
|
||||
public static final byte GLASSES_WORN = 0x06;
|
||||
public static final byte GLASSES_NOT_WORN_NO_CASE = 0x07;
|
||||
public static final byte CASE_LID_OPEN = 0x08;
|
||||
// Sent with a payload of 00 or 01 to indicate charging state.
|
||||
public static final byte GLASSES_CHARGING = 0x09;
|
||||
// Comes with a payload 00 - 64
|
||||
public static final byte GLASSES_SIDE_BATTERY_LEVEL = 0x0A;
|
||||
public static final byte CASE_LID_CLOSE = 0x0B;
|
||||
public static final byte UNKNOWN_4 = 0x0C;
|
||||
public static final byte UNKNOWN_5 = 0x0D;
|
||||
// Sent with a payload of 00 or 01 to indicate charging state.
|
||||
public static final byte CASE_CHARGING = 0x0E;
|
||||
// Comes with a payload 00 - 64
|
||||
public static final byte CASE_BATTERY_LEVEL = 0x0F;
|
||||
public static final byte UNKNOWN_6 = 0x10;
|
||||
public static final byte BINDING_SUCCESS = 0x11;
|
||||
public static final byte DASHBOARD_SHOW = 0x1E;
|
||||
public static final byte DASHBOARD_CLOSE = 0x1F;
|
||||
// Used to initiate translate or transcribe in the official app.
|
||||
// For us it's strictly a double tap that only sends the event.
|
||||
public static final byte DOUBLE_TAP_FOR_ACTION = 0x20;
|
||||
}
|
||||
|
||||
public static class TemperatureUnit {
|
||||
public static final byte CELSIUS = 0x00;
|
||||
public static final byte FAHRENHEIT = 0x01;
|
||||
}
|
||||
|
||||
public static class TimeFormat {
|
||||
public static final byte TWELVE_HOUR = 0x00;
|
||||
public static final byte TWENTY_FOUR_HOUR = 0x01;
|
||||
}
|
||||
|
||||
public static class WeatherId {
|
||||
public static final byte NONE = 0x00;
|
||||
public static final byte NIGHT = 0x01;
|
||||
public static final byte CLOUDS = 0x02;
|
||||
public static final byte DRIZZLE = 0x03;
|
||||
public static final byte HEAVY_DRIZZLE = 0x04;
|
||||
public static final byte RAIN = 0x05;
|
||||
public static final byte HEAVY_RAIN = 0x06;
|
||||
public static final byte THUNDER = 0x07;
|
||||
public static final byte THUNDERSTORM = 0x08;
|
||||
public static final byte SNOW = 0x09;
|
||||
public static final byte MIST = 0x0A;
|
||||
public static final byte FOG = 0x0B;
|
||||
public static final byte SAND = 0x0C;
|
||||
public static final byte SQUALLS = 0x0D;
|
||||
public static final byte TORNADO = 0x0E;
|
||||
public static final byte FREEZING_RAIN = 0x0F;
|
||||
public static final byte SUNNY = 0x10;
|
||||
}
|
||||
|
||||
public static byte fromOpenWeatherCondition(int openWeatherMapCondition) {
|
||||
// http://openweathermap.org/weather-conditions
|
||||
switch (openWeatherMapCondition) {
|
||||
//Group 2xx: Thunderstorm
|
||||
case 200: //thunderstorm with light rain:
|
||||
case 201: //thunderstorm with rain:
|
||||
case 202: //thunderstorm with heavy rain:
|
||||
case 210: //light thunderstorm::
|
||||
case 211: //thunderstorm:
|
||||
case 230: //thunderstorm with light drizzle:
|
||||
case 231: //thunderstorm with drizzle:
|
||||
case 232: //thunderstorm with heavy drizzle:
|
||||
case 212: //heavy thunderstorm:
|
||||
case 221: //ragged thunderstorm:
|
||||
return WeatherId.THUNDERSTORM;
|
||||
//Group 3xx: Drizzle
|
||||
case 300: //light intensity drizzle:
|
||||
case 301: //drizzle:
|
||||
case 310: //light intensity drizzle rain:
|
||||
return WeatherId.DRIZZLE;
|
||||
case 302: //heavy intensity drizzle:
|
||||
case 311: //drizzle rain:
|
||||
case 312: //heavy intensity drizzle rain:
|
||||
case 313: //shower rain and drizzle:
|
||||
case 314: //heavy shower rain and drizzle:
|
||||
case 321: //shower drizzle:
|
||||
return WeatherId.HEAVY_DRIZZLE;
|
||||
//Group 5xx: Rain
|
||||
case 500: //light rain:
|
||||
case 501: //moderate rain:
|
||||
return WeatherId.RAIN;
|
||||
case 502: //heavy intensity rain:
|
||||
case 503: //very heavy rain:
|
||||
case 504: //extreme rain:
|
||||
case 511: //freezing rain:
|
||||
case 520: //light intensity shower rain:
|
||||
case 521: //shower rain:
|
||||
case 522: //heavy intensity shower rain:
|
||||
case 531: //ragged shower rain:
|
||||
return WeatherId.HEAVY_RAIN;
|
||||
//Group 6xx: Snow
|
||||
case 600: //light snow:
|
||||
case 601: //snow:
|
||||
case 602: //heavy snow:
|
||||
return WeatherId.SNOW;
|
||||
case 611: //sleet:
|
||||
case 612: //shower sleet:
|
||||
case 615: //light rain and snow:
|
||||
case 616: //rain and snow:
|
||||
case 620: //light shower snow:
|
||||
case 621: //shower snow:
|
||||
case 622: //heavy shower snow:
|
||||
return WeatherId.FREEZING_RAIN;
|
||||
//Group 7xx: Atmosphere
|
||||
case 701: //mist:
|
||||
return WeatherId.MIST;
|
||||
case 711: //smoke:
|
||||
return WeatherId.FOG;
|
||||
case 721: //haze:
|
||||
return WeatherId.MIST;
|
||||
case 731: //sandcase dust whirls:
|
||||
return WeatherId.SAND;
|
||||
case 741: //fog:
|
||||
return WeatherId.FOG;
|
||||
case 751: //sand:
|
||||
case 761: //dust:
|
||||
case 762: //volcanic ash:
|
||||
return WeatherId.SAND;
|
||||
case 771: //squalls:
|
||||
return WeatherId.SQUALLS;
|
||||
case 781: //tornado:
|
||||
case 900: //tornado
|
||||
return WeatherId.TORNADO;
|
||||
//Group 800: Clear
|
||||
case 800: //clear sky:
|
||||
return WeatherId.SUNNY;
|
||||
//Group 80x: Clouds
|
||||
case 801: //few clouds:
|
||||
case 802: //scattered clouds:
|
||||
case 803: //broken clouds:
|
||||
case 804: //overcast clouds:
|
||||
return WeatherId.CLOUDS;
|
||||
//Group 90x: Extreme
|
||||
case 903: //cold
|
||||
return WeatherId.SNOW;
|
||||
case 904: //hot
|
||||
return WeatherId.SUNNY;
|
||||
case 905: //windy
|
||||
return WeatherId.NONE;
|
||||
case 906: //hail
|
||||
return WeatherId.THUNDERSTORM;
|
||||
//Group 9xx: Additional
|
||||
case 951: //calm
|
||||
return WeatherId.SUNNY;
|
||||
case 952: //light breeze
|
||||
case 953: //gentle breeze
|
||||
case 954: //moderate breeze
|
||||
case 955: //fresh breeze
|
||||
case 956: //strong breeze
|
||||
case 957: //high windcase near gale
|
||||
case 958: //gale
|
||||
return WeatherId.SQUALLS;
|
||||
case 901: //tropical storm
|
||||
case 959: //severe gale
|
||||
case 960: //storm
|
||||
case 961: //violent storm
|
||||
case 902: //hurricane
|
||||
case 962: //hurricane
|
||||
return WeatherId.TORNADO;
|
||||
default:
|
||||
return WeatherId.SUNNY;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -13,11 +13,13 @@ import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -27,13 +29,15 @@ import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.SettingsActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.jyou.BFH16Constants;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ItemWithDetails;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Weather;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEMultiDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEQueue;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
/**
|
||||
@@ -49,9 +53,10 @@ public class G1DeviceSupport extends AbstractBTLEMultiDeviceSupport {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(G1DeviceSupport.class);
|
||||
private final Handler backgroundTasksHandler = new Handler(Looper.getMainLooper());
|
||||
private BroadcastReceiver intentReceiver = null;
|
||||
private final Object sendTimeLock = new Object();
|
||||
private final Object lensSkewLock = new Object();
|
||||
private G1SideManager leftSide = null;
|
||||
private G1SideManager rightSide = null;
|
||||
|
||||
public G1DeviceSupport() {
|
||||
this(LOG);
|
||||
}
|
||||
@@ -60,17 +65,9 @@ public class G1DeviceSupport extends AbstractBTLEMultiDeviceSupport {
|
||||
super(logger, 2);
|
||||
addSupportedService(G1Constants.UUID_SERVICE_NORDIC_UART,
|
||||
G1Constants.Side.LEFT.getDeviceIndex());
|
||||
addSupportedService(BFH16Constants.BFH16_GENERIC_ACCESS_SERVICE,
|
||||
G1Constants.Side.LEFT.getDeviceIndex());
|
||||
addSupportedService(BFH16Constants.BFH16_GENERIC_ATTRIBUTE_SERVICE,
|
||||
G1Constants.Side.LEFT.getDeviceIndex());
|
||||
|
||||
addSupportedService(G1Constants.UUID_SERVICE_NORDIC_UART,
|
||||
G1Constants.Side.RIGHT.getDeviceIndex());
|
||||
addSupportedService(BFH16Constants.BFH16_GENERIC_ACCESS_SERVICE,
|
||||
G1Constants.Side.RIGHT.getDeviceIndex());
|
||||
addSupportedService(BFH16Constants.BFH16_GENERIC_ATTRIBUTE_SERVICE,
|
||||
G1Constants.Side.RIGHT.getDeviceIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,8 +76,7 @@ public class G1DeviceSupport extends AbstractBTLEMultiDeviceSupport {
|
||||
// Ignore any context sets from non-left devices.
|
||||
G1Constants.Side side = G1Constants.getSideFromFullName(device.getName());
|
||||
if (side == G1Constants.Side.LEFT) {
|
||||
ItemWithDetails right_name =
|
||||
device.getDeviceInfo(G1Constants.Side.RIGHT.getNameKey());
|
||||
ItemWithDetails right_name = device.getDeviceInfo(G1Constants.Side.RIGHT.getNameKey());
|
||||
ItemWithDetails right_address =
|
||||
device.getDeviceInfo(G1Constants.Side.RIGHT.getAddressKey());
|
||||
if (right_name != null && !right_name.getDetails().isEmpty() && right_address != null &&
|
||||
@@ -106,11 +102,9 @@ public class G1DeviceSupport extends AbstractBTLEMultiDeviceSupport {
|
||||
// Register to receive silent mode intent calls from the UI.
|
||||
if (intentReceiver == null) {
|
||||
intentReceiver = new IntentReceiver();
|
||||
ContextCompat.registerReceiver(
|
||||
context,
|
||||
intentReceiver,
|
||||
new IntentFilter(G1Constants.INTENT_TOGGLE_SILENT_MODE),
|
||||
ContextCompat.RECEIVER_NOT_EXPORTED);
|
||||
ContextCompat.registerReceiver(context, intentReceiver,
|
||||
new IntentFilter(G1Constants.INTENT_TOGGLE_SILENT_MODE),
|
||||
ContextCompat.RECEIVER_NOT_EXPORTED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,6 +115,7 @@ public class G1DeviceSupport extends AbstractBTLEMultiDeviceSupport {
|
||||
getCharacteristic(G1Constants.UUID_CHARACTERISTIC_NORDIC_UART_RX, deviceIdx);
|
||||
BluetoothGattCharacteristic tx =
|
||||
getCharacteristic(G1Constants.UUID_CHARACTERISTIC_NORDIC_UART_TX, deviceIdx);
|
||||
|
||||
if (rx == null || tx == null) {
|
||||
// If the characteristics are not received from the device reconnect and try again.
|
||||
LOG.warn("RX/TX characteristics are null, will attempt to reconnect");
|
||||
@@ -161,15 +156,22 @@ public class G1DeviceSupport extends AbstractBTLEMultiDeviceSupport {
|
||||
// IMPORTANT: use getDevice(deviceIdx), not getDevice(/* 0 */) here otherwise the device
|
||||
// will lock up in a half initialized state because GB thinks the left side is initialized,
|
||||
// after because the right ran first.
|
||||
if (side.getState() == GBDevice.State.CONNECTED) {
|
||||
if (side.getConnectingState() == GBDevice.State.CONNECTED) {
|
||||
builder.add(new SetDeviceStateAction(getDevice(deviceIdx), GBDevice.State.INITIALIZING,
|
||||
getContext()));
|
||||
side.initialize(builder);
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
if (leftSide != null && leftSide.getState() == GBDevice.State.INITIALIZED &&
|
||||
rightSide != null && rightSide.getState() == GBDevice.State.INITIALIZED) {
|
||||
if (leftSide != null && leftSide.getConnectingState() == GBDevice.State.INITIALIZED &&
|
||||
rightSide != null && rightSide.getConnectingState() == GBDevice.State.INITIALIZED) {
|
||||
// set device firmware to prevent the following error when data is saved to the
|
||||
// database and device firmware has not been set yet.
|
||||
// java.lang.IllegalArgumentException: the bind value at index 2 is null.
|
||||
// Must be called before the PostInitialize down below.
|
||||
getDevice().setFirmwareVersion("N/A");
|
||||
getDevice().setFirmwareVersion2("N/A");
|
||||
|
||||
// Both sides are initialized. The whole device is initialized, don't use a device
|
||||
// index here. Device 0 is the device that the reset of GB sees.
|
||||
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED,
|
||||
@@ -181,6 +183,7 @@ public class G1DeviceSupport extends AbstractBTLEMultiDeviceSupport {
|
||||
backgroundTasksHandler.postDelayed(() -> {
|
||||
leftSide.postInitializeLeft();
|
||||
rightSide.postInitializeRight();
|
||||
onSetDashboardMode();
|
||||
onSetTime();
|
||||
}, 200);
|
||||
}
|
||||
@@ -222,6 +225,7 @@ public class G1DeviceSupport extends AbstractBTLEMultiDeviceSupport {
|
||||
// passing in "this" because we don't want to forward ALL functionality of the device
|
||||
// support and we don't want a hard dependency on G1DeviceSupport in G1SideManager.
|
||||
Callable<BtLEQueue> getQueue = () -> this.getQueue(deviceIdx);
|
||||
Callable<GBDevice> getDevice = () -> this.getDevice(deviceIdx);
|
||||
Function<GBDeviceEvent, Void> handleEvent = (GBDeviceEvent event) -> {
|
||||
this.evaluateGBDeviceEvent(event);
|
||||
return null;
|
||||
@@ -229,13 +233,12 @@ public class G1DeviceSupport extends AbstractBTLEMultiDeviceSupport {
|
||||
|
||||
// Create the desired side.
|
||||
if (deviceIdx == G1Constants.Side.LEFT.getDeviceIndex()) {
|
||||
leftSide =
|
||||
new G1SideManager(G1Constants.Side.LEFT, backgroundTasksHandler, getQueue,
|
||||
handleEvent, this::getDevicePrefs, rx, tx);
|
||||
leftSide = new G1SideManager(G1Constants.Side.LEFT, backgroundTasksHandler, getQueue,
|
||||
getDevice, handleEvent, this::getDevicePrefs, rx, tx);
|
||||
return leftSide;
|
||||
} else if (deviceIdx == G1Constants.Side.RIGHT.getDeviceIndex()) {
|
||||
rightSide = new G1SideManager(G1Constants.Side.RIGHT, backgroundTasksHandler,
|
||||
getQueue, handleEvent, this::getDevicePrefs, rx, tx);
|
||||
rightSide = new G1SideManager(G1Constants.Side.RIGHT, backgroundTasksHandler, getQueue,
|
||||
getDevice, handleEvent, this::getDevicePrefs, rx, tx);
|
||||
return rightSide;
|
||||
}
|
||||
|
||||
@@ -285,8 +288,7 @@ public class G1DeviceSupport extends AbstractBTLEMultiDeviceSupport {
|
||||
if (characteristic.getUuid().equals(G1Constants.UUID_CHARACTERISTIC_NORDIC_UART_RX)) {
|
||||
String address = gatt.getDevice().getAddress();
|
||||
if (getDevice(G1Constants.Side.LEFT.getDeviceIndex()) != null) {
|
||||
String leftAddress =
|
||||
getDevice(G1Constants.Side.LEFT.getDeviceIndex()).getAddress();
|
||||
String leftAddress = getDevice(G1Constants.Side.LEFT.getDeviceIndex()).getAddress();
|
||||
if (address.equals(leftAddress) && leftSide != null) {
|
||||
return leftSide.handlePayload(characteristic.getValue());
|
||||
}
|
||||
@@ -317,22 +319,30 @@ public class G1DeviceSupport extends AbstractBTLEMultiDeviceSupport {
|
||||
switch (config) {
|
||||
case DeviceSettingsPreferenceConst.PREF_EVEN_REALITIES_SCREEN_ACTIVATION_ANGLE:
|
||||
// This setting is only sent to the right arm.
|
||||
if (rightSide != null) rightSide.onSendConfiguration(config);
|
||||
if (rightSide != null)
|
||||
rightSide.onSendConfiguration(config);
|
||||
break;
|
||||
case SettingsActivity.PREF_MEASUREMENT_SYSTEM:
|
||||
case DeviceSettingsPreferenceConst.PREF_TIMEFORMAT:
|
||||
// Units or time format updated, update the time and weather on the glasses to match
|
||||
onSetTimeOrWeather();
|
||||
break;
|
||||
default:
|
||||
// Forward to both sides.
|
||||
if (leftSide != null) leftSide.onSendConfiguration(config);
|
||||
if (rightSide != null) rightSide.onSendConfiguration(config);
|
||||
if (leftSide != null)
|
||||
leftSide.onSendConfiguration(config);
|
||||
if (rightSide != null)
|
||||
rightSide.onSendConfiguration(config);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetTime() {
|
||||
if (leftSide == null || rightSide == null) return;
|
||||
private void onSetTimeOrWeather() {
|
||||
if (leftSide == null || rightSide == null)
|
||||
return;
|
||||
|
||||
boolean use12HourFormat = getDevicePrefs().getTimeFormat().equals(
|
||||
DeviceSettingsPreferenceConst.PREF_TIMEFORMAT_12H);
|
||||
boolean use12HourFormat = getDevicePrefs().getTimeFormat()
|
||||
.equals(DeviceSettingsPreferenceConst.PREF_TIMEFORMAT_12H);
|
||||
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
|
||||
long currentMilliseconds = c.getTimeInMillis();
|
||||
long tzOffset = TimeZone.getDefault().getOffset(currentMilliseconds);
|
||||
@@ -340,18 +350,25 @@ public class G1DeviceSupport extends AbstractBTLEMultiDeviceSupport {
|
||||
|
||||
// Check if the GB settings are set to metric, if not, set the temp to use Fahrenheit.
|
||||
String metricString = GBApplication.getContext().getString(R.string.p_unit_metric);
|
||||
boolean useFahrenheit = !GBApplication.getPrefs().getString(SettingsActivity.PREF_MEASUREMENT_SYSTEM, metricString).equals(metricString);
|
||||
boolean useFahrenheit = !GBApplication.getPrefs()
|
||||
.getString(SettingsActivity.PREF_MEASUREMENT_SYSTEM,
|
||||
metricString).equals(metricString);
|
||||
|
||||
// Pull the weather into a local variable so that if it changes between the two lenses being
|
||||
// updated, we won't end up with a skewed value.
|
||||
@Nullable WeatherSpec weather = Weather.getInstance().getWeatherSpec();
|
||||
|
||||
// Run in the background in case the command hangs and this was run from the UI thread.
|
||||
backgroundTasksHandler.post(() -> {
|
||||
// This block is synchronized. We do not want two calls to overlap, otherwise the lenses
|
||||
// could get skewed with different values.
|
||||
synchronized (sendTimeLock) {
|
||||
synchronized (lensSkewLock) {
|
||||
// Send the left the time synchronously, then once a response is received, send the right.
|
||||
// The glasses will ignore the command on the right lens if it arrives before the left.
|
||||
G1Communications.CommandHandler leftCommandHandler =
|
||||
new G1Communications.CommandSetTimeAndWeather(timeMilliseconds,
|
||||
use12HourFormat, useFahrenheit);
|
||||
use12HourFormat, weather,
|
||||
useFahrenheit);
|
||||
leftSide.send(leftCommandHandler);
|
||||
if (!leftCommandHandler.waitForResponsePayload()) {
|
||||
LOG.error("Set time on left lens timed out");
|
||||
@@ -361,21 +378,72 @@ public class G1DeviceSupport extends AbstractBTLEMultiDeviceSupport {
|
||||
|
||||
rightSide.send(new G1Communications.CommandSetTimeAndWeather(timeMilliseconds,
|
||||
use12HourFormat,
|
||||
weather,
|
||||
useFahrenheit));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onToggleSilentMode() {
|
||||
if (leftSide == null || rightSide == null) return;
|
||||
if (leftSide == null || rightSide == null)
|
||||
return;
|
||||
|
||||
// If both lenses are in sync on what the status is, set them both. Otherwise, only set the
|
||||
// right one so they can be resynchronized.
|
||||
if(leftSide.getSilentModeStatus() == rightSide.getSilentModeStatus()){
|
||||
if (leftSide.getSilentModeStatus() == rightSide.getSilentModeStatus()) {
|
||||
leftSide.onToggleSilentMode();
|
||||
rightSide.onToggleSilentMode();
|
||||
} else {
|
||||
rightSide.onToggleSilentMode();
|
||||
}
|
||||
}
|
||||
|
||||
private void onSetDashboardMode() {
|
||||
// Run in the background in case the command hangs and this was run from the UI thread.
|
||||
backgroundTasksHandler.post(() -> {
|
||||
// This block is synchronized. We do not want two calls to overlap, otherwise the lenses
|
||||
// could get skewed with different values.
|
||||
synchronized (lensSkewLock) {
|
||||
// Send to the left synchronously, then once a response is received, send the right.
|
||||
// The glasses will ignore the command on the right lens if it arrives before the
|
||||
// left.
|
||||
// TODO: Pull these values from the settings and build a UI to configure it.
|
||||
G1Communications.CommandHandler leftCommandHandler =
|
||||
new G1Communications.CommandSetDashboardModeSettings(
|
||||
G1Constants.DashboardConfig.MODE_MINIMAl,
|
||||
G1Constants.DashboardConfig.PANE_EMPTY);
|
||||
|
||||
leftSide.send(leftCommandHandler);
|
||||
if (!leftCommandHandler.waitForResponsePayload()) {
|
||||
LOG.error("Set dashboard on right lens timed out");
|
||||
getDevice().setState(GBDevice.State.WAITING_FOR_RECONNECT);
|
||||
getDevice().sendDeviceUpdateIntent(getContext());
|
||||
}
|
||||
|
||||
rightSide.send(new G1Communications.CommandSetDashboardModeSettings(
|
||||
G1Constants.DashboardConfig.MODE_MINIMAl,
|
||||
G1Constants.DashboardConfig.PANE_EMPTY));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReset(int flags) {
|
||||
if (flags == GBDeviceProtocol.RESET_FLAGS_REBOOT) {
|
||||
leftSide.send(new G1Communications.CommandSendReset());
|
||||
rightSide.send(new G1Communications.CommandSendReset());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendWeather(ArrayList<WeatherSpec> weatherSpecs) {
|
||||
// onSetTimeAndWeather() fetches the weather directly from the global state, so no need to
|
||||
// pass in the weatherSpecs.
|
||||
onSetTimeOrWeather();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetTime() {
|
||||
onSetTimeOrWeather();
|
||||
}
|
||||
}
|
||||
|
@@ -14,10 +14,12 @@ import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.Function;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.Logging;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryIncrementalInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
|
||||
@@ -38,6 +40,7 @@ public class G1SideManager {
|
||||
private final G1Constants.Side mySide;
|
||||
private final Handler backgroundTasksHandler;
|
||||
private final Callable<BtLEQueue> getQueueHandler;
|
||||
private final Callable<GBDevice> getDeviceHandler;
|
||||
private final Function<GBDeviceEvent, Void> sendEventHandler;
|
||||
private final Callable<DevicePrefs> getPrefsHandler;
|
||||
private final BluetoothGattCharacteristic rx;
|
||||
@@ -48,15 +51,17 @@ public class G1SideManager {
|
||||
private final Set<G1Communications.CommandHandler> commandHandlers;
|
||||
private byte globalSequence;
|
||||
private boolean isSilentModeEnabled;
|
||||
private GBDevice.State state;
|
||||
private GBDevice.State connectingState;
|
||||
private boolean debugEnabled;
|
||||
|
||||
public G1SideManager(G1Constants.Side mySide, Handler backgroundTasksHandler,
|
||||
Callable<BtLEQueue> getQueue, Function<GBDeviceEvent, Void> sendEvent,
|
||||
Callable<DevicePrefs> getPrefs,
|
||||
Callable<BtLEQueue> getQueue, Callable<GBDevice> getDevice,
|
||||
Function<GBDeviceEvent, Void> sendEvent, Callable<DevicePrefs> getPrefs,
|
||||
BluetoothGattCharacteristic rx, BluetoothGattCharacteristic tx) {
|
||||
this.mySide = mySide;
|
||||
this.backgroundTasksHandler = backgroundTasksHandler;
|
||||
this.getQueueHandler = getQueue;
|
||||
this.getDeviceHandler = getDevice;
|
||||
this.sendEventHandler = sendEvent;
|
||||
this.getPrefsHandler = getPrefs;
|
||||
this.rx = rx;
|
||||
@@ -67,9 +72,14 @@ public class G1SideManager {
|
||||
};
|
||||
|
||||
this.heartBeatRunner = () -> {
|
||||
// We can send any command as a heart beat. The official app uses this one.
|
||||
send(new G1Communications.CommandGetSilentModeSettings(null));
|
||||
scheduleHeatBeat();
|
||||
if (getDevice().isConnected()) {
|
||||
// We can send any command as a heart beat. The official app uses this one.
|
||||
send(new G1Communications.CommandGetSilentModeSettings(null));
|
||||
scheduleHeatBeat();
|
||||
} else {
|
||||
// Don't reschedule if the device is disconnected.
|
||||
LOG.debug("Stopping heartbeat runner since side is in state: {}", getDevice().getState());
|
||||
}
|
||||
};
|
||||
this.displaySettingsPreviewCloserRunner = () -> {
|
||||
DevicePrefs prefs = getDevicePrefs();
|
||||
@@ -85,7 +95,8 @@ public class G1SideManager {
|
||||
// Non Finals
|
||||
this.globalSequence = 0;
|
||||
this.isSilentModeEnabled = false;
|
||||
this.state = GBDevice.State.CONNECTED;
|
||||
this.connectingState = GBDevice.State.CONNECTED;
|
||||
this.debugEnabled = false;
|
||||
}
|
||||
|
||||
private BtLEQueue getQueue() {
|
||||
@@ -96,6 +107,14 @@ public class G1SideManager {
|
||||
}
|
||||
}
|
||||
|
||||
private GBDevice getDevice() {
|
||||
try {
|
||||
return getDeviceHandler.call();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void evaluateGBDeviceEvent(GBDeviceEvent event) {
|
||||
sendEventHandler.apply(event);
|
||||
}
|
||||
@@ -108,11 +127,19 @@ public class G1SideManager {
|
||||
}
|
||||
}
|
||||
|
||||
public GBDevice.State getState() {
|
||||
return state;
|
||||
public GBDevice.State getConnectingState() {
|
||||
return connectingState;
|
||||
}
|
||||
|
||||
public void initialize(TransactionBuilder transaction) {
|
||||
// Disable device logging in the prefs. There is no way to query this state from the device
|
||||
// so instead, it is always disabled on connection, and then if a debug message arrives, the
|
||||
// setting will be flipped to true.
|
||||
this.debugEnabled = false;
|
||||
getDevicePrefs().getPreferences().edit()
|
||||
.putBoolean(DeviceSettingsPreferenceConst.PREF_DEVICE_LOGS_TOGGLE, this.debugEnabled)
|
||||
.apply();
|
||||
|
||||
// The glasses will auto disconnect after 30 seconds of no data on the wire.
|
||||
// Schedule a heartbeat task. If this is not enabled, the glasses will disconnect and be
|
||||
// useless to the user.
|
||||
@@ -121,7 +148,7 @@ public class G1SideManager {
|
||||
// Schedule the battery polling.
|
||||
scheduleBatteryPolling();
|
||||
|
||||
state = GBDevice.State.INITIALIZED;
|
||||
connectingState = GBDevice.State.INITIALIZED;
|
||||
}
|
||||
|
||||
public byte getSilentModeStatus() {
|
||||
@@ -142,6 +169,7 @@ public class G1SideManager {
|
||||
// These can be sent to both, but the left lens is used as the master for these settings.
|
||||
sendInTransaction(transaction, new G1Communications.CommandGetDisplaySettings(this::handleDisplaySettingsPayload));
|
||||
sendInTransaction(transaction, new G1Communications.CommandGetBrightnessSettings(this::handleBrightnessSettingsPayload));
|
||||
sendInTransaction(transaction, new G1Communications.CommandGetSerialNumber(this::handleSerialNumberPayload));
|
||||
transaction.queue(getQueue());
|
||||
}
|
||||
|
||||
@@ -184,15 +212,17 @@ public class G1SideManager {
|
||||
send(new G1Communications.CommandSetWearDetectionSettings(
|
||||
prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_WEAR_SENSOR_TOGGLE, true)));
|
||||
break;
|
||||
case DeviceSettingsPreferenceConst.PREF_DEVICE_LOGS_TOGGLE:
|
||||
this.debugEnabled = prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_DEVICE_LOGS_TOGGLE, false);
|
||||
send(new G1Communications.CommandSetDebugLogSettings(this.debugEnabled));
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void onToggleSilentMode() {
|
||||
isSilentModeEnabled = !isSilentModeEnabled;
|
||||
G1Communications.CommandHandler commandHandler =
|
||||
new G1Communications.CommandSetSilentModeSettings(isSilentModeEnabled);
|
||||
send(commandHandler);
|
||||
send(new G1Communications.CommandSetSilentModeSettings(isSilentModeEnabled));
|
||||
}
|
||||
|
||||
private void scheduleHeatBeat() {
|
||||
@@ -299,6 +329,22 @@ public class G1SideManager {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBatteryLevel(int level, int index) {
|
||||
evaluateGBDeviceEvent(new GBDeviceEventBatteryIncrementalInfo(index, level));
|
||||
}
|
||||
|
||||
private void updateBatteryLevel(int level) {
|
||||
updateBatteryLevel(level, mySide.getDeviceIndex());
|
||||
}
|
||||
|
||||
private void updateBatteryState(BatteryState state, int index) {
|
||||
evaluateGBDeviceEvent(new GBDeviceEventBatteryIncrementalInfo(index, state));
|
||||
}
|
||||
|
||||
private void updateBatteryState(BatteryState state) {
|
||||
updateBatteryState(state, mySide.getDeviceIndex());
|
||||
}
|
||||
|
||||
public boolean handlePayload(byte[] payload) {
|
||||
for (G1Communications.CommandHandler commandHandler : commandHandlers) {
|
||||
if (commandHandler.responseMatches(payload)) {
|
||||
@@ -315,14 +361,14 @@ public class G1SideManager {
|
||||
}
|
||||
}
|
||||
|
||||
// These can come in unprompted from the glasses, call the correct handler based on what the
|
||||
// command is.
|
||||
if (payload.length > 1) {
|
||||
if (payload[0] == G1Constants.CommandId.DEVICE_ACTION.id) {
|
||||
return handleDeviceActionPayload(payload);
|
||||
} else if (payload[0] == G1Constants.CommandId.DASHBOARD.id) {
|
||||
return handleDashboardPayload(payload);
|
||||
}
|
||||
// The glasses will send unprompted messages indicating certain events happening.
|
||||
// ex. glasses are taken off, glasses are charging, or touch pad was pressed.
|
||||
if (G1Communications.DeviceEvent.messageMatches(payload)) {
|
||||
return handleDeviceEventPayload(payload);
|
||||
}
|
||||
|
||||
if (G1Communications.DebugLog.messageMatches(payload)) {
|
||||
return handleDebugLogPayload(payload);
|
||||
}
|
||||
|
||||
LOG.debug("Unhandled payload on side {}: {}",
|
||||
@@ -333,11 +379,7 @@ public class G1SideManager {
|
||||
}
|
||||
|
||||
private boolean handleBatteryPayload(byte[] payload) {
|
||||
GBDeviceEventBatteryInfo batteryInfo = new GBDeviceEventBatteryInfo();
|
||||
batteryInfo.state = BatteryState.BATTERY_NORMAL;
|
||||
batteryInfo.level = payload[2];
|
||||
batteryInfo.batteryIndex = mySide.getDeviceIndex();
|
||||
evaluateGBDeviceEvent(batteryInfo);
|
||||
updateBatteryLevel(G1Communications.CommandGetBatteryInfo.getBatteryPercent(payload));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -349,15 +391,32 @@ public class G1SideManager {
|
||||
int versionEnd = fwString.indexOf(',', versionStart);
|
||||
if (versionStart > -1 && versionEnd > versionStart) {
|
||||
String version = fwString.substring(versionStart, versionEnd);
|
||||
LOG.debug("Parsed fw version: {}", version);
|
||||
GBDeviceEventVersionInfo fwInfo = new GBDeviceEventVersionInfo();
|
||||
if (mySide == G1Constants.Side.LEFT) {
|
||||
fwInfo.fwVersion = version;
|
||||
} else if (mySide == G1Constants.Side.RIGHT) {
|
||||
fwInfo.fwVersion2 = version;
|
||||
}
|
||||
// Actually get this some how?
|
||||
fwInfo.hwVersion = "G1A";
|
||||
fwInfo.hwVersion = null;
|
||||
fwInfo.fwVersion = mySide == G1Constants.Side.LEFT ? version : null;
|
||||
fwInfo.fwVersion2 = mySide == G1Constants.Side.RIGHT ? version : null;
|
||||
evaluateGBDeviceEvent(fwInfo);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handleSerialNumberPayload(byte[] payload) {
|
||||
String serialNumber = G1Communications.CommandGetSerialNumber.getSerialNumber(payload);
|
||||
|
||||
// Parse the hardware information out of the serial number.
|
||||
int shape = G1Communications.CommandGetSerialNumber.getFrameType(payload);
|
||||
int color = G1Communications.CommandGetSerialNumber.getFrameColor(payload);
|
||||
if (shape != -1 && color != -1) {
|
||||
GBDeviceEventVersionInfo fwInfo = new GBDeviceEventVersionInfo();
|
||||
fwInfo.hwVersion = GBApplication.getContext().getString(
|
||||
R.string.even_realities_frame_description,
|
||||
GBApplication.getContext().getString(color),
|
||||
GBApplication.getContext().getString(shape),
|
||||
GBApplication.getContext().getString(R.string.serial_number),
|
||||
serialNumber);
|
||||
fwInfo.fwVersion = null;
|
||||
fwInfo.fwVersion2 = null;
|
||||
evaluateGBDeviceEvent(fwInfo);
|
||||
return true;
|
||||
}
|
||||
@@ -405,13 +464,48 @@ public class G1SideManager {
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean handleDeviceActionPayload(byte[] payload) {
|
||||
LOG.debug("Device Action payload on side {}: {}", mySide.getDeviceIndex(), Logging.formatBytes(payload));
|
||||
private boolean handleDeviceEventPayload(byte[] payload) {
|
||||
switch (G1Communications.DeviceEvent.getEventId(payload)) {
|
||||
case G1Constants.DeviceEventId.GLASSES_CHARGING:
|
||||
updateBatteryState(
|
||||
G1Communications.DeviceEvent.getValue(payload) == 0x01
|
||||
? BatteryState.BATTERY_CHARGING
|
||||
: BatteryState.BATTERY_NORMAL);
|
||||
break;
|
||||
case G1Constants.DeviceEventId.GLASSES_SIDE_BATTERY_LEVEL:
|
||||
updateBatteryLevel(G1Communications.DeviceEvent.getValue(payload));
|
||||
break;
|
||||
case G1Constants.DeviceEventId.CASE_CHARGING:
|
||||
updateBatteryState(
|
||||
G1Communications.DeviceEvent.getValue(payload) == 0x01
|
||||
? BatteryState.BATTERY_CHARGING
|
||||
: BatteryState.BATTERY_NORMAL,
|
||||
G1Constants.CASE_BATTERY_INDEX);
|
||||
break;
|
||||
case G1Constants.DeviceEventId.CASE_BATTERY_LEVEL:
|
||||
updateBatteryLevel(G1Communications.DeviceEvent.getValue(payload),
|
||||
G1Constants.CASE_BATTERY_INDEX);
|
||||
break;
|
||||
case G1Constants.DeviceEventId.GLASSES_NOT_WORN_NO_CASE:
|
||||
updateBatteryState(BatteryState.NO_BATTERY, G1Constants.CASE_BATTERY_INDEX);
|
||||
break;
|
||||
default:
|
||||
LOG.debug("Device Event on side {}: {}", mySide.getDeviceIndex(),
|
||||
Logging.formatBytes(payload));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean handleDashboardPayload(byte[] payload) {
|
||||
LOG.debug("Dashboard payload on side {}: {}", mySide.getDeviceIndex(), Logging.formatBytes(payload));
|
||||
private boolean handleDebugLogPayload(byte[] payload) {
|
||||
// Use the local boolean so that we aren't constantly committing the same value to the prefs
|
||||
if (!this.debugEnabled) {
|
||||
this.debugEnabled = true;
|
||||
// Mark the pref as enabled so that the Setting UI reflects the true state.
|
||||
getDevicePrefs().getPreferences().edit().putBoolean(
|
||||
DeviceSettingsPreferenceConst.PREF_DEVICE_LOGS_TOGGLE, this.debugEnabled).apply();
|
||||
}
|
||||
LOG.info("{}: {}", mySide, G1Communications.DebugLog.getMessage(payload));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
40
app/src/main/res/drawable/ic_even_realities_g1_case.xml
Normal file
40
app/src/main/res/drawable/ic_even_realities_g1_case.xml
Normal file
@@ -0,0 +1,40 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<!-- Back fill -->
|
||||
<path
|
||||
android:pathData="M2,11h20"
|
||||
android:strokeWidth="12"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeAlpha=".3" />
|
||||
|
||||
<!-- Side Lines -->
|
||||
<path
|
||||
android:strokeLineCap="round"
|
||||
android:strokeWidth="2"
|
||||
android:pathData="
|
||||
M2,6v12
|
||||
M22,6v12"
|
||||
android:strokeColor="#000000"/>
|
||||
|
||||
<!-- Horizontal Lines -->
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="
|
||||
M2,17 L22,17 L22,19 L2,19 Z
|
||||
M2,13.5 L22,13.5 L22,14.5 L2,14.5 Z
|
||||
M2,5 L22,5 L22,7 L2,7 Z
|
||||
"
|
||||
android:fillColor="#000000"/>
|
||||
|
||||
<!-- Center LED -->
|
||||
<path
|
||||
android:strokeLineCap="round"
|
||||
android:strokeWidth=".5"
|
||||
android:pathData="M11.5,15.5 h1"
|
||||
android:strokeColor="#000000"/>
|
||||
</vector>
|
@@ -0,0 +1,48 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<!-- Back fill -->
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:fillAlpha=".3"
|
||||
android:pathData="
|
||||
M2,5h20v13H2V6z
|
||||
M11,18v-5.5H9L13,5v5.5h2L11,18z"
|
||||
android:fillColor="#000000" />
|
||||
|
||||
<!-- Side Lines -->
|
||||
<path
|
||||
android:strokeLineCap="round"
|
||||
android:strokeWidth="2"
|
||||
android:pathData="
|
||||
M2,6v12
|
||||
M22,6v12"
|
||||
android:strokeColor="#000000"/>
|
||||
|
||||
<!-- Horizontal Lines -->
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="
|
||||
M2,17 L22,17 L22,19 L2,19 Z
|
||||
M13,5 L13,7 L11.935,7 Z
|
||||
|
||||
M2,13.5 L22,13.5 L22,14.5 L2,14.5 Z
|
||||
M11,13.5 L13.4,13.5 L12.86,14.5 L11,14.5 Z
|
||||
|
||||
M2,5 L22,5 L22,7 L2,7 Z
|
||||
M11,18 L11,17 L11.533,17 Z
|
||||
"
|
||||
android:fillColor="#000000"/>
|
||||
|
||||
<!-- Center LED -->
|
||||
<path
|
||||
android:pathData="
|
||||
M 12.5 15.75 A 0.25 0.25 0 0 0 12.5 15.25 Z
|
||||
M12.465,15.25 L12.2,15.75 L12.5,15.75 L12.5,15.25 Z"
|
||||
android:fillColor="#000000"/>
|
||||
|
||||
</vector>
|
@@ -0,0 +1,33 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:alpha=".3"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<!-- Side Lines -->
|
||||
<path
|
||||
android:strokeLineCap="round"
|
||||
android:strokeWidth="2"
|
||||
android:pathData="
|
||||
M2,6v12
|
||||
M22,6v12"
|
||||
android:strokeColor="#000000"/>
|
||||
|
||||
<!-- Horizontal Lines -->
|
||||
<path
|
||||
android:pathData="
|
||||
M2,17 L22,17 L22,19 L2,19 Z
|
||||
M2,13.5 L22,13.5 L22,14.5 L2,14.5 Z
|
||||
M2,5 L22,5 L22,7 L2,7 Z
|
||||
"
|
||||
android:fillColor="#000000"/>
|
||||
|
||||
<!-- Center LED -->
|
||||
<path
|
||||
android:strokeLineCap="round"
|
||||
android:strokeWidth=".5"
|
||||
android:pathData="M11.5,15.5 h1"
|
||||
android:strokeColor="#000000"/>
|
||||
</vector>
|
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:drawable="@drawable/ic_even_realities_g1_case"
|
||||
android:maxLevel="10" />
|
||||
<item
|
||||
android:drawable="@drawable/ic_even_realities_g1_case"
|
||||
android:maxLevel="30" />
|
||||
<item
|
||||
android:drawable="@drawable/ic_even_realities_g1_case"
|
||||
android:maxLevel="60" />
|
||||
<item
|
||||
android:drawable="@drawable/ic_even_realities_g1_case"
|
||||
android:maxLevel="80" />
|
||||
<item
|
||||
android:drawable="@drawable/ic_even_realities_g1_case"
|
||||
android:maxLevel="100" />
|
||||
<item
|
||||
android:drawable="@drawable/ic_even_realities_g1_case_charging"
|
||||
android:maxLevel="110" />
|
||||
<item
|
||||
android:drawable="@drawable/ic_even_realities_g1_case_charging"
|
||||
android:maxLevel="130" />
|
||||
<item
|
||||
android:drawable="@drawable/ic_even_realities_g1_case_charging"
|
||||
android:maxLevel="160" />
|
||||
<item
|
||||
android:drawable="@drawable/ic_even_realities_g1_case_charging"
|
||||
android:maxLevel="180" />
|
||||
<item
|
||||
android:drawable="@drawable/ic_even_realities_g1_case_charging"
|
||||
android:maxLevel="200" />
|
||||
<item
|
||||
android:drawable="@drawable/ic_even_realities_g1_case_unknown"
|
||||
android:maxLevel="300" />
|
||||
</level-list>
|
@@ -3330,6 +3330,8 @@
|
||||
<string name="pref_app_logs_summary">Enable logs from watch apps</string>
|
||||
<string name="pref_app_logs_start_summary">Start logging from watch apps</string>
|
||||
<string name="pref_app_logs_stop_summary">Stop logging from watch apps</string>
|
||||
<string name="pref_device_logs_summary">Enable logging from the device</string>
|
||||
<string name="pref_device_logs_title">Device Logs</string>
|
||||
<string name="pref_app_connection_duration">App connection duration</string>
|
||||
<string name="title">Title</string>
|
||||
<string name="description">Description</string>
|
||||
@@ -3968,4 +3970,13 @@
|
||||
<string name="pref_even_realities_g1_screen_activation_angle_summary">Changes how high the wearer needs to lift their head for the dashboard to be shown</string>
|
||||
<string name="pref_even_realities_g1_screen_calibrate_angle_title">Calibrate Angle</string>
|
||||
<string name="pref_even_realities_g1_screen_calibrate_angle_summary">Zeroes the angle of the glasses, look straight ahead and tap here</string>
|
||||
<string name="even_realities_frame_shape_G1A">G1A/Round</string>
|
||||
<string name="even_realities_frame_shape_G1B">G1B/Square</string>
|
||||
<string name="even_realities_frame_color_grey">Grey</string>
|
||||
<string name="even_realities_frame_color_brown">Brown</string>
|
||||
<string name="even_realities_frame_color_green">Green</string>
|
||||
<string name="even_realities_frame_description">%1s %2s (%3s: %4s)</string>
|
||||
<string name="even_realities_left_lens">Left Lens</string>
|
||||
<string name="even_realities_right_lens">Right Lens</string>
|
||||
|
||||
</resources>
|
||||
|
10
app/src/main/res/xml/devicesettings_debug_logs_toggle.xml
Normal file
10
app/src/main/res/xml/devicesettings_debug_logs_toggle.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:icon="@drawable/ic_developer_mode"
|
||||
android:key="device_logs_enabled"
|
||||
android:layout="@layout/preference_checkbox"
|
||||
android:summary="@string/pref_device_logs_summary"
|
||||
android:title="@string/pref_device_logs_title" />
|
||||
</androidx.preference.PreferenceScreen>
|
Reference in New Issue
Block a user