mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2025-01-10 17:11:56 +01:00
Cycling Sensor: added live data view
Cycling Sensor: I18N Cycling Sensor: honor metric/imperial setting Cycling Sensor: re-use existing speed strings
This commit is contained in:
parent
5ebb3b85b0
commit
e3e5c20a5a
@ -901,6 +901,11 @@
|
|||||||
android:name=".activities.CameraActivity"
|
android:name=".activities.CameraActivity"
|
||||||
android:launchMode="singleInstance"
|
android:launchMode="singleInstance"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".devices.cycling_sensor.activity.CyclingLiveDataActivity"
|
||||||
|
android:launchMode="singleInstance"
|
||||||
|
android:exported="true" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
@ -0,0 +1,170 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.devices.cycling_sensor.activity;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.SettingsActivity;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.CyclingSample;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||||
|
|
||||||
|
public class CyclingLiveDataActivity extends AbstractGBActivity {
|
||||||
|
private TextView speedView, tripDistanceView, totalDistanceView;
|
||||||
|
private GBDevice selectedDevice;
|
||||||
|
private float tripStartDistance = 0, tripCurrentDistance = 0;
|
||||||
|
private float toUnitFactor = 1;
|
||||||
|
private int
|
||||||
|
speedStringResource = R.string.km_h,
|
||||||
|
tripStringResource = R.string.label_distance_trip,
|
||||||
|
totalStringResource = R.string.label_distance_total;
|
||||||
|
|
||||||
|
private static final String PREFS_KEY_TRIP_START = "CYCLING_TRIP_START";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
if(getIntent() == null) {
|
||||||
|
selectedDevice = getStoredCyclingSensor();
|
||||||
|
}else if(getIntent().getExtras() == null) {
|
||||||
|
selectedDevice = getStoredCyclingSensor();
|
||||||
|
}else if((selectedDevice =
|
||||||
|
getIntent().getExtras().getParcelable("device")) == null) {
|
||||||
|
selectedDevice = getStoredCyclingSensor();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedDevice == null) {
|
||||||
|
GB.toast(getString(R.string.error_no_cycling_sensor_found), Toast.LENGTH_SHORT, GB.ERROR);
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setContentView(R.layout.activity_cycling_live_data);
|
||||||
|
|
||||||
|
speedView = findViewById(R.id.cycling_data_speed);
|
||||||
|
tripDistanceView = findViewById(R.id.cycling_data_trip_distance);
|
||||||
|
totalDistanceView = findViewById(R.id.cycling_data_total_distance);
|
||||||
|
|
||||||
|
tripStartDistance = GBApplication
|
||||||
|
.getDevicePrefs(selectedDevice)
|
||||||
|
.getFloat(PREFS_KEY_TRIP_START, 0);
|
||||||
|
|
||||||
|
tripDistanceView.setOnLongClickListener(new View.OnLongClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onLongClick(View view) {
|
||||||
|
if(tripCurrentDistance == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
tripStartDistance = tripCurrentDistance;
|
||||||
|
|
||||||
|
GBApplication
|
||||||
|
.getDeviceSpecificSharedPrefs(selectedDevice.getAddress())
|
||||||
|
.edit()
|
||||||
|
.putFloat(PREFS_KEY_TRIP_START, tripStartDistance)
|
||||||
|
.apply();
|
||||||
|
|
||||||
|
tripDistanceView.setText(getString(tripStringResource, 0f));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
String measurementSystem = GBApplication.getPrefs().getString(SettingsActivity.PREF_MEASUREMENT_SYSTEM, "metric");
|
||||||
|
|
||||||
|
if(!measurementSystem.equals("metric")) {
|
||||||
|
toUnitFactor = 0.621371f;
|
||||||
|
|
||||||
|
speedStringResource = R.string.mi_h;
|
||||||
|
tripStringResource = R.string.label_distance_trip_mph;
|
||||||
|
totalStringResource = R.string.label_distance_total_mph;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private GBDevice getStoredCyclingSensor(){
|
||||||
|
List<GBDevice> devices = GBApplication
|
||||||
|
.app()
|
||||||
|
.getDeviceManager()
|
||||||
|
.getDevices();
|
||||||
|
|
||||||
|
for(GBDevice device: devices) {
|
||||||
|
if (device.getState() != GBDevice.State.INITIALIZED) continue;
|
||||||
|
if (device.getType() != DeviceType.CYCLING_SENSOR) continue;
|
||||||
|
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BroadcastReceiver cyclingDataReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
String deviceAddress = intent.getStringExtra("EXTRA_DEVICE_ADDRESS");
|
||||||
|
|
||||||
|
if(deviceAddress == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!deviceAddress.equals(selectedDevice.getAddress())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CyclingSample sample = (CyclingSample) intent.getSerializableExtra(DeviceService.EXTRA_REALTIME_SAMPLE);
|
||||||
|
|
||||||
|
if(sample == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Float metersPerSecond = sample.getSpeed();
|
||||||
|
if(metersPerSecond != null) {
|
||||||
|
float kmh = metersPerSecond * 3.6f * toUnitFactor;
|
||||||
|
speedView.setText(String.format("%.1f %s", kmh, getString(speedStringResource)));
|
||||||
|
}else{
|
||||||
|
speedView.setText(String.format("%.1f %s", 0f, getString(speedStringResource)));
|
||||||
|
}
|
||||||
|
|
||||||
|
tripCurrentDistance = sample.getDistance();
|
||||||
|
|
||||||
|
tripDistanceView.setText(getString(tripStringResource, ((tripCurrentDistance - tripStartDistance) * toUnitFactor) / 1000));
|
||||||
|
totalDistanceView.setText(getString(totalStringResource, (tripCurrentDistance * toUnitFactor) / 1000));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
LocalBroadcastManager
|
||||||
|
.getInstance(this)
|
||||||
|
.registerReceiver(
|
||||||
|
cyclingDataReceiver,
|
||||||
|
new IntentFilter(DeviceService.ACTION_REALTIME_SAMPLES)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
|
||||||
|
LocalBroadcastManager
|
||||||
|
.getInstance(this)
|
||||||
|
.unregisterReceiver(cyclingDataReceiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -11,6 +11,7 @@ import nodomain.freeyourgadget.gadgetbridge.R;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLEDeviceCoordinator;
|
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLEDeviceCoordinator;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.TimeSampleProvider;
|
import nodomain.freeyourgadget.gadgetbridge.devices.TimeSampleProvider;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.cycling_sensor.activity.CyclingLiveDataActivity;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.cycling_sensor.db.CyclingSampleProvider;
|
import nodomain.freeyourgadget.gadgetbridge.devices.cycling_sensor.db.CyclingSampleProvider;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.entities.CyclingSample;
|
import nodomain.freeyourgadget.gadgetbridge.entities.CyclingSample;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.entities.CyclingSampleDao;
|
import nodomain.freeyourgadget.gadgetbridge.entities.CyclingSampleDao;
|
||||||
@ -80,7 +81,7 @@ public class CyclingSensorCoordinator extends AbstractBLEDeviceCoordinator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<? extends Activity> getAppsManagementActivity() {
|
public Class<? extends Activity> getAppsManagementActivity() {
|
||||||
return null;
|
return CyclingLiveDataActivity.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -110,4 +111,11 @@ public class CyclingSensorCoordinator extends AbstractBLEDeviceCoordinator {
|
|||||||
public int getDeviceNameResource() {
|
public int getDeviceNameResource() {
|
||||||
return R.string.devicetype_cycling_sensor;
|
return R.string.devicetype_cycling_sensor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsAppsManagement(GBDevice device) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.cycling_sensor.support;
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.cycling_sensor.support;
|
||||||
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityKind.CYCLING;
|
|
||||||
|
|
||||||
import android.bluetooth.BluetoothGatt;
|
import android.bluetooth.BluetoothGatt;
|
||||||
import android.bluetooth.BluetoothGattCharacteristic;
|
import android.bluetooth.BluetoothGattCharacteristic;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@ -93,7 +91,7 @@ public class CyclingSensorSupport extends CyclingSensorBaseSupport {
|
|||||||
private long persistenceInterval;
|
private long persistenceInterval;
|
||||||
private long nextPersistenceTimestamp = 0;
|
private long nextPersistenceTimestamp = 0;
|
||||||
|
|
||||||
private float wheelCircumference;
|
private float wheelCircumferenceMeters;
|
||||||
|
|
||||||
private CyclingSpeedCadenceMeasurement lastReportedMeasurement = null;
|
private CyclingSpeedCadenceMeasurement lastReportedMeasurement = null;
|
||||||
private long lastMeasurementTime = 0;
|
private long lastMeasurementTime = 0;
|
||||||
@ -125,7 +123,7 @@ public class CyclingSensorSupport extends CyclingSensorBaseSupport {
|
|||||||
nextPersistenceTimestamp = 0;
|
nextPersistenceTimestamp = 0;
|
||||||
|
|
||||||
float wheelDiameter = deviceSpecificPrefs.getFloat(DeviceSettingsPreferenceConst.PREF_CYCLING_SENSOR_WHEEL_DIAMETER, 29);
|
float wheelDiameter = deviceSpecificPrefs.getFloat(DeviceSettingsPreferenceConst.PREF_CYCLING_SENSOR_WHEEL_DIAMETER, 29);
|
||||||
wheelCircumference = (float)(wheelDiameter * 2.54 * Math.PI) / 100;
|
wheelCircumferenceMeters = (float)(wheelDiameter * 2.54 * Math.PI) / 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -196,13 +194,30 @@ public class CyclingSensorSupport extends CyclingSensorBaseSupport {
|
|||||||
|
|
||||||
float revolutionsPerSecond = revolutionsDelta * (1000f / millisDelta);
|
float revolutionsPerSecond = revolutionsDelta * (1000f / millisDelta);
|
||||||
|
|
||||||
speed = revolutionsPerSecond * wheelCircumference;
|
speed = revolutionsPerSecond * wheelCircumferenceMeters;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lastReportedMeasurement = currentMeasurement;
|
lastReportedMeasurement = currentMeasurement;
|
||||||
lastMeasurementTime = now;
|
lastMeasurementTime = now;
|
||||||
|
|
||||||
|
CyclingSample sample = new CyclingSample();
|
||||||
|
|
||||||
|
if (currentMeasurement.revolutionDataPresent) {
|
||||||
|
sample.setRevolutionCount(currentMeasurement.revolutionCount);
|
||||||
|
sample.setSpeed(speed);
|
||||||
|
sample.setDistance(currentMeasurement.revolutionCount * wheelCircumferenceMeters);
|
||||||
|
}
|
||||||
|
|
||||||
|
sample.setTimestamp(now);
|
||||||
|
|
||||||
|
Intent liveIntent = new Intent(DeviceService.ACTION_REALTIME_SAMPLES);
|
||||||
|
liveIntent.putExtra(DeviceService.EXTRA_REALTIME_SAMPLE, sample);
|
||||||
|
liveIntent.putExtra("EXTRA_DEVICE_ADDRESS", getDevice().getAddress());
|
||||||
|
LocalBroadcastManager.getInstance(getContext())
|
||||||
|
.sendBroadcast(liveIntent);
|
||||||
|
|
||||||
|
|
||||||
if(now < nextPersistenceTimestamp){
|
if(now < nextPersistenceTimestamp){
|
||||||
// too early
|
// too early
|
||||||
return;
|
return;
|
||||||
@ -210,21 +225,6 @@ public class CyclingSensorSupport extends CyclingSensorBaseSupport {
|
|||||||
|
|
||||||
nextPersistenceTimestamp = now + persistenceInterval;
|
nextPersistenceTimestamp = now + persistenceInterval;
|
||||||
|
|
||||||
CyclingSample sample = new CyclingSample();
|
|
||||||
|
|
||||||
if (currentMeasurement.revolutionDataPresent) {
|
|
||||||
sample.setRevolutionCount(currentMeasurement.revolutionCount);
|
|
||||||
sample.setSpeed(speed);
|
|
||||||
sample.setDistance(currentMeasurement.revolutionCount * wheelCircumference);
|
|
||||||
}
|
|
||||||
|
|
||||||
sample.setTimestamp(now);
|
|
||||||
|
|
||||||
Intent liveIntent = new Intent(DeviceService.ACTION_REALTIME_SAMPLES);
|
|
||||||
liveIntent.putExtra(DeviceService.EXTRA_REALTIME_SAMPLE, sample);
|
|
||||||
LocalBroadcastManager.getInstance(getContext())
|
|
||||||
.sendBroadcast(liveIntent);
|
|
||||||
|
|
||||||
try(DBHandler handler = GBApplication.acquireDB()) {
|
try(DBHandler handler = GBApplication.acquireDB()) {
|
||||||
DaoSession session = handler.getDaoSession();
|
DaoSession session = handler.getDaoSession();
|
||||||
|
|
||||||
|
51
app/src/main/res/layout/activity_cycling_live_data.xml
Normal file
51
app/src/main/res/layout/activity_cycling_live_data.xml
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?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="match_parent"
|
||||||
|
android:orientation="vertical" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/cycling_data_speed"
|
||||||
|
android:textSize="@dimen/um25_value_text_size"
|
||||||
|
android:layout_marginTop="100dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textColor="@color/primary_dark"
|
||||||
|
android:text="-"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/cycling_data_trip_distance"
|
||||||
|
android:textSize="@dimen/um25_value_text_size"
|
||||||
|
android:layout_marginTop="100dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textColor="@color/primary_dark"
|
||||||
|
android:text="-"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="@dimen/um25_value_text_size_small"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textColor="@color/secondarytext"
|
||||||
|
android:text="Long press to zero"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/cycling_data_total_distance"
|
||||||
|
android:textSize="@dimen/um25_value_text_size_small"
|
||||||
|
android:layout_marginTop="100dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textColor="@color/secondarytext"
|
||||||
|
android:text="-"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -3296,4 +3296,9 @@
|
|||||||
<string name="backup_restore_abort_import_confirmation">Abort the import? This may lead to a corrupted or inconsistent database.</string>
|
<string name="backup_restore_abort_import_confirmation">Abort the import? This may lead to a corrupted or inconsistent database.</string>
|
||||||
<string name="backup_restore_restart_title">Restart</string>
|
<string name="backup_restore_restart_title">Restart</string>
|
||||||
<string name="backup_restore_restart_summary">%1s will now restart.</string>
|
<string name="backup_restore_restart_summary">%1s will now restart.</string>
|
||||||
|
<string name="label_distance_trip">Trip: %.1f km</string>
|
||||||
|
<string name="label_distance_total">Total: %.1f km</string>
|
||||||
|
<string name="label_distance_trip_mph">Trip: %.1f mi</string>
|
||||||
|
<string name="label_distance_total_mph">Total: %.1f mi</string>
|
||||||
|
<string name="error_no_cycling_sensor_found">no cycling sensor found</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
Reference in New Issue
Block a user