mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2025-01-10 17:11:56 +01:00
Merge branch 'master' of codeberg.org:Freeyourgadget/Gadgetbridge into multi-device-support
This commit is contained in:
commit
dc7bdb3e3d
@ -124,6 +124,10 @@
|
||||
android:name=".activities.CalBlacklistActivity"
|
||||
android:label="@string/title_activity_calblacklist"
|
||||
android:parentActivityName=".activities.SettingsActivity" />
|
||||
<activity
|
||||
android:name=".devices.vesc.VescControlActivity"
|
||||
android:label="@string/devicetype_vesc"
|
||||
android:parentActivityName=".activities.ControlCenterv2" />
|
||||
<activity
|
||||
android:name=".activities.FwAppInstallerActivity"
|
||||
android:label="@string/title_activity_fw_app_insaller"
|
||||
|
@ -133,6 +133,8 @@ public class DeviceSettingsPreferenceConst {
|
||||
public static final String PREF_SONY_AUTOMATIC_POWER_OFF = "pref_sony_automatic_power_off";
|
||||
public static final String PREF_SONY_NOTIFICATION_VOICE_GUIDE = "pref_sony_notification_voice_guide";
|
||||
|
||||
public static final String PREF_QC35_NOISE_CANCELLING_LEVEL = "qc35_noise_cancelling_level";
|
||||
|
||||
public static final String PREFS_ACTIVITY_IN_DEVICE_CARD = "prefs_activity_in_device_card";
|
||||
public static final String PREFS_ACTIVITY_IN_DEVICE_CARD_STEPS = "prefs_activity_in_device_card_steps";
|
||||
public static final String PREFS_ACTIVITY_IN_DEVICE_CARD_SLEEP = "prefs_activity_in_device_card_sleep";
|
||||
|
@ -152,6 +152,7 @@ import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.Dev
|
||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREFS_ACTIVITY_IN_DEVICE_CARD_SLEEP;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREFS_ACTIVITY_IN_DEVICE_CARD_STEPS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREFS_DEVICE_CHARTS_TABS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_QC35_NOISE_CANCELLING_LEVEL;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_ACTIVATE_DISPLAY_ON_LIFT;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_FELL_SLEEP_BROADCAST;
|
||||
@ -569,6 +570,8 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
|
||||
addPreferenceHandlerFor(PREF_SONY_AUTOMATIC_POWER_OFF);
|
||||
addPreferenceHandlerFor(PREF_SONY_NOTIFICATION_VOICE_GUIDE);
|
||||
|
||||
addPreferenceHandlerFor(PREF_QC35_NOISE_CANCELLING_LEVEL);
|
||||
|
||||
String sleepTimeState = prefs.getString(PREF_SLEEP_TIME, PREF_DO_NOT_DISTURB_OFF);
|
||||
boolean sleepTimeScheduled = sleepTimeState.equals(PREF_DO_NOT_DISTURB_SCHEDULED);
|
||||
|
||||
|
@ -93,6 +93,9 @@ public class HuamiActivitySummaryParser implements ActivitySummaryParser {
|
||||
float caloriesBurnt;
|
||||
float distanceMeters;
|
||||
float distanceMeters2 = 0;
|
||||
float ascentDistance = 0;
|
||||
float descentDistance = 0;
|
||||
float flatDistance = 0;
|
||||
float ascentMeters = 0;
|
||||
float descentMeters = 0;
|
||||
float maxAltitude = 0;
|
||||
@ -183,11 +186,11 @@ public class HuamiActivitySummaryParser implements ActivitySummaryParser {
|
||||
// for cycling it seems to work... hmm...
|
||||
// 28 bytes
|
||||
buffer.getInt(); // unknown
|
||||
buffer.getInt(); // unknown
|
||||
ascentDistance = buffer.getFloat();
|
||||
ascentSeconds = buffer.getInt() / 1000; //ms?
|
||||
buffer.getInt(); // unknown;
|
||||
descentDistance = buffer.getFloat();
|
||||
descentSeconds = buffer.getInt() / 1000; //ms?
|
||||
buffer.getInt(); // unknown;
|
||||
flatDistance = buffer.getFloat();
|
||||
flatSeconds = buffer.getInt() / 1000; // ms?
|
||||
} else if (activityKind == ActivityKind.TYPE_SWIMMING || activityKind == ActivityKind.TYPE_SWIMMING_OPENWATER) {
|
||||
// offset 0x8c
|
||||
@ -253,11 +256,11 @@ public class HuamiActivitySummaryParser implements ActivitySummaryParser {
|
||||
} else {
|
||||
// 28 bytes
|
||||
buffer.getInt(); // unknown
|
||||
buffer.getInt(); // unknown
|
||||
buffer.getInt(); // unknown probably ascentDistance = buffer.getFloat();
|
||||
ascentSeconds = buffer.getInt() / 1000; //ms?
|
||||
buffer.getInt(); // unknown;
|
||||
buffer.getInt(); // unknown probably descentDistance = buffer.getFloat();
|
||||
descentSeconds = buffer.getInt() / 1000; //ms?
|
||||
buffer.getInt(); // unknown;
|
||||
buffer.getInt(); // unknown probably flatDistance = buffer.getFloat();
|
||||
flatSeconds = buffer.getInt() / 1000; // ms?
|
||||
|
||||
addSummaryData("ascentSeconds", ascentSeconds, "seconds");
|
||||
@ -298,6 +301,9 @@ public class HuamiActivitySummaryParser implements ActivitySummaryParser {
|
||||
addSummaryData("ascentSeconds", ascentSeconds, "seconds");
|
||||
addSummaryData("descentSeconds", descentSeconds, "seconds");
|
||||
addSummaryData("flatSeconds", flatSeconds, "seconds");
|
||||
addSummaryData("ascentDistance", ascentDistance, "meters");
|
||||
addSummaryData("descentDistance", descentDistance, "meters");
|
||||
addSummaryData("flatDistance", flatDistance, "meters");
|
||||
|
||||
addSummaryData("distanceMeters", distanceMeters, "meters");
|
||||
// addSummaryData("distanceMeters2", distanceMeters2, "meters");
|
||||
|
@ -0,0 +1,145 @@
|
||||
/* Copyright (C) 2021 Daniel Dakhno
|
||||
|
||||
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 <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.qc35;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
|
||||
public class QC35Coordinator extends AbstractDeviceCoordinator {
|
||||
@Override
|
||||
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
|
||||
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
|
||||
if (candidate.getName().startsWith("Bose QC 35")) {
|
||||
return DeviceType.BOSE_QC35;
|
||||
}
|
||||
return DeviceType.UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceType getDeviceType() {
|
||||
return DeviceType.BOSE_QC35;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Class<? extends Activity> getPairingActivity() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getSupportedDeviceSpecificSettings(GBDevice device) {
|
||||
return new int[]{
|
||||
R.xml.devicesettings_qc35
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsActivityDataFetching() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsActivityTracking() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstallHandler findInstallHandler(Uri uri, Context context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsScreenshots() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAlarmSlotCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsSmartWakeup(GBDevice device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsHeartRateMeasurement(GBDevice device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getManufacturer() {
|
||||
return "Bose";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAppsManagement() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Activity> getAppsManagementActivity() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCalendarEvents() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRealtimeData() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsWeather() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFindDevice() {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,247 @@
|
||||
/* Copyright (C) 2021 Daniel Dakhno
|
||||
|
||||
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 <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.vesc;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.EditText;
|
||||
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.vesc.VescDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
|
||||
public class VescControlActivity extends AbstractGBActivity {
|
||||
private static final String TAG = "VescControlActivity";
|
||||
private boolean volumeKeyPressed = false;
|
||||
private boolean volumeKeysControl = false;
|
||||
private int currentRPM = 0;
|
||||
private int currentBreakCurrentMa = 0;
|
||||
LocalBroadcastManager localBroadcastManager;
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
EditText rpmEditText, breakCurrentEditText;
|
||||
|
||||
private final int DELAY_SAVE = 1000;
|
||||
|
||||
Prefs preferences;
|
||||
|
||||
final String PREFS_KEY_LAST_RPM = "VESC_LAST_RPM";
|
||||
final String PREFS_KEY_LAST_BREAK_CURRENT = "VESC_LAST_BREAK_CURRENT";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_vesc_control);
|
||||
|
||||
localBroadcastManager = LocalBroadcastManager.getInstance(this);
|
||||
|
||||
preferences = GBApplication.getPrefs();
|
||||
|
||||
initViews();
|
||||
|
||||
restoreValues();
|
||||
}
|
||||
|
||||
private void restoreValues(){
|
||||
rpmEditText.setText(preferences.getInt(PREFS_KEY_LAST_RPM, 0));
|
||||
breakCurrentEditText.setText(preferences.getInt(PREFS_KEY_LAST_BREAK_CURRENT, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
setCurrent(0);
|
||||
}
|
||||
|
||||
private boolean handleKeyPress(int keyCode, boolean isPressed) {
|
||||
if (!volumeKeysControl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (keyCode != 24 && keyCode != 25) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (volumeKeyPressed == isPressed) {
|
||||
return true;
|
||||
}
|
||||
volumeKeyPressed = isPressed;
|
||||
|
||||
logger.debug("volume " + (keyCode == 25 ? "down" : "up") + (isPressed ? " pressed" : " released"));
|
||||
if (!isPressed) {
|
||||
setCurrent(0);
|
||||
return true;
|
||||
}
|
||||
if (keyCode == 24) {
|
||||
setRPM(currentRPM);
|
||||
} else {
|
||||
setBreakCurrent(VescControlActivity.this.currentBreakCurrentMa);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Runnable rpmSaveRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
preferences.getPreferences().edit().putInt(PREFS_KEY_LAST_RPM, currentRPM).apply();
|
||||
}
|
||||
};
|
||||
|
||||
Runnable breakCurrentSaveRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
preferences.getPreferences().edit().putInt(PREFS_KEY_LAST_BREAK_CURRENT, currentBreakCurrentMa).apply();
|
||||
}
|
||||
};
|
||||
|
||||
private void initViews() {
|
||||
((CheckBox) findViewById(R.id.vesc_control_checkbox_volume_keys))
|
||||
.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
VescControlActivity.this.volumeKeysControl = isChecked;
|
||||
if (!isChecked) {
|
||||
setRPM(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
rpmEditText = ((EditText) findViewById(R.id.vesc_control_input_rpm));
|
||||
rpmEditText.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
rpmEditText.removeCallbacks(rpmSaveRunnable);
|
||||
rpmEditText.postDelayed(rpmSaveRunnable, DELAY_SAVE);
|
||||
|
||||
String text = s.toString();
|
||||
if (text.isEmpty()) {
|
||||
currentRPM = 0;
|
||||
return;
|
||||
}
|
||||
VescControlActivity.this.currentRPM = Integer.parseInt(text);
|
||||
}
|
||||
});
|
||||
|
||||
breakCurrentEditText = ((EditText) findViewById(R.id.vesc_control_input_break_current));
|
||||
breakCurrentEditText.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
breakCurrentEditText.removeCallbacks(breakCurrentSaveRunnable);
|
||||
breakCurrentEditText.postDelayed(breakCurrentSaveRunnable, DELAY_SAVE);
|
||||
|
||||
String text = s.toString();
|
||||
if (text.isEmpty()) {
|
||||
currentBreakCurrentMa = 0;
|
||||
return;
|
||||
}
|
||||
VescControlActivity.this.currentBreakCurrentMa = Integer.parseInt(text) * 1000;
|
||||
}
|
||||
});
|
||||
|
||||
View.OnTouchListener controlTouchListener = new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
if (v.getId() == R.id.vesc_control_button_fwd) {
|
||||
setRPM(VescControlActivity.this.currentRPM);
|
||||
} else {
|
||||
setBreakCurrent(VescControlActivity.this.currentBreakCurrentMa);
|
||||
}
|
||||
} else if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
setCurrent(0);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
findViewById(R.id.vesc_control_button_fwd).setOnTouchListener(controlTouchListener);
|
||||
findViewById(R.id.vesc_control_button_break).setOnTouchListener(controlTouchListener);
|
||||
}
|
||||
|
||||
private void setBreakCurrent(int breakCurrentMa) {
|
||||
logger.debug("setting break current to {}", breakCurrentMa);
|
||||
Intent intent = new Intent(VescDeviceSupport.COMMAND_SET_BREAK_CURRENT);
|
||||
intent.putExtra(VescDeviceSupport.EXTRA_CURRENT, breakCurrentMa);
|
||||
sendLocalBroadcast(intent);
|
||||
}
|
||||
|
||||
private void setCurrent(int currentMa) {
|
||||
logger.debug("setting current to {}", currentMa);
|
||||
Intent intent = new Intent(VescDeviceSupport.COMMAND_SET_CURRENT);
|
||||
intent.putExtra(VescDeviceSupport.EXTRA_CURRENT, currentMa);
|
||||
sendLocalBroadcast(intent);
|
||||
}
|
||||
|
||||
private void setRPM(int rpm) {
|
||||
logger.debug("setting rpm to {}", rpm);
|
||||
Intent intent = new Intent(VescDeviceSupport.COMMAND_SET_RPM);
|
||||
intent.putExtra(VescDeviceSupport.EXTRA_RPM, rpm);
|
||||
sendLocalBroadcast(intent);
|
||||
}
|
||||
|
||||
private void sendLocalBroadcast(Intent intent) {
|
||||
localBroadcastManager.sendBroadcast(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
return handleKeyPress(keyCode, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
return handleKeyPress(keyCode, true);
|
||||
}
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
/* Copyright (C) 2021 Daniel Dakhno
|
||||
|
||||
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 <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.vesc;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelUuid;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
|
||||
public class VescCoordinator extends AbstractDeviceCoordinator {
|
||||
public final static String UUID_SERVICE_SERIAL_HM10 = "0000ffe0-0000-1000-8000-00805f9b34fb";
|
||||
public final static String UUID_CHARACTERISTIC_SERIAL_TX_HM10 = "0000ffe1-0000-1000-8000-00805f9b34fb";
|
||||
|
||||
public final static String UUID_SERVICE_SERIAL_NRF = "0000ffe0-0000-1000-8000-00805f9b34fb";
|
||||
public final static String UUID_CHARACTERISTIC_SERIAL_TX_NRF = "0000ffe0-0000-1000-8000-00805f9b34fb";
|
||||
|
||||
|
||||
@Override
|
||||
protected void deleteDevice(GBDevice gbDevice, Device device, DaoSession session) throws GBException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
|
||||
ParcelUuid[] uuids = candidate.getServiceUuids();
|
||||
Logger logger = LoggerFactory.getLogger(getClass());
|
||||
for(ParcelUuid uuid: uuids){
|
||||
logger.debug("service: {}", uuid.toString());
|
||||
}
|
||||
for(ParcelUuid uuid : uuids){
|
||||
if(uuid.getUuid().toString().equals(UUID_SERVICE_SERIAL_NRF)){
|
||||
return DeviceType.VESC_NRF;
|
||||
}else if(uuid.getUuid().toString().equals(UUID_SERVICE_SERIAL_HM10)){
|
||||
return DeviceType.VESC_HM10;
|
||||
}
|
||||
}
|
||||
return DeviceType.UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceType getDeviceType() {
|
||||
return DeviceType.VESC_HM10; // TODO: this limits this coordinator to NRF serial service
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Activity> getPairingActivity() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsActivityDataFetching() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBondingStyle() {
|
||||
return BONDING_STYLE_NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsActivityTracking() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstallHandler findInstallHandler(Uri uri, Context context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsScreenshots() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAlarmSlotCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsSmartWakeup(GBDevice device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsHeartRateMeasurement(GBDevice device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getManufacturer() {
|
||||
return "Benjamin Vedder";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAppsManagement() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Activity> getAppsManagementActivity() {
|
||||
return VescControlActivity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCalendarEvents() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRealtimeData() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsWeather() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFindDevice() {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -145,7 +145,7 @@ public class ActivitySummaryJsonSummary {
|
||||
private JSONObject createActivitySummaryGroups(){
|
||||
String groupDefinitions = "{'Strokes':['averageStrokeDistance','averageStrokesPerSecond','strokes'], " +
|
||||
"'Swimming':['swolfIndex','swimStyle'], " +
|
||||
"'Elevation':['ascentMeters','descentMeters','maxAltitude','minAltitude','averageAltitude','ascentSeconds','descentSeconds','flatSeconds', 'baseAltitude'], " +
|
||||
"'Elevation':['ascentMeters','descentMeters','maxAltitude','minAltitude','averageAltitude', 'baseAltitude','ascentSeconds','descentSeconds','flatSeconds','ascentDistance','descentDistance','flatDistance'], " +
|
||||
"'Speed':['averageSpeed','maxSpeed','minSpeed','averageKMPaceSeconds','minPace','maxPace','averageSpeed2','averageCadence','maxCadence','minCadence'], " +
|
||||
"'Activity':['distanceMeters','steps','activeSeconds','caloriesBurnt','totalStride'," +
|
||||
"'averageHR','maxHR','minHR','averageStride','maxStride','minStride'], " +
|
||||
|
@ -105,6 +105,9 @@ public enum DeviceType {
|
||||
GALAXY_BUDS(420, R.drawable.ic_device_galaxy_buds, R.drawable.ic_device_galaxy_buds_disabled, R.string.devicetype_galaxybuds),
|
||||
SONY_WH_1000XM3(430, R.drawable.ic_device_headphones, R.drawable.ic_device_headphones_disabled, R.string.devicetype_sony_wh_1000xm3),
|
||||
SONY_WF_SP800N(431, R.drawable.ic_device_galaxy_buds, R.drawable.ic_device_galaxy_buds_disabled, R.string.devicetype_sony_wf_sp800n),
|
||||
BOSE_QC35(440, R.drawable.ic_device_headphones, R.drawable.ic_device_headphones_disabled, R.string.devicetype_bose_qc35),
|
||||
VESC_NRF(500, R.drawable.ic_devices_other, R.drawable.ic_devices_other, R.string.devicetype_vesc),
|
||||
VESC_HM10(501, R.drawable.ic_devices_other, R.drawable.ic_devices_other, R.string.devicetype_vesc),
|
||||
TEST(1000, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_test);
|
||||
|
||||
private final int key;
|
||||
|
@ -85,6 +85,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.nothing.Ear1Support;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.nut.NutSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.pinetime.PineTimeJFSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qc35.QC35BaseSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.roidmi.RoidmiSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.smaq2oss.SMAQ2OSSSupport;
|
||||
@ -92,6 +93,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.Sony
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.SonySWR12DeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.tlw64.TLW64Support;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.um25.Support.UM25Support;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.vesc.VescDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.vibratissimo.VibratissimoSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.waspos.WaspOSDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.watch9.Watch9DeviceSupport;
|
||||
@ -376,6 +378,13 @@ public class DeviceSupportFactory {
|
||||
case SONY_WF_SP800N:
|
||||
deviceSupport = new ServiceDeviceSupport(new SonyHeadphonesSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
|
||||
break;
|
||||
case VESC_NRF:
|
||||
case VESC_HM10:
|
||||
deviceSupport = new ServiceDeviceSupport(new VescDeviceSupport(gbDevice.getType()), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
|
||||
break;
|
||||
case BOSE_QC35:
|
||||
deviceSupport = new ServiceDeviceSupport(new QC35BaseSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
|
||||
break;
|
||||
}
|
||||
if (deviceSupport != null) {
|
||||
deviceSupport.setContext(gbDevice, mBtAdapter, mContext);
|
||||
|
@ -470,7 +470,7 @@ public class PineTimeJFSupport extends AbstractBTLEDeviceSupport implements DfuL
|
||||
|
||||
if (getSupportedServices().contains(PineTimeJFConstants.UUID_SERVICE_MOTION)) {
|
||||
builder.notify(getCharacteristic(PineTimeJFConstants.UUID_CHARACTERISTIC_MOTION_STEP_COUNT), true);
|
||||
builder.notify(getCharacteristic(PineTimeJFConstants.UUID_CHARACTERISTIC_MOTION_RAW_XYZ_VALUES), true);
|
||||
//builder.notify(getCharacteristic(PineTimeJFConstants.UUID_CHARACTERISTIC_MOTION_RAW_XYZ_VALUES), false); // issue #2527
|
||||
}
|
||||
|
||||
setInitialized(builder);
|
||||
@ -635,7 +635,6 @@ public class PineTimeJFSupport extends AbstractBTLEDeviceSupport implements DfuL
|
||||
} else if (characteristicUUID.equals(PineTimeJFConstants.UUID_CHARACTERISTIC_MOTION_STEP_COUNT)) {
|
||||
int steps = BLETypeConversions.toUint32(characteristic.getValue());
|
||||
if (LOG.isDebugEnabled()) {
|
||||
GB.toast("Steps count: " + steps, Toast.LENGTH_SHORT, GB.INFO);
|
||||
LOG.debug("onCharacteristicChanged: MotionService:Steps=" + steps);
|
||||
}
|
||||
onReceiveStepsSample(steps);
|
||||
|
@ -0,0 +1,89 @@
|
||||
/* Copyright (C) 2021 Daniel Dakhno
|
||||
|
||||
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 <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.qc35;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.AbstractSerialDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
|
||||
|
||||
public class QC35BaseSupport extends AbstractSerialDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void onSetAlarms(ArrayList<? extends Alarm> alarms) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInstallApp(Uri uri) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAppConfiguration(UUID appUuid, String config, Integer id) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeartRateTest() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetConstantVibration(int integer) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetHeartRateMeasurementInterval(int seconds) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadConfiguration(String config) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean connect() {
|
||||
getDeviceProtocol();
|
||||
getDeviceIOThread().start();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useAutoConnect() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GBDeviceProtocol createDeviceProtocol() {
|
||||
return new QC35Protocol(getDevice());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GBDeviceIoThread createDeviceIOThread() {
|
||||
return new QC35IOThread(getDevice(), getContext(), (QC35Protocol) createDeviceProtocol(), this, getBluetoothAdapter());
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
/* Copyright (C) 2021 Daniel Dakhno
|
||||
|
||||
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 <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.qc35;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Context;
|
||||
import android.os.ParcelUuid;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btclassic.BtClassicIoThread;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.AbstractSerialDeviceSupport;
|
||||
|
||||
public class QC35IOThread extends BtClassicIoThread {
|
||||
QC35Protocol protocol;
|
||||
byte[] buffer = new byte[1024];
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
public QC35IOThread(GBDevice gbDevice, Context context, QC35Protocol deviceProtocol, AbstractSerialDeviceSupport deviceSupport, BluetoothAdapter btAdapter) {
|
||||
super(gbDevice, context, deviceProtocol, deviceSupport, btAdapter);
|
||||
this.protocol = deviceProtocol;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected UUID getUuidToConnect(@NonNull ParcelUuid[] uuids) {
|
||||
return UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize() {
|
||||
super.initialize();
|
||||
|
||||
byte[] connectPayload = new byte[]{0x00, 0x01, 0x01, 0x00};
|
||||
byte[] ncPayload = protocol.encodeSendConfiguration(DeviceSettingsPreferenceConst.PREF_QC35_NOISE_CANCELLING_LEVEL);
|
||||
byte[] batteryPayload = new byte[]{0x02, 0x02, 0x01, 0x00};
|
||||
byte[] packet = new byte[connectPayload.length + ncPayload.length + batteryPayload.length];
|
||||
System.arraycopy(connectPayload, 0, packet, 0, connectPayload.length);
|
||||
System.arraycopy(ncPayload, 0, packet, connectPayload.length, ncPayload.length);
|
||||
System.arraycopy(batteryPayload, 0, packet, ncPayload.length + connectPayload.length, batteryPayload.length);
|
||||
|
||||
getDevice().setFirmwareVersion("0");
|
||||
|
||||
write(packet);
|
||||
}
|
||||
@Override
|
||||
protected byte[] parseIncoming(InputStream inStream) throws IOException {
|
||||
int size = inStream.read(buffer);
|
||||
logger.debug("read bytes: {}", size);
|
||||
byte[] actual = new byte[size];
|
||||
System.arraycopy(buffer, 0, actual, 0, size);
|
||||
return actual;
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/* Copyright (C) 2021 Daniel Dakhno
|
||||
|
||||
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 <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.qc35;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
|
||||
|
||||
public class QC35Protocol extends GBDeviceProtocol {
|
||||
Logger logger = LoggerFactory.getLogger(getClass());
|
||||
protected QC35Protocol(GBDevice device) {
|
||||
super(device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GBDeviceEvent[] decodeResponse(byte[] responseData) {
|
||||
logger.debug("response: {}", StringUtils.bytesToHex(responseData));
|
||||
|
||||
ArrayList<GBDeviceEvent> events = new ArrayList<>();
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.wrap(responseData);
|
||||
while(buffer.remaining() > 0){
|
||||
int first = buffer.get();
|
||||
int second = buffer.get();
|
||||
int third = buffer.get();
|
||||
int length = buffer.get();
|
||||
byte[] data = new byte[length];
|
||||
buffer.get(data);
|
||||
if(first == 0x02){
|
||||
if(second == 0x02){
|
||||
if(third == 0x03){
|
||||
GBDeviceEventBatteryInfo batteryInfo = new GBDeviceEventBatteryInfo();
|
||||
batteryInfo.level = data[0];
|
||||
events.add(batteryInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return events.toArray(new GBDeviceEvent[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeTestNewFunction() {
|
||||
return new byte[]{0x02, 0x02, 0x01, 0x00};
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeSendConfiguration(String config) {
|
||||
SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress());
|
||||
|
||||
if(config.equals(DeviceSettingsPreferenceConst.PREF_QC35_NOISE_CANCELLING_LEVEL)){
|
||||
int level = prefs.getInt(config, 0);
|
||||
if(level == 2){
|
||||
level = 1;
|
||||
}else if(level == 1){
|
||||
level = 3;
|
||||
}
|
||||
return new byte[]{0x01, 0x06, 0x02, 0x01, (byte) level};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/* Copyright (C) 2021 Daniel Dakhno
|
||||
|
||||
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 <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.vesc;
|
||||
|
||||
public enum CommandType {
|
||||
SET_CURRENT((byte) 0x06),
|
||||
SET_CURRENT_BRAKE((byte) 0x07),
|
||||
SET_RPM((byte) 0x08),
|
||||
;
|
||||
byte commandByte;
|
||||
|
||||
CommandType(byte commandByte){
|
||||
this.commandByte = commandByte;
|
||||
}
|
||||
|
||||
public byte getCommandByte(){
|
||||
return this.commandByte;
|
||||
}
|
||||
}
|
@ -0,0 +1,198 @@
|
||||
/* Copyright (C) 2021 Daniel Dakhno
|
||||
|
||||
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 <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.vesc;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.UUID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
|
||||
|
||||
public class VescBaseDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(VescBaseDeviceSupport.class);
|
||||
|
||||
public VescBaseDeviceSupport() {
|
||||
super(LOG);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onNotification(NotificationSpec notificationSpec) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeleteNotification(int id) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetTime() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetAlarms(ArrayList<? extends Alarm> alarms) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetCallState(CallSpec callSpec) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetMusicState(MusicStateSpec stateSpec) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetMusicInfo(MusicSpec musicSpec) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnableRealtimeSteps(boolean enable) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInstallApp(Uri uri) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAppInfoReq() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAppStart(UUID uuid, boolean start) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAppDelete(UUID uuid) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAppConfiguration(UUID appUuid, String config, Integer id) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAppReorder(UUID[] uuids) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFetchRecordedData(int dataTypes) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReset(int flags) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeartRateTest() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFindDevice(boolean start) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetConstantVibration(int integer) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScreenshotReq() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnableHeartRateSleepSupport(boolean enable) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetHeartRateMeasurementInterval(int seconds) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeleteCalendarEvent(byte type, long id) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendConfiguration(String config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadConfiguration(String config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestNewFunction() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendWeather(WeatherSpec weatherSpec) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useAutoConnect() {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,179 @@
|
||||
/* Copyright (C) 2021 Daniel Dakhno
|
||||
|
||||
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 <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.vesc;
|
||||
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.vesc.VescCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.CheckSums;
|
||||
|
||||
public class VescDeviceSupport extends VescBaseDeviceSupport{
|
||||
BluetoothGattCharacteristic serialWriteCharacteristic;
|
||||
|
||||
public static final String COMMAND_SET_RPM = "nodomain.freeyourgadget.gadgetbridge.vesc.command.SET_RPM";
|
||||
public static final String COMMAND_SET_CURRENT = "nodomain.freeyourgadget.gadgetbridge.vesc.command.SET_CURRENT";
|
||||
public static final String COMMAND_SET_BREAK_CURRENT = "nodomain.freeyourgadget.gadgetbridge.vesc.command.SET_BREAK_CURRENT";
|
||||
public static final String EXTRA_RPM = "EXTRA_RPM";
|
||||
public static final String EXTRA_CURRENT = "EXTRA_CURRENT";
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private DeviceType deviceType;
|
||||
|
||||
public VescDeviceSupport(DeviceType type){
|
||||
super();
|
||||
logger.debug("VescDeviceSupport() {}", type);
|
||||
|
||||
deviceType = type;
|
||||
|
||||
if(type == DeviceType.VESC_NRF){
|
||||
addSupportedService(UUID.fromString(VescCoordinator.UUID_SERVICE_SERIAL_NRF));
|
||||
}else if(type == DeviceType.VESC_HM10){
|
||||
addSupportedService(UUID.fromString(VescCoordinator.UUID_SERVICE_SERIAL_HM10));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
|
||||
logger.debug("initializing device");
|
||||
|
||||
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
|
||||
|
||||
initBroadcast();
|
||||
|
||||
if(deviceType == DeviceType.VESC_NRF){
|
||||
this.serialWriteCharacteristic = getCharacteristic(UUID.fromString(VescCoordinator.UUID_CHARACTERISTIC_SERIAL_TX_NRF));
|
||||
}else if(deviceType == DeviceType.VESC_HM10){
|
||||
this.serialWriteCharacteristic = getCharacteristic(UUID.fromString(VescCoordinator.UUID_CHARACTERISTIC_SERIAL_TX_HM10));
|
||||
}
|
||||
|
||||
return builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext()));
|
||||
}
|
||||
|
||||
private void initBroadcast() {
|
||||
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext());
|
||||
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(COMMAND_SET_RPM);
|
||||
filter.addAction(COMMAND_SET_CURRENT);
|
||||
filter.addAction(COMMAND_SET_BREAK_CURRENT);
|
||||
|
||||
broadcastManager.registerReceiver(commandReceiver, filter);
|
||||
}
|
||||
|
||||
BroadcastReceiver commandReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if(intent.getAction().equals(COMMAND_SET_RPM)){
|
||||
VescDeviceSupport.this.setRPM(
|
||||
intent.getIntExtra(EXTRA_RPM, 0)
|
||||
);
|
||||
}else if(intent.getAction().equals(COMMAND_SET_BREAK_CURRENT)){
|
||||
VescDeviceSupport.this.setBreakCurrent(
|
||||
intent.getIntExtra(EXTRA_CURRENT, 0)
|
||||
);
|
||||
}else if(intent.getAction().equals(COMMAND_SET_CURRENT)){
|
||||
VescDeviceSupport.this.setCurrent(
|
||||
intent.getIntExtra(EXTRA_CURRENT, 0)
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public void setCurrent(int currentMillisAmperes){
|
||||
buildAndQueryPacket(CommandType.SET_CURRENT, currentMillisAmperes);
|
||||
}
|
||||
|
||||
public void setBreakCurrent(int breakCurrentMillisAmperes){
|
||||
buildAndQueryPacket(CommandType.SET_CURRENT_BRAKE, breakCurrentMillisAmperes);
|
||||
}
|
||||
|
||||
public void setRPM(int rpm){
|
||||
buildAndQueryPacket(CommandType.SET_RPM, rpm);
|
||||
}
|
||||
|
||||
public void buildAndQueryPacket(CommandType commandType, Object ... args){
|
||||
byte[] data = buildPacket(commandType, args);
|
||||
queryPacket(data);
|
||||
}
|
||||
|
||||
public void queryPacket(byte[] data){
|
||||
new TransactionBuilder("write serial packet")
|
||||
.write(this.serialWriteCharacteristic, data)
|
||||
.queue(getQueue());
|
||||
}
|
||||
|
||||
public byte[] buildPacket(CommandType commandType, Object ... args){
|
||||
int dataLength = 0;
|
||||
for(Object arg : args){
|
||||
if(arg instanceof Integer) dataLength += 4;
|
||||
else if(arg instanceof Short) dataLength += 2;
|
||||
}
|
||||
ByteBuffer buffer = ByteBuffer.allocate(dataLength);
|
||||
|
||||
for(Object arg : args){
|
||||
if(arg instanceof Integer) buffer.putInt((Integer) arg);
|
||||
if(arg instanceof Short) buffer.putShort((Short) arg);
|
||||
}
|
||||
|
||||
return buildPacket(commandType, buffer.array());
|
||||
}
|
||||
|
||||
public byte[] buildPacket(CommandType commandType, byte[] data){
|
||||
return buildPacket(commandType.getCommandByte(), data);
|
||||
}
|
||||
|
||||
private byte[] buildPacket(byte commandByte, byte[] data){
|
||||
byte[] contents = new byte[data.length + 1];
|
||||
contents[0] = commandByte;
|
||||
System.arraycopy(data, 0, contents, 1, data.length);
|
||||
return buildPacket(contents);
|
||||
}
|
||||
|
||||
private byte[] buildPacket(byte[] contents){
|
||||
int dataLength = contents.length;
|
||||
ByteBuffer buffer = ByteBuffer.allocate(dataLength + (dataLength < 256 ? 5 : 6));
|
||||
if(dataLength < 256){
|
||||
buffer.put((byte)0x02);
|
||||
buffer.put((byte)dataLength);
|
||||
}else{
|
||||
buffer.put((byte) 0x03);
|
||||
buffer.putShort((short) dataLength);
|
||||
}
|
||||
buffer.put(contents);
|
||||
buffer.putShort((short) CheckSums.getCRC16(contents, 0));
|
||||
buffer.put((byte) 0x03);
|
||||
|
||||
return buffer.array();
|
||||
}
|
||||
}
|
@ -103,6 +103,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.nothing.Ear1Coordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.nut.NutCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.pinetime.PineTimeJFCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.qc35.QC35Coordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.QHybridCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.roidmi.Roidmi1Coordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.roidmi.Roidmi3Coordinator;
|
||||
@ -110,6 +111,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.smaq2oss.SMAQ2OSSCoordinator
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sonyswr12.SonySWR12DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.tlw64.TLW64Coordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.um25.Coordinator.UM25Coordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.vesc.VescCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.vibratissimo.VibratissimoCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.waspos.WaspOSCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.watch9.Watch9DeviceCoordinator;
|
||||
@ -315,8 +317,10 @@ public class DeviceHelper {
|
||||
result.add(new Ear1Coordinator());
|
||||
result.add(new GalaxyBudsDeviceCoordinator());
|
||||
result.add(new GalaxyBudsLiveDeviceCoordinator());
|
||||
result.add(new VescCoordinator());
|
||||
result.add(new SonyWH1000XM3Coordinator());
|
||||
result.add(new SonyWFSP800NCoordinator());
|
||||
result.add(new QC35Coordinator());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
76
app/src/main/res/layout/activity_vesc_control.xml
Normal file
76
app/src/main/res/layout/activity_vesc_control.xml
Normal file
@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="RPM: "
|
||||
android:labelFor="@+id/vesc_control_input_rpm"/>
|
||||
|
||||
<EditText
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="numberDecimal"
|
||||
android:id="@+id/vesc_control_input_rpm"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Break current (A): " />
|
||||
|
||||
<EditText
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="numberDecimal"
|
||||
android:id="@+id/vesc_control_input_break_current"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="100dp"
|
||||
android:layout_weight="0.5"
|
||||
android:text="break"
|
||||
android:id="@+id/vesc_control_button_break"/>
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="100dp"
|
||||
android:layout_weight="0.5"
|
||||
android:text="fwd"
|
||||
android:id="@+id/vesc_control_button_fwd" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<CheckBox
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Volume keys control"
|
||||
android:id="@+id/vesc_control_checkbox_volume_keys" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -988,7 +988,7 @@
|
||||
<string name="companiondevice_pairing">Párování systémem Doprovodného Zařízení</string>
|
||||
<string name="require_location_provider">Poloha musí být povolena</string>
|
||||
<string name="menuitem_stress">Stres</string>
|
||||
<string name="menuitem_cycles">Záznam cyklistiky</string>
|
||||
<string name="menuitem_cycles">Cyklus</string>
|
||||
<string name="menuitem_breathing">Dýchání</string>
|
||||
<string name="devicetype_pinetime_jf">PineTime (JF Firmware)</string>
|
||||
<string name="devicetype_tlw64">TLW64</string>
|
||||
@ -1496,4 +1496,19 @@
|
||||
<string name="minCadence">Min rytmus</string>
|
||||
<string name="pref_header_sony_equalizer_bands">Rozsahy</string>
|
||||
<string name="sony_audio_upsampling">Převzorkování zvuku</string>
|
||||
<string name="activity_prefs_discovery_pairing">Možnosti vyhledávání a párování</string>
|
||||
<string name="sony_button_mode_ambient_sound_control">Nastavení zvuku okolí</string>
|
||||
<string name="sony_button_mode_playback_control">Nastavení přehrávání</string>
|
||||
<string name="sony_button_mode_volume_control">Ovládání hlasitosti</string>
|
||||
<string name="controlcenter_power_off_confirm_title">Vypnout</string>
|
||||
<string name="controlcenter_power_off">Vypnutí</string>
|
||||
<string name="controlcenter_power_off_confirm_description">Opravdu chcete zařízení vypnout\?</string>
|
||||
<string name="add_test_device">Přidat testovacího zařízení</string>
|
||||
<string name="devicetype_sony_wf_sp800n">Sony WF-SP800N</string>
|
||||
<string name="discover_unsupported_devices">Vyhledávat nepodporovaná zařízení</string>
|
||||
<string name="discover_unsupported_devices_description">Povolením této volby budou vyhledávána i nepodporovaná zařízení. Kliknutí zkopíruje jméno a MAC adresu zařízení do schránky. Podržení spustí dialog `Přidat testovací zařízení`.</string>
|
||||
<string name="sony_pause_when_taken_off">Pozastavení přehrávání při sundání sluchátek</string>
|
||||
<string name="sony_button_mode_left">Režim tlačítka (vlevo)</string>
|
||||
<string name="sony_button_mode_off">Vypnout</string>
|
||||
<string name="sony_button_mode_right">Režim tlačítka (vpravo)</string>
|
||||
</resources>
|
@ -983,10 +983,10 @@
|
||||
<string name="caloriesBurnt">Kalorien</string>
|
||||
<string name="activeSeconds">Aktiv</string>
|
||||
<string name="steps">Schritte</string>
|
||||
<string name="minAltitude">Minimum</string>
|
||||
<string name="maxAltitude">Maximum</string>
|
||||
<string name="descentMeters">Bergab</string>
|
||||
<string name="ascentMeters">Bergauf</string>
|
||||
<string name="minAltitude">Minimale Höhe</string>
|
||||
<string name="maxAltitude">Maximale Höhe</string>
|
||||
<string name="descentMeters">Höhenverlust</string>
|
||||
<string name="ascentMeters">Höhengewinn</string>
|
||||
<string name="distanceMeters">Strecke</string>
|
||||
<string name="activity_summary_detail">Detail der Sportaktivität</string>
|
||||
<string name="maxSpeed">Maximum</string>
|
||||
@ -1034,8 +1034,8 @@
|
||||
<string name="strokes_unit">Züge</string>
|
||||
<string name="flatSeconds">Flach</string>
|
||||
<string name="averageStrokesPerSecond">Durchschnittliche Schläge</string>
|
||||
<string name="averageStrokeDistance">Durchschnittliche Schlag Distanz</string>
|
||||
<string name="averageStride">Durchschnittsfortschritt</string>
|
||||
<string name="averageStrokeDistance">Durchschnittliche Schlagdistanz</string>
|
||||
<string name="averageStride">Mittlere Schrittlänge</string>
|
||||
<string name="totalStride">Gesamtfortschritt</string>
|
||||
<string name="activity_summary_edit_name_title">Beschriftung bearbeiten</string>
|
||||
<string name="swolf_index">Swolf-Index</string>
|
||||
@ -1491,6 +1491,33 @@
|
||||
<string name="prefs_activity_in_device_card_distance_title_summary">Strecke basiert auf Schrittzahl und Schrittlänge (siehe Einstellungen - Über Dich)</string>
|
||||
<string name="sony_equalizer_preset_excited">Angeregt</string>
|
||||
<string name="battery_case">Batteriefach</string>
|
||||
<string name="sony_button_mode_ambient_sound_control">Umgebungsgeräusch-Steuerung</string>
|
||||
<string name="minSpeed">Minimum</string>
|
||||
<string name="maxCadence">Max Kadenz</string>
|
||||
<string name="minCadence">Min Kadenz</string>
|
||||
<string name="sony_button_mode_off">Aus</string>
|
||||
<string name="activity_prefs_discovery_pairing">Auffindungs- und Kopplungsoptionen</string>
|
||||
<string name="add_test_device">Testgerät hinzufügen</string>
|
||||
<string name="devicetype_sony_wf_sp800n">Sony WF-SP800N</string>
|
||||
<string name="controlcenter_power_off_confirm_title">Ausschalten</string>
|
||||
<string name="controlcenter_power_off">Ausschalten</string>
|
||||
<string name="controlcenter_power_off_confirm_description">Sind Sie sicher, dass Sie das Gerät abschalten wollen\?</string>
|
||||
<string name="discover_unsupported_devices">Nicht unterstützte Geräte finden</string>
|
||||
<string name="minStride">Min Schrittlänge</string>
|
||||
<string name="discover_unsupported_devices_description">Das aktivieren dieser Option erlaubt das Anzeigen aller Bluetooth Geräte die während eines Scans gefunden werden. Ein kurzes Tippen kopiert Gerätename und Mac-adresse in die Zwischenablage. Langes Drücken öffnet den \'Neues Gerät hinzufügen\' Dialog.</string>
|
||||
<string name="averageAltitude">Mittlere Höhe</string>
|
||||
<string name="maxHR">Max Herzfrequenz</string>
|
||||
<string name="minHR">Min Herzfrequenz</string>
|
||||
<string name="sony_pause_when_taken_off">Pausieren wenn Kopfhörer abgenommen werden</string>
|
||||
<string name="maxStride">Max Schrittlänge</string>
|
||||
<string name="averageCadence">Mittlere Kadenz</string>
|
||||
<string name="spm">Schritte/Min</string>
|
||||
<string name="sony_button_mode_left">Schalter Modus (Links)</string>
|
||||
<string name="sony_button_mode_playback_control">Wiedergabesteuerung</string>
|
||||
<string name="sony_button_mode_right">Schalter Modus (Rechts)</string>
|
||||
<string name="sony_button_mode_volume_control">Lautstärkeregelung</string>
|
||||
<string name="pref_header_sony_equalizer_bands">Bänder</string>
|
||||
<string name="sony_audio_upsampling">Audio Upsampling</string>
|
||||
<string name="info_connected_count">%d Geräte verbunden</string>
|
||||
<string name="info_no_devices_connected">Keine Geräte verbunden</string>
|
||||
</resources>
|
@ -1490,4 +1490,21 @@
|
||||
<string name="minHR">Min Heartrate</string>
|
||||
<string name="maxStride">Max Stride</string>
|
||||
<string name="minStride">Min Stride</string>
|
||||
<string name="controlcenter_power_off">Power Off</string>
|
||||
<string name="sony_pause_when_taken_off">Pause when headphones are taken off</string>
|
||||
<string name="sony_button_mode_ambient_sound_control">Ambient Sound Control</string>
|
||||
<string name="activity_prefs_discovery_pairing">Discovery and Pairing options</string>
|
||||
<string name="controlcenter_power_off_confirm_title">Power Off</string>
|
||||
<string name="add_test_device">Add test device</string>
|
||||
<string name="devicetype_sony_wf_sp800n">Sony WF-SP800N</string>
|
||||
<string name="controlcenter_power_off_confirm_description">Are you sure you want to power off the device\?</string>
|
||||
<string name="sony_button_mode_left">Button Mode (Left)</string>
|
||||
<string name="discover_unsupported_devices">Discover unsupported devices</string>
|
||||
<string name="sony_button_mode_volume_control">Volume Control</string>
|
||||
<string name="discover_unsupported_devices_description">Enabling this option will display all discovered Bluetooth devices when scanning. Short tap will copy device name and MAC address to the clipboard. Long press will launch `Add test device` dialog.</string>
|
||||
<string name="sony_button_mode_right">Button Mode (Right)</string>
|
||||
<string name="sony_button_mode_off">Off</string>
|
||||
<string name="sony_button_mode_playback_control">Playback Control</string>
|
||||
<string name="pref_header_sony_equalizer_bands">Bands</string>
|
||||
<string name="sony_audio_upsampling">Audio Upsampling</string>
|
||||
</resources>
|
@ -1494,4 +1494,20 @@
|
||||
<string name="minStride">צעד מזערי</string>
|
||||
<string name="pref_header_sony_equalizer_bands">צמידים</string>
|
||||
<string name="sony_audio_upsampling">Upsampling לשמע</string>
|
||||
<string name="controlcenter_power_off_confirm_description">לכבות את ההתקן\?</string>
|
||||
<string name="sony_button_mode_volume_control">בקרת שמע</string>
|
||||
<string name="activity_prefs_discovery_pairing">אפשרויות גילוי וצימוד</string>
|
||||
<string name="sony_button_mode_right">מצב הכפתור (ימין)</string>
|
||||
<string name="discover_unsupported_devices">גילוי התקנים שאינם נתמכים</string>
|
||||
<string name="controlcenter_power_off">כיבוי</string>
|
||||
<string name="controlcenter_power_off_confirm_title">כיבוי</string>
|
||||
<string name="add_test_device">הוספת התקן בדיקה</string>
|
||||
<string name="sony_button_mode_left">מצב כפתור (שמאל)</string>
|
||||
<string name="sony_button_mode_off">כבוי</string>
|
||||
<string name="devicetype_sony_wf_sp800n">Sony WF-SP800N</string>
|
||||
<string name="discover_unsupported_devices_description">הפעלת אפשרות זו תציג התקני Bluetooth בעת הסריקה. נגיעה קצרה תעתיק את שם ההתקן ואת כתובת החומרה שלו ללוח הגזירים. לחיצה ארוכה תפעיל את חלונית `הוספת התקן בדיקה`.</string>
|
||||
<string name="sony_pause_when_taken_off">להשהות כאשר האוזניות מוסרות</string>
|
||||
<string name="sony_button_mode_ambient_sound_control">בקרת שמע אופף</string>
|
||||
<string name="sony_button_mode_playback_control">בקרת נגינה</string>
|
||||
<string name="devicetype_bose_qc35">Bose QC35</string>
|
||||
</resources>
|
@ -6,14 +6,14 @@
|
||||
<string name="action_debug">Depurar</string>
|
||||
<string name="action_quit">Sair</string>
|
||||
<string name="controlcenter_fetch_activity_data">Sincronizar</string>
|
||||
<string name="controlcenter_find_device">Procurar aparelho perdido</string>
|
||||
<string name="controlcenter_find_device">Encontrar dispositivo perdido</string>
|
||||
<string name="controlcenter_take_screenshot">Captura de ecrã</string>
|
||||
<string name="controlcenter_change_led_color">Mudar cor do LED</string>
|
||||
<string name="controlcenter_change_fm_frequency">Mudar frequência FM</string>
|
||||
<string name="controlcenter_disconnect">Desconectar</string>
|
||||
<string name="controlcenter_delete_device">Apagar aparelho</string>
|
||||
<string name="controlcenter_delete_device_name">Apagar %1$s</string>
|
||||
<string name="controlcenter_delete_device_dialogmessage">Isto irá apagar o aparelho e apagar todos os dados associados!</string>
|
||||
<string name="controlcenter_delete_device_dialogmessage">Isto irá apagar o dispositivo e todos os dados associados!</string>
|
||||
<string name="controlcenter_navigation_drawer_open">Abrir gaveta de navegação</string>
|
||||
<string name="controlcenter_navigation_drawer_close">Fechar gaveta de navegação</string>
|
||||
<string name="controlcenter_snackbar_need_longpress">Pressione longo no cartão para desconectar</string>
|
||||
@ -123,7 +123,7 @@
|
||||
<string name="pref_summary_location_keep_uptodate">Tente obter a localização online, use dados armazenados como alternativa</string>
|
||||
<string name="toast_enable_networklocationprovider">Por favor, ative a localização de rede</string>
|
||||
<string name="toast_aqurired_networklocation">localização obtida</string>
|
||||
<string name="pref_title_pebble_forceprotocol">Forçar o protocolo de notificação</string>
|
||||
<string name="pref_title_pebble_forceprotocol">Forçar protocolo de notificação</string>
|
||||
<string name="pref_summary_pebble_forceprotocol">Esta opção força o uso do protocolo de notificação mais recente dependendo da versão do firmware. FAÇA ISSO APENAS SE SOUBER O QUE ESTÁ FAZENDO!</string>
|
||||
<string name="pref_title_pebble_forceuntested">Permitir recursos não certificados</string>
|
||||
<string name="pref_summary_pebble_forceuntested">Ativar recursos não testados. FAÇA ISSO APENAS SE SOUBER O QUE ESTÁ FAZENDO!</string>
|
||||
@ -543,7 +543,7 @@
|
||||
<string name="pref_summary_rtl">Ative isto se o seu aparelho pode mostrar idiomas que se escrevem da direita para a esquerda</string>
|
||||
<string name="pref_rtl_max_line_length">Comprimento máximo da linha da direita para a esquerda</string>
|
||||
<string name="pref_rtl_max_line_length_summary">Alonga ou encurta as linhas da direita para a esquerda em que o texto é cortado</string>
|
||||
<string name="pref_summary_enable_calendar_sync">Envia eventos do calendário para linha do tempo</string>
|
||||
<string name="pref_summary_enable_calendar_sync">Enviar eventos do calendário para a linha do tempo</string>
|
||||
<string name="pref_title_pebble_enable_bgjs">Ativar JS em segundo plano</string>
|
||||
<string name="pref_summary_pebble_enable_bgjs">Quando ativado, permite que as mostradores mostrem o clima, informações da bateria, etc.</string>
|
||||
<string name="prefs_title_heartrate_measurement_interval">Medição diária dos batimentos cardíacos</string>
|
||||
@ -1446,4 +1446,7 @@
|
||||
<string name="watchface_dialog_widget_preset_right">Direita</string>
|
||||
<string name="watchface_widget_type_2nd_tz">2º fuso horário</string>
|
||||
<string name="watchface_widget_type_active_mins">Minutos ativos</string>
|
||||
<string name="controlcenter_power_off_confirm_description">Tem a certeza que pretende desligar o dispositivo\?</string>
|
||||
<string name="controlcenter_power_off">Desligar</string>
|
||||
<string name="controlcenter_power_off_confirm_title">Desligar</string>
|
||||
</resources>
|
@ -460,8 +460,8 @@
|
||||
<string name="mi2_prefs_inactivity_warnings_summary">Когда вы мало двигаетесь, браслет время от времени вибрирует</string>
|
||||
<string name="mi2_prefs_inactivity_warnings_threshold">Период низкой активности (в минутах)</string>
|
||||
<string name="mi2_prefs_inactivity_warnings_dnd_summary">Отключить уведомления о низкой активности во время режима \"Не беспокоить\"</string>
|
||||
<string name="mi2_prefs_do_not_disturb_start">Начало режим \"Не беспокоить\"</string>
|
||||
<string name="mi2_prefs_do_not_disturb_end">Окончание режима \"Не беспокоить\"</string>
|
||||
<string name="mi2_prefs_do_not_disturb_start">Начальное время</string>
|
||||
<string name="mi2_prefs_do_not_disturb_end">Время окончания</string>
|
||||
<string name="automatic">Автоматически</string>
|
||||
<string name="simplified_chinese">Упрощённый китайский язык</string>
|
||||
<string name="traditional_chinese">Традиционный китайский язык</string>
|
||||
|
@ -1521,4 +1521,19 @@
|
||||
<string name="spm">adım/dak</string>
|
||||
<string name="pref_header_sony_equalizer_bands">Bant</string>
|
||||
<string name="sony_audio_upsampling">Ses Üst Örnekleme</string>
|
||||
<string name="sony_button_mode_right">Düğme Modu (Sağ)</string>
|
||||
<string name="sony_button_mode_playback_control">Oynatım Denetimi</string>
|
||||
<string name="discover_unsupported_devices_description">Bu seçeneğin etkinleştirilmesi, tarama sırasında keşfedilen tüm bluetooth aygıtlarını görüntüleyecektir. Kısa dokunma, aygıt adını ve MAC adresini panoya kopyalayacaktır. Uzun basıldığında, `Test aygıtı ekle` iletişim kutusu açılacaktır.</string>
|
||||
<string name="controlcenter_power_off_confirm_description">Aygıtı kapatmak istediğinizden emin misiniz\?</string>
|
||||
<string name="controlcenter_power_off_confirm_title">Kapat</string>
|
||||
<string name="devicetype_sony_wf_sp800n">Sony WF-SP800N</string>
|
||||
<string name="controlcenter_power_off">Kapat</string>
|
||||
<string name="activity_prefs_discovery_pairing">Keşif ve Eşleştirme seçenekleri</string>
|
||||
<string name="add_test_device">Test aygıtı ekle</string>
|
||||
<string name="discover_unsupported_devices">Desteklenmeyen aygıtları keşfet</string>
|
||||
<string name="sony_button_mode_off">Kapalı</string>
|
||||
<string name="sony_pause_when_taken_off">Kulaklıklar çıkarıldığında duraklat</string>
|
||||
<string name="sony_button_mode_left">Düğme Modu (Sol)</string>
|
||||
<string name="sony_button_mode_ambient_sound_control">Ortam Sesi Denetimi</string>
|
||||
<string name="sony_button_mode_volume_control">Ses Denetimi</string>
|
||||
</resources>
|
@ -1505,4 +1505,15 @@
|
||||
<string name="add_test_device">Додати тестовий пристрій</string>
|
||||
<string name="discover_unsupported_devices">Виявляти непідтримувані пристрої</string>
|
||||
<string name="discover_unsupported_devices_description">Якщо ввімкнути цей параметр, під час сканування буде показано всі виявлені Bluetooth-пристрої. Короткий дотик призведе до копіювання імені пристрою та mac-адреси до буфера обміну. Тривале натискання відкриє діалогове вікно «Додати тестовий пристрій».</string>
|
||||
<string name="sony_button_mode_left">Режим кнопки (Ліворуч)</string>
|
||||
<string name="devicetype_sony_wf_sp800n">Sony WF-SP800N</string>
|
||||
<string name="sony_button_mode_volume_control">Регулювання гучності</string>
|
||||
<string name="controlcenter_power_off_confirm_description">Ви впевнені, що хочете вимкнути пристрій\?</string>
|
||||
<string name="controlcenter_power_off">Вимкнути</string>
|
||||
<string name="controlcenter_power_off_confirm_title">Вимкнути</string>
|
||||
<string name="sony_pause_when_taken_off">Зупиняти, коли знімаються навушники</string>
|
||||
<string name="sony_button_mode_off">Вимкнути</string>
|
||||
<string name="sony_button_mode_right">Режим кнопки (Праворуч)</string>
|
||||
<string name="sony_button_mode_ambient_sound_control">Керування навколишнім звуком</string>
|
||||
<string name="sony_button_mode_playback_control">Керування відтворенням</string>
|
||||
</resources>
|
@ -1500,4 +1500,19 @@
|
||||
<string name="spm">步数/分钟</string>
|
||||
<string name="pref_header_sony_equalizer_bands">乐队</string>
|
||||
<string name="sony_audio_upsampling">音频提升采样</string>
|
||||
<string name="controlcenter_power_off_confirm_title">关机</string>
|
||||
<string name="controlcenter_power_off">关机</string>
|
||||
<string name="controlcenter_power_off_confirm_description">您确定想要关闭此设备的电源吗?</string>
|
||||
<string name="devicetype_sony_wf_sp800n">索尼 WF-SP800N</string>
|
||||
<string name="activity_prefs_discovery_pairing">发现和配对选项</string>
|
||||
<string name="add_test_device">添加测试设备</string>
|
||||
<string name="discover_unsupported_devices">发现未支持的设备</string>
|
||||
<string name="discover_unsupported_devices_description">启用该选项将会显示扫描时所有已发现的设备。短按将会复制设备名称和MAC地址到剪切板。长按将会显示“添加测试设备”对话框。</string>
|
||||
<string name="sony_button_mode_off">关</string>
|
||||
<string name="sony_pause_when_taken_off">当摘下耳机时暂停</string>
|
||||
<string name="sony_button_mode_left">按钮模式(左)</string>
|
||||
<string name="sony_button_mode_right">按钮模式(右)</string>
|
||||
<string name="sony_button_mode_ambient_sound_control">环境音控制</string>
|
||||
<string name="sony_button_mode_playback_control">回放控制</string>
|
||||
<string name="sony_button_mode_volume_control">音量控制</string>
|
||||
</resources>
|
@ -1120,6 +1120,8 @@
|
||||
<string name="distanceMeters">Distance</string>
|
||||
<string name="ascentMeters">Uphill</string>
|
||||
<string name="descentMeters">Downhill</string>
|
||||
<string name="ascentDistance">Uphill distance</string>
|
||||
<string name="descentDistance">Downhill distance</string>
|
||||
<string name="maxAltitude">Maximum</string>
|
||||
<string name="minAltitude">Minimum</string>
|
||||
<string name="averageAltitude">Average</string>
|
||||
@ -1442,6 +1444,8 @@
|
||||
<string name="watchface_dialog_widget_timeout_show_circle">Show circle on timeout:</string>
|
||||
<string name="qhybrid_title_on_device_confirmation">Enable on-device pairing confirmation</string>
|
||||
<string name="qhybrid_summary_on_device_confirmation">On-device pairing confirmations can get annoying. Disabling them might lose you functionality.</string>
|
||||
<string name="devicetype_vesc">VESC</string>
|
||||
<string name="devicetype_bose_qc35">Bose QC35</string>
|
||||
<string name="info_no_devices_connected">no devices connected</string>
|
||||
<string name="info_connected_count">%d devices connected</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
9
app/src/main/res/xml/devicesettings_qc35.xml
Normal file
9
app/src/main/res/xml/devicesettings_qc35.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<SeekBarPreference
|
||||
android:title="Noice cancelling level"
|
||||
android:max="2"
|
||||
android:key="qc35_noise_cancelling_level" />
|
||||
|
||||
</PreferenceScreen>
|
@ -15,6 +15,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Reminder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
|
||||
|
||||
class TestDeviceSupport extends AbstractDeviceSupport {
|
||||
@ -64,6 +65,11 @@ class TestDeviceSupport extends AbstractDeviceSupport {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetReminders(ArrayList<? extends Reminder> reminders) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetCallState(CallSpec callSpec) {
|
||||
|
||||
@ -203,4 +209,9 @@ class TestDeviceSupport extends AbstractDeviceSupport {
|
||||
public void onSetLedColor(int color) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPowerOff() {
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user