mirror of
https://github.com/oliexdev/openScale.git
synced 2025-09-01 20:33:31 +02:00
Merge branch 'master' of https://github.com/oliexdev/openScale
This commit is contained in:
@@ -21,7 +21,7 @@ before_script:
|
||||
- sed -i -r
|
||||
-e 's/applicationId "[^"]+/\0.dev/'
|
||||
-e 's/(versionCode ).*/\1'$(date +%s)'/'
|
||||
-e 's/versionName "[^"]+/\0-dev_'${TRAVIS_COMMIT:0:8}'/'
|
||||
-e 's/versionName "[^"]+/\0-dev_'${TRAVIS_COMMIT:0:8}_$(date +%F)'/'
|
||||
android_app/app/build.gradle
|
||||
- sed -i -r
|
||||
-e 's/(<string name="app_name"[^>]*>[^<]+)/\1 (dev)/'
|
||||
|
@@ -21,8 +21,8 @@ Install [openScale-dev-build.apk](https://github.com/oliexdev/openScale/releases
|
||||
- Import or export your data from/into a CSV file
|
||||
- Estimates body metrics (body fat, body water and lean body mass) based on scientic publications
|
||||
- Support for multiple users
|
||||
- Partially or full support for custom made Bluetooth scale, Xiaomi Mi scale v1/v2, Sanitas SBF70, Medisana BS444/BS440, Digoo DG-S038H, Yunmai Mini, Excelvan CF369BLE, Yunmai SE, MGB, Exingtech Y1, Beurer BF700/BF710/BF800, Silvercrest SBF75, Runtastic Libra (see [openScale wiki](https://github.com/oliexdev/openScale/wiki/Supported-scales-in-openScale) for details)
|
||||
- Partially or full translated into Brazilian Portuguese, Catalan, Chinese (traditional), Czech, Dutch, English, French, Galician, German, Greek, Italian, Japanese, Norwegian Bokmål, Polish, Romanian, Russian, Slovak, Spanish, Swedish, Turkish
|
||||
- Partially or full support for custom made Bluetooth scale, Xiaomi Mi scale v1/v2, Sanitas SBF70, Medisana BS444/BS440, Digoo DG-S038H, Yunmai Mini, Excelvan CF369BLE/CF366BLE, Yunmai SE, MGB, Exingtech Y1, Beurer BF700/BF710/BF800, Silvercrest SBF75, Runtastic Libra, Hesley (Yunchen), iHealth HS3, Easy Home 64050, Accuway (see [openScale wiki](https://github.com/oliexdev/openScale/wiki/Supported-scales-in-openScale) for details)
|
||||
- Partially or full translated into Brazilian Portuguese, Catalan, Chinese (traditional), Croatian, Czech, Danish, Dutch, English, French, Galician, German, Greek, Italian, Japanese, Norwegian Bokmål, Polish, Romanian, Russian, Slovak, Slovenian, Spanish, Swedish, Turkish
|
||||
- No advertising and for free
|
||||
- All data belongs to you (no cloud service)
|
||||
|
||||
|
@@ -42,6 +42,7 @@ dependencies {
|
||||
implementation "com.android.support:design:${supportLibVersion}"
|
||||
implementation "com.android.support:support-v4:${supportLibVersion}"
|
||||
implementation "com.android.support:appcompat-v7:${supportLibVersion}"
|
||||
implementation "com.android.support:recyclerview-v7:${supportLibVersion}"
|
||||
|
||||
// HelloCharts
|
||||
implementation 'com.github.lecho:hellocharts-library:1.5.8@aar'
|
||||
|
@@ -21,6 +21,7 @@ import com.health.openscale.BuildConfig;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class Application extends android.app.Application {
|
||||
OpenScale openScale;
|
||||
|
||||
private class TimberLogAdapter extends Timber.DebugTree {
|
||||
@Override
|
||||
@@ -40,5 +41,8 @@ public class Application extends android.app.Application {
|
||||
|
||||
// Create OpenScale instance
|
||||
OpenScale.createInstance(getApplicationContext());
|
||||
|
||||
// Hold on to the instance for as long as the application exists
|
||||
openScale = OpenScale.getInstance();
|
||||
}
|
||||
}
|
||||
|
@@ -28,8 +28,9 @@ import com.health.openscale.core.OpenScale;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
@@ -95,7 +96,8 @@ public class AlarmBackupHandler
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
if (!prefs.getBoolean("overwriteBackup", false)) {
|
||||
databaseName = DateFormat.getDateInstance(DateFormat.SHORT).format(new Date()) + "_" + databaseName;
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
|
||||
databaseName = dateFormat.format(new Date()) + "_" + databaseName;
|
||||
}
|
||||
|
||||
File exportFile = new File(exportDir, databaseName);
|
||||
|
@@ -30,6 +30,7 @@ import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.SystemClock;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
|
||||
import com.health.openscale.core.datatypes.ScaleMeasurement;
|
||||
@@ -49,9 +50,12 @@ public abstract class BluetoothCommunication {
|
||||
|
||||
public enum BT_MACHINE_STATE {BT_INIT_STATE, BT_CMD_STATE, BT_CLEANUP_STATE}
|
||||
|
||||
private static final long LE_SCAN_TIMEOUT_MS = 10 * 1000;
|
||||
|
||||
protected Context context;
|
||||
|
||||
private Handler callbackBtHandler;
|
||||
private Handler handler;
|
||||
private BluetoothGatt bluetoothGatt;
|
||||
private boolean connectionEstablished;
|
||||
private BluetoothGattCallback gattCallback;
|
||||
@@ -81,6 +85,7 @@ public abstract class BluetoothCommunication {
|
||||
public BluetoothCommunication(Context context)
|
||||
{
|
||||
this.context = context;
|
||||
handler = new Handler();
|
||||
btAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
gattCallback = new GattCallback();
|
||||
bluetoothGatt = null;
|
||||
@@ -96,8 +101,8 @@ public abstract class BluetoothCommunication {
|
||||
return bluetoothGatt.getServices();
|
||||
}
|
||||
|
||||
protected boolean discoverDeviceBeforeConnecting() {
|
||||
return false;
|
||||
protected boolean hasBluetoothGattService(UUID service) {
|
||||
return bluetoothGatt != null && bluetoothGatt.getService(service) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,7 +130,10 @@ public abstract class BluetoothCommunication {
|
||||
* @param infoText the information text that is displayed to the status code.
|
||||
*/
|
||||
protected void setBtStatus(BT_STATUS_CODE statusCode, String infoText) {
|
||||
callbackBtHandler.obtainMessage(statusCode.ordinal(), infoText).sendToTarget();
|
||||
if (callbackBtHandler != null) {
|
||||
callbackBtHandler.obtainMessage(
|
||||
statusCode.ordinal(), infoText).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,7 +142,10 @@ public abstract class BluetoothCommunication {
|
||||
* @param scaleMeasurement the scale data that should be added to openScale
|
||||
*/
|
||||
protected void addScaleData(ScaleMeasurement scaleMeasurement) {
|
||||
callbackBtHandler.obtainMessage(BT_STATUS_CODE.BT_RETRIEVE_SCALE_DATA.ordinal(), scaleMeasurement).sendToTarget();
|
||||
if (callbackBtHandler != null) {
|
||||
callbackBtHandler.obtainMessage(
|
||||
BT_STATUS_CODE.BT_RETRIEVE_SCALE_DATA.ordinal(), scaleMeasurement).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,7 +155,10 @@ public abstract class BluetoothCommunication {
|
||||
* @param value the value to be used
|
||||
*/
|
||||
protected void sendMessage(int msg, Object value) {
|
||||
callbackBtHandler.obtainMessage(BT_STATUS_CODE.BT_SCALE_MESSAGE.ordinal(), msg, 0, value).sendToTarget();
|
||||
if (callbackBtHandler != null) {
|
||||
callbackBtHandler.obtainMessage(
|
||||
BT_STATUS_CODE.BT_SCALE_MESSAGE.ordinal(), msg, 0, value).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -261,6 +275,7 @@ public abstract class BluetoothCommunication {
|
||||
BluetoothGattCharacteristic gattCharacteristic = bluetoothGatt.getService(service)
|
||||
.getCharacteristic(characteristic);
|
||||
|
||||
Timber.d("Read characteristic %s", characteristic);
|
||||
bluetoothGatt.readCharacteristic(gattCharacteristic);
|
||||
}
|
||||
|
||||
@@ -268,6 +283,7 @@ public abstract class BluetoothCommunication {
|
||||
BluetoothGattDescriptor gattDescriptor = bluetoothGatt.getService(service)
|
||||
.getCharacteristic(characteristic).getDescriptor(descriptor);
|
||||
|
||||
Timber.d("Read descriptor %s", descriptor);
|
||||
bluetoothGatt.readDescriptor(gattDescriptor);
|
||||
}
|
||||
|
||||
@@ -280,6 +296,7 @@ public abstract class BluetoothCommunication {
|
||||
protected void setIndicationOn(UUID service, UUID characteristic, UUID descriptor) {
|
||||
Timber.d("Set indication on for %s", characteristic);
|
||||
|
||||
try {
|
||||
BluetoothGattCharacteristic gattCharacteristic =
|
||||
bluetoothGatt.getService(service).getCharacteristic(characteristic);
|
||||
bluetoothGatt.setCharacteristicNotification(gattCharacteristic, true);
|
||||
@@ -292,6 +309,10 @@ public abstract class BluetoothCommunication {
|
||||
handleRequests();
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
Timber.e(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set notification flag on for the Bluetooth device.
|
||||
@@ -302,6 +323,7 @@ public abstract class BluetoothCommunication {
|
||||
protected void setNotificationOn(UUID service, UUID characteristic, UUID descriptor) {
|
||||
Timber.d("Set notification on for %s", characteristic);
|
||||
|
||||
try {
|
||||
BluetoothGattCharacteristic gattCharacteristic =
|
||||
bluetoothGatt.getService(service).getCharacteristic(characteristic);
|
||||
bluetoothGatt.setCharacteristicNotification(gattCharacteristic, true);
|
||||
@@ -314,6 +336,10 @@ public abstract class BluetoothCommunication {
|
||||
handleRequests();
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
Timber.e(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set notification flag off for the Bluetooth device.
|
||||
@@ -349,12 +375,16 @@ public abstract class BluetoothCommunication {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (data.length == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
final StringBuilder stringBuilder = new StringBuilder(3 * data.length);
|
||||
for (byte byteChar : data) {
|
||||
stringBuilder.append(String.format("%02X ", byteChar));
|
||||
}
|
||||
|
||||
return stringBuilder.toString();
|
||||
return stringBuilder.substring(0, stringBuilder.length() - 1);
|
||||
}
|
||||
|
||||
protected byte xorChecksum(byte[] data, int offset, int length) {
|
||||
@@ -384,9 +414,33 @@ public abstract class BluetoothCommunication {
|
||||
*
|
||||
* @param hwAddress the Bluetooth address to connect to
|
||||
*/
|
||||
public void connect(final String hwAddress) {
|
||||
Timber.i("Connecting to [%s] (driver: %s)", hwAddress, driverName());
|
||||
public void connect(String hwAddress) {
|
||||
logBluetoothStatus();
|
||||
|
||||
// Some good tips to improve BLE connections:
|
||||
// https://android.jlelse.eu/lessons-for-first-time-android-bluetooth-le-developers-i-learned-the-hard-way-fee07646624
|
||||
|
||||
btAdapter.cancelDiscovery();
|
||||
stopLeScan();
|
||||
|
||||
// Don't do any cleanup if disconnected before fully connected
|
||||
btMachineState = BT_MACHINE_STATE.BT_CLEANUP_STATE;
|
||||
|
||||
// Running an LE scan during connect improves connectivity on some phones
|
||||
// (e.g. Sony Xperia Z5 compact, Android 7.1.1). For some scales (e.g. Medisana BS444)
|
||||
// it seems to be a requirement that the scale is discovered before connecting to it.
|
||||
// Otherwise the connection almost never succeeds.
|
||||
if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION)
|
||||
== PackageManager.PERMISSION_GRANTED) {
|
||||
startLeScanForDevice(hwAddress);
|
||||
}
|
||||
else {
|
||||
Timber.d("No coarse location permission, connecting without LE scan");
|
||||
connectGatt(hwAddress);
|
||||
}
|
||||
}
|
||||
|
||||
private void logBluetoothStatus() {
|
||||
Timber.d("BT is%s enabled, state=%d, scan mode=%d, is%s discovering",
|
||||
btAdapter.isEnabled() ? "" : " not", btAdapter.getState(),
|
||||
btAdapter.getScanMode(), btAdapter.isDiscovering() ? "" : " not");
|
||||
@@ -401,47 +455,11 @@ public abstract class BluetoothCommunication {
|
||||
Timber.d("Connected GATT_SERVER device: %s [%s]",
|
||||
device.getName(), device.getAddress());
|
||||
}
|
||||
|
||||
// Some good tips to improve BLE connections:
|
||||
// https://android.jlelse.eu/lessons-for-first-time-android-bluetooth-le-developers-i-learned-the-hard-way-fee07646624
|
||||
|
||||
final boolean doDiscoveryFirst = discoverDeviceBeforeConnecting();
|
||||
|
||||
// Running an LE scan during connect improves connectivity on some phones
|
||||
// (e.g. Sony Xperia Z5 compact, Android 7.1.1).
|
||||
btAdapter.cancelDiscovery();
|
||||
if (leScanCallback == null) {
|
||||
if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION)
|
||||
== PackageManager.PERMISSION_GRANTED) {
|
||||
Timber.d("Starting LE scan");
|
||||
leScanCallback = new BluetoothAdapter.LeScanCallback() {
|
||||
@Override
|
||||
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
|
||||
Timber.d("Found LE device %s [%s]", device.getName(), device.getAddress());
|
||||
if (!doDiscoveryFirst || !device.getAddress().equals(hwAddress)) {
|
||||
return;
|
||||
}
|
||||
synchronized (lock) {
|
||||
connectGatt(device);
|
||||
}
|
||||
}
|
||||
};
|
||||
btAdapter.startLeScan(leScanCallback);
|
||||
}
|
||||
else {
|
||||
Timber.d("No coarse location permission, skipping LE scan");
|
||||
}
|
||||
}
|
||||
|
||||
// Don't do any cleanup if disconnected before fully connected
|
||||
btMachineState = BT_MACHINE_STATE.BT_CLEANUP_STATE;
|
||||
|
||||
if (!doDiscoveryFirst || leScanCallback == null) {
|
||||
connectGatt(btAdapter.getRemoteDevice(hwAddress));
|
||||
}
|
||||
}
|
||||
|
||||
private void connectGatt(BluetoothDevice device) {
|
||||
Timber.i("Connecting to [%s] (driver: %s)", device.getAddress(), driverName());
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
bluetoothGatt = device.connectGatt(
|
||||
context, false, gattCallback, BluetoothDevice.TRANSPORT_LE);
|
||||
@@ -451,15 +469,55 @@ public abstract class BluetoothCommunication {
|
||||
}
|
||||
}
|
||||
|
||||
private void connectGatt(String hwAddress) {
|
||||
connectGatt(btAdapter.getRemoteDevice(hwAddress));
|
||||
}
|
||||
|
||||
private void startLeScanForDevice(final String hwAddress) {
|
||||
leScanCallback = new BluetoothAdapter.LeScanCallback() {
|
||||
@Override
|
||||
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
|
||||
Timber.d("Found LE device %s [%s]", device.getName(), device.getAddress());
|
||||
if (!device.getAddress().equals(hwAddress)) {
|
||||
return;
|
||||
}
|
||||
synchronized (lock) {
|
||||
stopLeScan();
|
||||
connectGatt(device);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Timber.d("Starting LE scan for device [%s]", hwAddress);
|
||||
btAdapter.startLeScan(leScanCallback);
|
||||
|
||||
handler.postAtTime(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Timber.d("Device not found in LE scan, connecting directly");
|
||||
synchronized (lock) {
|
||||
stopLeScan();
|
||||
connectGatt(hwAddress);
|
||||
}
|
||||
}
|
||||
}, leScanCallback, SystemClock.uptimeMillis() + LE_SCAN_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
private void stopLeScan() {
|
||||
if (leScanCallback != null) {
|
||||
Timber.d("Stopping LE scan");
|
||||
btAdapter.stopLeScan(leScanCallback);
|
||||
handler.removeCallbacksAndMessages(leScanCallback);
|
||||
leScanCallback = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from a Bluetooth device
|
||||
*/
|
||||
public void disconnect(boolean doCleanup) {
|
||||
synchronized (lock) {
|
||||
if (leScanCallback != null) {
|
||||
btAdapter.stopLeScan(leScanCallback);
|
||||
leScanCallback = null;
|
||||
}
|
||||
stopLeScan();
|
||||
|
||||
if (bluetoothGatt == null) {
|
||||
return;
|
||||
@@ -467,17 +525,34 @@ public abstract class BluetoothCommunication {
|
||||
|
||||
Timber.i("Disconnecting%s", doCleanup ? " (with cleanup)" : "");
|
||||
|
||||
handler.removeCallbacksAndMessages(null);
|
||||
callbackBtHandler = null;
|
||||
|
||||
if (doCleanup) {
|
||||
if (btMachineState != BT_MACHINE_STATE.BT_CLEANUP_STATE) {
|
||||
setBtMachineState(BT_MACHINE_STATE.BT_CLEANUP_STATE);
|
||||
nextMachineStateStep();
|
||||
}
|
||||
}
|
||||
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (lock) {
|
||||
if (openRequest) {
|
||||
handler.postDelayed(this, 10);
|
||||
} else {
|
||||
bluetoothGatt.close();
|
||||
bluetoothGatt = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
bluetoothGatt.close();
|
||||
bluetoothGatt = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke next step for internal Bluetooth state machine.
|
||||
@@ -509,6 +584,8 @@ public abstract class BluetoothCommunication {
|
||||
synchronized (lock) {
|
||||
// check for pending request
|
||||
if (openRequest) {
|
||||
Timber.d("Request pending (queue %d %d)",
|
||||
descriptorRequestQueue.size(), characteristicRequestQueue.size());
|
||||
return; // yes, do nothing
|
||||
}
|
||||
|
||||
@@ -517,8 +594,9 @@ public abstract class BluetoothCommunication {
|
||||
if (descriptor != null) {
|
||||
descriptor.gattObject.setValue(descriptor.value);
|
||||
|
||||
Timber.d("Write descriptor %s: %s",
|
||||
descriptor.gattObject.getUuid(), byteInHex(descriptor.gattObject.getValue()));
|
||||
Timber.d("Write descriptor %s: %s (queue: %d %d)",
|
||||
descriptor.gattObject.getUuid(), byteInHex(descriptor.gattObject.getValue()),
|
||||
descriptorRequestQueue.size(), characteristicRequestQueue.size());
|
||||
if (!bluetoothGatt.writeDescriptor(descriptor.gattObject)) {
|
||||
Timber.e("Failed to initiate write of descriptor %s",
|
||||
descriptor.gattObject.getUuid());
|
||||
@@ -532,8 +610,9 @@ public abstract class BluetoothCommunication {
|
||||
if (characteristic != null) {
|
||||
characteristic.gattObject.setValue(characteristic.value);
|
||||
|
||||
Timber.d("Write characteristic %s: %s",
|
||||
characteristic.gattObject.getUuid(), byteInHex(characteristic.gattObject.getValue()));
|
||||
Timber.d("Write characteristic %s: %s (queue: %d %d)",
|
||||
characteristic.gattObject.getUuid(), byteInHex(characteristic.gattObject.getValue()),
|
||||
descriptorRequestQueue.size(), characteristicRequestQueue.size());
|
||||
if (!bluetoothGatt.writeCharacteristic(characteristic.gattObject)) {
|
||||
Timber.e("Failed to initiate write of characteristic %s",
|
||||
characteristic.gattObject.getUuid());
|
||||
@@ -557,10 +636,7 @@ public abstract class BluetoothCommunication {
|
||||
|
||||
if (newState == BluetoothProfile.STATE_CONNECTED) {
|
||||
synchronized (lock) {
|
||||
if (leScanCallback != null) {
|
||||
btAdapter.stopLeScan(leScanCallback);
|
||||
leScanCallback = null;
|
||||
}
|
||||
stopLeScan();
|
||||
}
|
||||
|
||||
connectionEstablished = true;
|
||||
@@ -588,7 +664,8 @@ public abstract class BluetoothCommunication {
|
||||
|
||||
@Override
|
||||
public void onServicesDiscovered(final BluetoothGatt gatt, int status) {
|
||||
Timber.d("onServicesDiscovered: status=%d", status);
|
||||
Timber.d("onServicesDiscovered: status=%d (%d services)",
|
||||
status, gatt.getServices().size());
|
||||
|
||||
synchronized (lock) {
|
||||
cmdStepNr = 0;
|
||||
@@ -615,24 +692,32 @@ public abstract class BluetoothCommunication {
|
||||
setBtMachineState(BT_MACHINE_STATE.BT_INIT_STATE);
|
||||
}
|
||||
|
||||
private void postDelayedHandleRequests() {
|
||||
// Wait a short while before starting the next operation as suggested
|
||||
// on the android.jlelse.eu link above.
|
||||
handler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void onDescriptorWrite(BluetoothGatt gatt,
|
||||
BluetoothGattDescriptor descriptor,
|
||||
int status) {
|
||||
public void run() {
|
||||
synchronized (lock) {
|
||||
openRequest = false;
|
||||
handleRequests();
|
||||
}
|
||||
}
|
||||
}, 60);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDescriptorWrite(BluetoothGatt gatt,
|
||||
BluetoothGattDescriptor descriptor,
|
||||
int status) {
|
||||
postDelayedHandleRequests();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCharacteristicWrite(BluetoothGatt gatt,
|
||||
BluetoothGattCharacteristic characteristic,
|
||||
int status) {
|
||||
synchronized (lock) {
|
||||
openRequest = false;
|
||||
handleRequests();
|
||||
}
|
||||
postDelayedHandleRequests();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -644,8 +729,7 @@ public abstract class BluetoothCommunication {
|
||||
|
||||
synchronized (lock) {
|
||||
onBluetoothDataRead(gatt, characteristic, status);
|
||||
openRequest = false;
|
||||
handleRequests();
|
||||
postDelayedHandleRequests();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -667,10 +751,7 @@ public abstract class BluetoothCommunication {
|
||||
Timber.d("onDescriptorRead %s (status=%d): %s",
|
||||
descriptor.getUuid(), status, byteInHex(descriptor.getValue()));
|
||||
|
||||
synchronized (lock) {
|
||||
openRequest = false;
|
||||
handleRequests();
|
||||
}
|
||||
postDelayedHandleRequests();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -15,28 +15,26 @@
|
||||
*/
|
||||
package com.health.openscale.core.bluetooth;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothSocket;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.content.Context;
|
||||
|
||||
import com.health.openscale.core.datatypes.ScaleMeasurement;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class BluetoothCustomOpenScale extends BluetoothCommunication {
|
||||
private final UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"); // Standard SerialPortService ID
|
||||
private final UUID WEIGHT_MEASUREMENT_SERVICE = UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb");
|
||||
private final UUID WEIGHT_MEASUREMENT_CHARACTERISTIC = UUID.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"); // Bluetooth Modul HM-10
|
||||
private final UUID WEIGHT_MEASUREMENT_CONFIG = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
|
||||
|
||||
private BluetoothSocket btSocket = null;
|
||||
private BluetoothDevice btDevice = null;
|
||||
|
||||
private BluetoothConnectedThread btConnectThread = null;
|
||||
private String string_data = new String();
|
||||
|
||||
public BluetoothCustomOpenScale(Context context) {
|
||||
super(context);
|
||||
@@ -44,14 +42,35 @@ public class BluetoothCustomOpenScale extends BluetoothCommunication {
|
||||
|
||||
@Override
|
||||
public String driverName() {
|
||||
return "Custom Open Scale";
|
||||
return "Custom openScale";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextInitCmd(int stateNr) {
|
||||
switch (stateNr) {
|
||||
case 0:
|
||||
setNotificationOn(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_CHARACTERISTIC, WEIGHT_MEASUREMENT_CONFIG);
|
||||
break;
|
||||
case 1:
|
||||
Calendar cal = Calendar.getInstance();
|
||||
|
||||
String date_time = String.format(Locale.US, "2%1d,%1d,%1d,%1d,%1d,%1d,",
|
||||
cal.get(Calendar.YEAR)-2000,
|
||||
cal.get(Calendar.MONTH) + 1,
|
||||
cal.get(Calendar.DAY_OF_MONTH),
|
||||
cal.get(Calendar.HOUR_OF_DAY),
|
||||
cal.get(Calendar.MINUTE),
|
||||
cal.get(Calendar.SECOND));
|
||||
|
||||
writeBytes(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_CHARACTERISTIC, date_time.getBytes());
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextBluetoothCmd(int stateNr) {
|
||||
return false;
|
||||
@@ -62,153 +81,51 @@ public class BluetoothCustomOpenScale extends BluetoothCommunication {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(String hwAddress) {
|
||||
|
||||
if (btAdapter == null) {
|
||||
setBtStatus(BT_STATUS_CODE.BT_NO_DEVICE_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
btDevice = btAdapter.getRemoteDevice(hwAddress);
|
||||
try {
|
||||
// Get a BluetoothSocket to connect with the given BluetoothDevice
|
||||
btSocket = btDevice.createRfcommSocketToServiceRecord(uuid);
|
||||
} catch (IOException e) {
|
||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Can't get a bluetooth socket");
|
||||
btDevice = null;
|
||||
return;
|
||||
}
|
||||
|
||||
Thread socketThread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (!btSocket.isConnected()) {
|
||||
// Connect the device through the socket. This will block
|
||||
// until it succeeds or throws an exception
|
||||
btSocket.connect();
|
||||
|
||||
// Bluetooth connection was successful
|
||||
setBtStatus(BT_STATUS_CODE.BT_CONNECTION_ESTABLISHED);
|
||||
|
||||
btConnectThread = new BluetoothConnectedThread();
|
||||
btConnectThread.start();
|
||||
}
|
||||
} catch (IOException connectException) {
|
||||
// Unable to connect; close the socket and get out
|
||||
disconnect(false);
|
||||
setBtStatus(BT_STATUS_CODE.BT_NO_DEVICE_FOUND);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
socketThread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(boolean doCleanup) {
|
||||
if (btSocket != null) {
|
||||
if (btSocket.isConnected()) {
|
||||
try {
|
||||
btSocket.close();
|
||||
btSocket = null;
|
||||
} catch (IOException closeException) {
|
||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Can't close bluetooth socket");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (btConnectThread != null) {
|
||||
btConnectThread.cancel();
|
||||
btConnectThread = null;
|
||||
}
|
||||
|
||||
btDevice = null;
|
||||
}
|
||||
|
||||
public void clearEEPROM()
|
||||
{
|
||||
sendBtData("9");
|
||||
byte[] cmd = {(byte)'9'};
|
||||
writeBytes(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_CHARACTERISTIC, cmd);
|
||||
}
|
||||
|
||||
private boolean sendBtData(String data) {
|
||||
if (btSocket.isConnected()) {
|
||||
btConnectThread = new BluetoothConnectedThread();
|
||||
btConnectThread.write(data.getBytes());
|
||||
@Override
|
||||
public void onBluetoothDataChange(BluetoothGatt bluetoothGatt, BluetoothGattCharacteristic gattCharacteristic) {
|
||||
final byte[] data = gattCharacteristic.getValue();
|
||||
|
||||
btConnectThread.cancel();
|
||||
return true;
|
||||
if (data != null) {
|
||||
for (byte character : data) {
|
||||
string_data += (char) (character & 0xFF);
|
||||
|
||||
if (character == '\n') {
|
||||
parseBtString(string_data);
|
||||
string_data = new String();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private class BluetoothConnectedThread extends Thread {
|
||||
private InputStream btInStream;
|
||||
private OutputStream btOutStream;
|
||||
private volatile boolean isCancel;
|
||||
|
||||
public BluetoothConnectedThread() {
|
||||
|
||||
isCancel = false;
|
||||
|
||||
// Get the input and output bluetooth streams
|
||||
try {
|
||||
btInStream = btSocket.getInputStream();
|
||||
btOutStream = btSocket.getOutputStream();
|
||||
} catch (IOException e) {
|
||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Can't get bluetooth input or output stream " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
final StringBuilder btLine = new StringBuilder();
|
||||
|
||||
// Keep listening to the InputStream until an exception occurs (e.g. device partner goes offline)
|
||||
while (!isCancel) {
|
||||
try {
|
||||
// stream read is a blocking method
|
||||
char btChar = (char) btInStream.read();
|
||||
|
||||
btLine.append(btChar);
|
||||
|
||||
if (btLine.charAt(btLine.length() - 1) == '\n') {
|
||||
ScaleMeasurement scaleMeasurement = parseBtString(btLine.toString());
|
||||
|
||||
if (scaleMeasurement != null) {
|
||||
addScaleData(scaleMeasurement);
|
||||
}
|
||||
|
||||
btLine.setLength(0);
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
cancel();
|
||||
setBtStatus(BT_STATUS_CODE.BT_CONNECTION_LOST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ScaleMeasurement parseBtString(String btString) throws IOException {
|
||||
ScaleMeasurement scaleBtData = new ScaleMeasurement();
|
||||
private void parseBtString(String btString) {
|
||||
btString = btString.substring(0, btString.length() - 1); // delete newline '\n' of the string
|
||||
|
||||
if (btString.charAt(0) != '$' && btString.charAt(2) != '$') {
|
||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Parse error of bluetooth string. String has not a valid format");
|
||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Parse error of bluetooth string. String has not a valid format: " + btString);
|
||||
}
|
||||
|
||||
String btMsg = btString.substring(3, btString.length()); // message string
|
||||
|
||||
switch (btString.charAt(1)) {
|
||||
case 'I':
|
||||
Timber.i("MCU Information: %s", btMsg);
|
||||
Timber.d("MCU Information: %s", btMsg);
|
||||
break;
|
||||
case 'E':
|
||||
Timber.e("MCU Error: %s", btMsg);
|
||||
break;
|
||||
case 'S':
|
||||
Timber.i("MCU stored data size: %s", btMsg);
|
||||
Timber.d("MCU stored data size: %s", btMsg);
|
||||
break;
|
||||
case 'F':
|
||||
Timber.d("All data sent");
|
||||
clearEEPROM();
|
||||
disconnect(false);
|
||||
break;
|
||||
case 'D':
|
||||
String[] csvField = btMsg.split(",");
|
||||
@@ -230,8 +147,8 @@ public class BluetoothCustomOpenScale extends BluetoothCommunication {
|
||||
int btChecksum = Integer.parseInt(csvField[10]);
|
||||
|
||||
if (checksum == btChecksum) {
|
||||
scaleBtData.setId(-1);
|
||||
scaleBtData.setUserId(Integer.parseInt(csvField[0]));
|
||||
ScaleMeasurement scaleBtData = new ScaleMeasurement();
|
||||
|
||||
String date_string = csvField[1] + "/" + csvField[2] + "/" + csvField[3] + "/" + csvField[4] + "/" + csvField[5];
|
||||
scaleBtData.setDateTime(new SimpleDateFormat("yyyy/MM/dd/HH/mm").parse(date_string));
|
||||
|
||||
@@ -240,7 +157,7 @@ public class BluetoothCustomOpenScale extends BluetoothCommunication {
|
||||
scaleBtData.setWater(Float.parseFloat(csvField[8]));
|
||||
scaleBtData.setMuscle(Float.parseFloat(csvField[9]));
|
||||
|
||||
return scaleBtData;
|
||||
addScaleData(scaleBtData);
|
||||
} else {
|
||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error calculated checksum (" + checksum + ") and received checksum (" + btChecksum + ") is different");
|
||||
}
|
||||
@@ -251,22 +168,7 @@ public class BluetoothCustomOpenScale extends BluetoothCommunication {
|
||||
}
|
||||
break;
|
||||
default:
|
||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error unknown MCU command");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void write(byte[] bytes) {
|
||||
try {
|
||||
btOutStream.write(bytes);
|
||||
} catch (IOException e) {
|
||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error while writing to bluetooth socket " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
isCancel = true;
|
||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error unknown MCU command : " + btString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -156,8 +156,8 @@ public class BluetoothDebug extends BluetoothCommunication {
|
||||
logService(service, false);
|
||||
}
|
||||
|
||||
disconnect(false);
|
||||
setBtStatus(BT_STATUS_CODE.BT_CONNECTION_LOST);
|
||||
disconnect(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -29,7 +29,7 @@ import com.health.openscale.core.utils.Converters;
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
|
||||
public class BluetoothExcelvanCF369BLE extends BluetoothCommunication {
|
||||
public class BluetoothExcelvanCF36xBLE extends BluetoothCommunication {
|
||||
private final UUID WEIGHT_MEASUREMENT_SERVICE = UUID.fromString("0000FFF0-0000-1000-8000-00805f9b34fb");
|
||||
private final UUID WEIGHT_MEASUREMENT_CHARACTERISTIC = UUID.fromString("0000FFF1-0000-1000-8000-00805f9b34fb");
|
||||
private final UUID WEIGHT_CUSTOM0_CHARACTERISTIC = UUID.fromString("0000FFF4-0000-1000-8000-00805f9b34fb");
|
||||
@@ -37,13 +37,13 @@ public class BluetoothExcelvanCF369BLE extends BluetoothCommunication {
|
||||
|
||||
private byte[] receivedData = new byte[]{};
|
||||
|
||||
public BluetoothExcelvanCF369BLE(Context context) {
|
||||
public BluetoothExcelvanCF36xBLE(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String driverName() {
|
||||
return "Excelvan CF369BLE";
|
||||
return "Excelvan CF36xBLE";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -117,8 +117,10 @@ public class BluetoothExcelvanCF369BLE extends BluetoothCommunication {
|
||||
|
||||
if (data != null && data.length > 0) {
|
||||
|
||||
// if data is body scale type
|
||||
if (data.length == 16 && data[0] == (byte)0xcf) {
|
||||
// if data is body scale type. At least some variants (e.g. CF366BLE) of this scale
|
||||
// return a 17th byte representing "physiological age". Allow (but ignore) that byte
|
||||
// to support those variants.
|
||||
if ((data.length >= 16 && data.length <= 17) && data[0] == (byte)0xcf) {
|
||||
if (!Arrays.equals(data, receivedData)) { // accepts only one data of the same content
|
||||
receivedData = data;
|
||||
parseBytes(data);
|
||||
@@ -135,6 +137,7 @@ public class BluetoothExcelvanCF369BLE extends BluetoothCommunication {
|
||||
float visceralFat = weightBytes[11] & 0xFF;
|
||||
float water = Converters.fromUnsignedInt16Be(weightBytes, 12) / 10.0f;
|
||||
float bmr = Converters.fromUnsignedInt16Be(weightBytes, 14);
|
||||
// weightBytes[16] is an (optional, ignored) "physiological age" in some scale variants.
|
||||
|
||||
ScaleMeasurement scaleBtData = new ScaleMeasurement();
|
||||
|
@@ -109,7 +109,7 @@ public class BluetoothExingtechY1 extends BluetoothCommunication {
|
||||
|
||||
final ScaleUser selectedUser = OpenScale.getInstance().getSelectedScaleUser();
|
||||
|
||||
scaleBtData.setWeight(Converters.toKilogram(weight, selectedUser.getScaleUnit()));
|
||||
scaleBtData.setWeight(weight);
|
||||
scaleBtData.setFat(fat);
|
||||
scaleBtData.setMuscle(muscle);
|
||||
scaleBtData.setWater(water);
|
||||
|
@@ -38,14 +38,14 @@ public class BluetoothFactory {
|
||||
if (name.startsWith("BEURER BF710".toLowerCase(Locale.US))) {
|
||||
return new BluetoothBeurerSanitas(context, BluetoothBeurerSanitas.DeviceType.BEURER_BF710);
|
||||
}
|
||||
if (name.equals("openScale_MCU".toLowerCase(Locale.US))) {
|
||||
if (name.equals("openScale".toLowerCase(Locale.US))) {
|
||||
return new BluetoothCustomOpenScale(context);
|
||||
}
|
||||
if (name.equals("Mengii".toLowerCase(Locale.US))) {
|
||||
return new BluetoothDigooDGSO38H(context);
|
||||
}
|
||||
if (name.equals("Electronic Scale".toLowerCase(Locale.US))) {
|
||||
return new BluetoothExcelvanCF369BLE(context);
|
||||
return new BluetoothExcelvanCF36xBLE(context);
|
||||
}
|
||||
if (name.equals("VScale".toLowerCase(Locale.US))) {
|
||||
return new BluetoothExingtechY1(context);
|
||||
@@ -58,7 +58,7 @@ public class BluetoothFactory {
|
||||
}
|
||||
// BS444 || BS440
|
||||
if (deviceName.startsWith("013197") || deviceName.startsWith("0202B6")) {
|
||||
return new BluetoothMedisanaBS444(context);
|
||||
return new BluetoothMedisanaBS44x(context);
|
||||
}
|
||||
if (deviceName.startsWith("SWAN") || name.equals("icomon".toLowerCase(Locale.US))) {
|
||||
return new BluetoothMGB(context);
|
||||
|
@@ -25,7 +25,7 @@ import com.health.openscale.core.utils.Converters;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
public class BluetoothMedisanaBS444 extends BluetoothCommunication {
|
||||
public class BluetoothMedisanaBS44x extends BluetoothCommunication {
|
||||
private final UUID WEIGHT_MEASUREMENT_SERVICE = UUID.fromString("000078b2-0000-1000-8000-00805f9b34fb");
|
||||
private final UUID WEIGHT_MEASUREMENT_CHARACTERISTIC = UUID.fromString("00008a21-0000-1000-8000-00805f9b34fb"); // indication, read-only
|
||||
private final UUID FEATURE_MEASUREMENT_CHARACTERISTIC = UUID.fromString("00008a22-0000-1000-8000-00805f9b34fb"); // indication, read-only
|
||||
@@ -39,16 +39,11 @@ public class BluetoothMedisanaBS444 extends BluetoothCommunication {
|
||||
// Scale time is in seconds since 2010-01-01
|
||||
private static final long SCALE_UNIX_TIMESTAMP_OFFSET = 1262304000;
|
||||
|
||||
public BluetoothMedisanaBS444(Context context) {
|
||||
public BluetoothMedisanaBS44x(Context context) {
|
||||
super(context);
|
||||
btScaleMeasurement = new ScaleMeasurement();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean discoverDeviceBeforeConnecting() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String driverName() {
|
||||
return "Medisana BS44x";
|
@@ -31,12 +31,16 @@ import timber.log.Timber;
|
||||
|
||||
public class BluetoothOneByone extends BluetoothCommunication {
|
||||
private final UUID WEIGHT_MEASUREMENT_SERVICE_BODY_COMPOSITION = UUID.fromString("0000181B-0000-1000-8000-00805f9b34fb");
|
||||
|
||||
private final UUID WEIGHT_MEASUREMENT_CHARACTERISTIC_BODY_COMPOSITION = UUID.fromString("00002A9C-0000-1000-8000-00805f9b34fb"); // read, indication
|
||||
private final UUID WEIGHT_MEASUREMENT_CHARACTERISTIC_BODY_COMPOSITION_ALT = UUID.fromString("0000fff4-0000-1000-8000-00805f9b34fb"); // notify
|
||||
|
||||
private final UUID WEIGHT_MEASUREMENT_SERVICE = UUID.fromString("0000fff0-0000-1000-8000-00805f9b34fb");
|
||||
private final UUID CMD_MEASUREMENT_CHARACTERISTIC = UUID.fromString("0000fff1-0000-1000-8000-00805f9b34fb"); // write only
|
||||
private final UUID WEIGHT_MEASUREMENT_CONFIG = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
|
||||
|
||||
private float lastWeight;
|
||||
|
||||
public BluetoothOneByone(Context context) {
|
||||
super(context);
|
||||
}
|
||||
@@ -50,7 +54,18 @@ public class BluetoothOneByone extends BluetoothCommunication {
|
||||
protected boolean nextInitCmd(int stateNr) {
|
||||
switch (stateNr) {
|
||||
case 0:
|
||||
setIndicationOn(WEIGHT_MEASUREMENT_SERVICE_BODY_COMPOSITION, WEIGHT_MEASUREMENT_CHARACTERISTIC_BODY_COMPOSITION, WEIGHT_MEASUREMENT_CONFIG);
|
||||
lastWeight = 0;
|
||||
|
||||
if (hasBluetoothGattService(WEIGHT_MEASUREMENT_SERVICE_BODY_COMPOSITION)) {
|
||||
setIndicationOn(WEIGHT_MEASUREMENT_SERVICE_BODY_COMPOSITION,
|
||||
WEIGHT_MEASUREMENT_CHARACTERISTIC_BODY_COMPOSITION,
|
||||
WEIGHT_MEASUREMENT_CONFIG);
|
||||
}
|
||||
else {
|
||||
setNotificationOn(WEIGHT_MEASUREMENT_SERVICE,
|
||||
WEIGHT_MEASUREMENT_CHARACTERISTIC_BODY_COMPOSITION_ALT,
|
||||
WEIGHT_MEASUREMENT_CONFIG);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
ScaleUser currentUser = OpenScale.getInstance().getSelectedScaleUser();
|
||||
@@ -91,11 +106,20 @@ public class BluetoothOneByone extends BluetoothCommunication {
|
||||
@Override
|
||||
public void onBluetoothDataChange(BluetoothGatt bluetoothGatt, BluetoothGattCharacteristic gattCharacteristic) {
|
||||
final byte[] data = gattCharacteristic.getValue();
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final UUID uuid = gattCharacteristic.getUuid();
|
||||
// if data is valid data
|
||||
if (data != null && data.length == 20) {
|
||||
if (data.length == 20
|
||||
&& uuid.equals(WEIGHT_MEASUREMENT_CHARACTERISTIC_BODY_COMPOSITION)) {
|
||||
parseBytes(data);
|
||||
}
|
||||
else if (data.length == 11
|
||||
&& uuid.equals(WEIGHT_MEASUREMENT_CHARACTERISTIC_BODY_COMPOSITION_ALT)) {
|
||||
parseBytesAlt(data);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseBytes(byte[] weightBytes) {
|
||||
@@ -104,10 +128,31 @@ public class BluetoothOneByone extends BluetoothCommunication {
|
||||
|
||||
Timber.d("weight: %.2f, impedance: %d", weight, impedance);
|
||||
|
||||
ScaleMeasurement scaleBtData = new ScaleMeasurement();
|
||||
// This check should be a bit more elaborate, but it works for now...
|
||||
if (weight != lastWeight) {
|
||||
lastWeight = weight;
|
||||
|
||||
ScaleMeasurement scaleBtData = new ScaleMeasurement();
|
||||
scaleBtData.setWeight(weight);
|
||||
|
||||
addScaleData(scaleBtData);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseBytesAlt(byte[] weightBytes) {
|
||||
float weight = Converters.fromUnsignedInt16Le(weightBytes, 3) / 100.0f;
|
||||
boolean done = (weightBytes[9] & 0xff) == 0;
|
||||
|
||||
Timber.d("weight: %.2f%s", weight, done ? " (done)" : "");
|
||||
|
||||
// This check should be a bit more elaborate, but it works for now...
|
||||
if (done && weight != lastWeight) {
|
||||
lastWeight = weight;
|
||||
|
||||
ScaleMeasurement scaleBtData = new ScaleMeasurement();
|
||||
scaleBtData.setWeight(weight);
|
||||
|
||||
addScaleData(scaleBtData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -127,7 +127,7 @@ public class BluetoothYunmaiSE_Mini extends BluetoothCommunication {
|
||||
scaleBtData.setDateTime(new Date(timestamp));
|
||||
|
||||
float weight = Converters.fromUnsignedInt16Be(weightBytes, 13) / 100.0f;
|
||||
scaleBtData.setWeight(Converters.toKilogram(weight, selectedUser.getScaleUnit()));
|
||||
scaleBtData.setWeight(weight);
|
||||
|
||||
if (isMini) {
|
||||
float fat = Converters.fromUnsignedInt16Be(weightBytes, 17) / 100.0f;
|
||||
|
@@ -353,8 +353,13 @@ public class ScaleMeasurement implements Cloneable {
|
||||
}
|
||||
|
||||
public void setComment(String comment) {
|
||||
if (comment == null) {
|
||||
this.comment = "";
|
||||
}
|
||||
else {
|
||||
this.comment = comment;
|
||||
}
|
||||
}
|
||||
|
||||
public float getBMI(float body_height) {
|
||||
return weight / ((body_height / 100.0f)*(body_height / 100.0f));
|
||||
|
@@ -41,6 +41,8 @@ import android.widget.TextView;
|
||||
import com.health.openscale.R;
|
||||
import com.health.openscale.core.OpenScale;
|
||||
import com.health.openscale.core.datatypes.ScaleMeasurement;
|
||||
import com.health.openscale.core.datatypes.ScaleUser;
|
||||
import com.health.openscale.core.utils.Converters;
|
||||
import com.health.openscale.core.utils.PolynomialFitter;
|
||||
import com.health.openscale.gui.activities.DataEntryActivity;
|
||||
import com.health.openscale.gui.views.BMRMeasurementView;
|
||||
@@ -412,7 +414,8 @@ public class GraphFragment extends Fragment implements FragmentUpdateListener {
|
||||
if (prefs.getBoolean("goalLine", true)) {
|
||||
Stack<PointValue> valuesGoalLine = new Stack<>();
|
||||
|
||||
float goalWeight = openScale.getSelectedScaleUser().getGoalWeight();
|
||||
final ScaleUser user = openScale.getSelectedScaleUser();
|
||||
float goalWeight = Converters.fromKilogram(user.getGoalWeight(), user.getScaleUnit());
|
||||
|
||||
valuesGoalLine.push(new PointValue(0, goalWeight));
|
||||
valuesGoalLine.push(new PointValue(maxDays, goalWeight));
|
||||
|
@@ -17,24 +17,18 @@ package com.health.openscale.gui.fragments;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.widget.DividerItemDecoration;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TableLayout;
|
||||
import android.widget.TableRow;
|
||||
import android.widget.TextView;
|
||||
|
||||
@@ -45,45 +39,43 @@ import com.health.openscale.gui.activities.DataEntryActivity;
|
||||
import com.health.openscale.gui.views.MeasurementView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
|
||||
|
||||
public class TableFragment extends Fragment implements FragmentUpdateListener {
|
||||
private View tableView;
|
||||
private ListView tableDataView;
|
||||
private LinearLayout tableHeaderView;
|
||||
private LinearLayout subpageView;
|
||||
|
||||
private RecyclerView recyclerView;
|
||||
private MeasurementsAdapter adapter;
|
||||
private LinearLayoutManager layoutManager;
|
||||
|
||||
private List<MeasurementView> measurementViews;
|
||||
|
||||
private int selectedSubpageNr;
|
||||
private static final String SELECTED_SUBPAGE_NR_KEY = "selectedSubpageNr";
|
||||
|
||||
public TableFragment() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
|
||||
{
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
tableView = inflater.inflate(R.layout.fragment_table, container, false);
|
||||
|
||||
subpageView = tableView.findViewById(R.id.subpageView);
|
||||
|
||||
tableDataView = tableView.findViewById(R.id.tableDataView);
|
||||
tableHeaderView = tableView.findViewById(R.id.tableHeaderView);
|
||||
recyclerView = tableView.findViewById(R.id.tableDataView);
|
||||
|
||||
tableDataView.setAdapter(new ListViewAdapter());
|
||||
tableDataView.setOnItemClickListener(new onClickListenerRow());
|
||||
recyclerView.setHasFixedSize(true);
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
selectedSubpageNr = 0;
|
||||
}
|
||||
else {
|
||||
selectedSubpageNr = savedInstanceState.getInt(SELECTED_SUBPAGE_NR_KEY);
|
||||
}
|
||||
layoutManager = new LinearLayoutManager(getContext());
|
||||
recyclerView.setLayoutManager(layoutManager);
|
||||
|
||||
recyclerView.addItemDecoration(new DividerItemDecoration(
|
||||
recyclerView.getContext(), layoutManager.getOrientation()));
|
||||
|
||||
adapter = new MeasurementsAdapter();
|
||||
recyclerView.setAdapter(adapter);
|
||||
|
||||
measurementViews = MeasurementView.getMeasurementList(
|
||||
getContext(), MeasurementView.DateTimeOrder.FIRST);
|
||||
@@ -103,236 +95,176 @@ public class TableFragment extends Fragment implements FragmentUpdateListener {
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putInt(SELECTED_SUBPAGE_NR_KEY, selectedSubpageNr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateOnView(List<ScaleMeasurement> scaleMeasurementList)
|
||||
{
|
||||
final int maxSize = 25;
|
||||
|
||||
final int subpageCount = (int)Math.ceil(scaleMeasurementList.size() / (double)maxSize);
|
||||
if (selectedSubpageNr >= subpageCount) {
|
||||
selectedSubpageNr = Math.max(0, subpageCount - 1);
|
||||
}
|
||||
|
||||
subpageView.removeAllViews();
|
||||
|
||||
Button moveSubpageLeft = new Button(tableView.getContext());
|
||||
moveSubpageLeft.setText("<");
|
||||
moveSubpageLeft.setPadding(0,0,0,0);
|
||||
moveSubpageLeft.setTextColor(Color.WHITE);
|
||||
moveSubpageLeft.setBackground(ContextCompat.getDrawable(tableView.getContext(), R.drawable.flat_selector));
|
||||
moveSubpageLeft.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));
|
||||
moveSubpageLeft.getLayoutParams().height = pxImageDp(20);
|
||||
moveSubpageLeft.getLayoutParams().width = pxImageDp(50);
|
||||
moveSubpageLeft.setOnClickListener(new onClickListenerMoveSubpageLeft());
|
||||
moveSubpageLeft.setEnabled(selectedSubpageNr > 0);
|
||||
subpageView.addView(moveSubpageLeft);
|
||||
|
||||
for (int i = 0; i < subpageCount; i++) {
|
||||
TextView subpageNrView = new TextView(tableView.getContext());
|
||||
subpageNrView.setOnClickListener(new onClickListenerSubpageSelect());
|
||||
subpageNrView.setText(Integer.toString(i+1));
|
||||
subpageNrView.setTextColor(Color.GRAY);
|
||||
subpageNrView.setPadding(10, 10, 20, 10);
|
||||
|
||||
subpageView.addView(subpageNrView);
|
||||
}
|
||||
|
||||
if (subpageView.getChildCount() > 1) {
|
||||
TextView selectedSubpageNrView = (TextView) subpageView.getChildAt(selectedSubpageNr + 1);
|
||||
if (selectedSubpageNrView != null) {
|
||||
selectedSubpageNrView.setTypeface(null, Typeface.BOLD);
|
||||
selectedSubpageNrView.setTextColor(Color.WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
Button moveSubpageRight = new Button(tableView.getContext());
|
||||
moveSubpageRight.setText(">");
|
||||
moveSubpageRight.setPadding(0,0,0,0);
|
||||
moveSubpageRight.setTextColor(Color.WHITE);
|
||||
moveSubpageRight.setBackground(ContextCompat.getDrawable(tableView.getContext(), R.drawable.flat_selector));
|
||||
moveSubpageRight.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));
|
||||
moveSubpageRight.getLayoutParams().height = pxImageDp(20);
|
||||
moveSubpageRight.getLayoutParams().width = pxImageDp(50);
|
||||
moveSubpageRight.setOnClickListener(new onClickListenerMoveSubpageRight());
|
||||
moveSubpageRight.setEnabled(selectedSubpageNr + 1 < subpageCount);
|
||||
subpageView.addView(moveSubpageRight);
|
||||
|
||||
subpageView.setVisibility(subpageCount > 1 ? View.VISIBLE : View.GONE);
|
||||
|
||||
tableHeaderView.removeAllViews();
|
||||
|
||||
final int iconHeight = pxImageDp(20);
|
||||
ArrayList<MeasurementView> visibleMeasurements = new ArrayList<>();
|
||||
for (MeasurementView measurement : measurementViews) {
|
||||
|
||||
if (measurement.isVisible()) {
|
||||
for (MeasurementView measurement : measurementViews) {
|
||||
if (!measurement.isVisible()) {
|
||||
continue;
|
||||
}
|
||||
ImageView headerIcon = new ImageView(tableView.getContext());
|
||||
headerIcon.setImageDrawable(measurement.getIcon());
|
||||
headerIcon.setLayoutParams(new TableRow.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.MATCH_PARENT, 1));
|
||||
headerIcon.getLayoutParams().width = 0;
|
||||
headerIcon.setLayoutParams(new TableRow.LayoutParams(0, iconHeight, 1));
|
||||
headerIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
|
||||
headerIcon.getLayoutParams().height = pxImageDp(20);
|
||||
|
||||
tableHeaderView.addView(headerIcon);
|
||||
|
||||
visibleMeasurements.add(measurement);
|
||||
}
|
||||
}
|
||||
|
||||
ListViewAdapter adapter = (ListViewAdapter) tableDataView.getAdapter();
|
||||
|
||||
final int startOffset = maxSize * selectedSubpageNr;
|
||||
final int endOffset = Math.min(startOffset + maxSize + 1, scaleMeasurementList.size());
|
||||
adapter.setMeasurements(visibleMeasurements, scaleMeasurementList.subList(startOffset, endOffset), maxSize);
|
||||
adapter.setMeasurements(visibleMeasurements, scaleMeasurementList);
|
||||
}
|
||||
|
||||
private int pxImageDp(float dp) {
|
||||
return (int)(dp * getResources().getDisplayMetrics().density + 0.5f);
|
||||
}
|
||||
|
||||
private class onClickListenerRow implements AdapterView.OnItemClickListener {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
Intent intent = new Intent(tableView.getContext(), DataEntryActivity.class);
|
||||
intent.putExtra(DataEntryActivity.EXTRA_ID, (int)id);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
private class MeasurementsAdapter extends RecyclerView.Adapter<MeasurementsAdapter.ViewHolder> {
|
||||
public static final int VIEW_TYPE_MEASUREMENT = 0;
|
||||
public static final int VIEW_TYPE_YEAR = 1;
|
||||
|
||||
private class onClickListenerMoveSubpageLeft implements View.OnClickListener {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (selectedSubpageNr > 0) {
|
||||
selectedSubpageNr--;
|
||||
updateOnView(OpenScale.getInstance().getScaleMeasurementList());
|
||||
public class ViewHolder extends RecyclerView.ViewHolder {
|
||||
public LinearLayout measurementView;
|
||||
public ViewHolder(LinearLayout view) {
|
||||
super(view);
|
||||
measurementView = view;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class onClickListenerMoveSubpageRight implements View.OnClickListener {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (selectedSubpageNr < (subpageView.getChildCount() - 3)) {
|
||||
selectedSubpageNr++;
|
||||
updateOnView(OpenScale.getInstance().getScaleMeasurementList());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class onClickListenerSubpageSelect implements View.OnClickListener {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
TextView nrView = (TextView)v;
|
||||
|
||||
selectedSubpageNr = Integer.parseInt(nrView.getText().toString())-1;
|
||||
updateOnView(OpenScale.getInstance().getScaleMeasurementList());
|
||||
}
|
||||
}
|
||||
|
||||
private class ListViewAdapter extends BaseAdapter {
|
||||
|
||||
private List<MeasurementView> visibleMeasurements;
|
||||
private List<ScaleMeasurement> scaleMeasurements;
|
||||
private int measurementsToShow = 0;
|
||||
|
||||
private Spanned[][] stringCache;
|
||||
|
||||
public void setMeasurements(List<MeasurementView> visibleMeasurements,
|
||||
List<ScaleMeasurement> scaleMeasurements,
|
||||
int maxSize) {
|
||||
List<ScaleMeasurement> scaleMeasurements) {
|
||||
this.visibleMeasurements = visibleMeasurements;
|
||||
this.scaleMeasurements = scaleMeasurements;
|
||||
measurementsToShow = Math.min(scaleMeasurements.size(), maxSize);
|
||||
this.scaleMeasurements = new ArrayList<>(scaleMeasurements.size() + 10);
|
||||
|
||||
stringCache = new Spanned[measurementsToShow][visibleMeasurements.size()];
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
if (!scaleMeasurements.isEmpty()) {
|
||||
calendar.setTime(scaleMeasurements.get(0).getDateTime());
|
||||
}
|
||||
calendar.set(calendar.get(Calendar.YEAR), 0, 1, 0, 0, 0);
|
||||
calendar.set(calendar.MILLISECOND, 0);
|
||||
|
||||
// Copy all measurements from input parameter to member variable and insert
|
||||
// an extra "null" entry when the year changes.
|
||||
Date yearStart = calendar.getTime();
|
||||
for (int i = 0; i < scaleMeasurements.size(); ++i) {
|
||||
final ScaleMeasurement measurement = scaleMeasurements.get(i);
|
||||
|
||||
if (measurement.getDateTime().before(yearStart)) {
|
||||
this.scaleMeasurements.add(null);
|
||||
|
||||
Calendar newCalendar = Calendar.getInstance();
|
||||
newCalendar.setTime(measurement.getDateTime());
|
||||
calendar.set(Calendar.YEAR, newCalendar.get(Calendar.YEAR));
|
||||
yearStart = calendar.getTime();
|
||||
}
|
||||
|
||||
this.scaleMeasurements.add(measurement);
|
||||
}
|
||||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return measurementsToShow;
|
||||
}
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
LinearLayout row = new LinearLayout(getContext());
|
||||
row.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return scaleMeasurements.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return scaleMeasurements.get(position).getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
// Create entries in stringCache if needed
|
||||
if (stringCache[position][0] == null) {
|
||||
ScaleMeasurement measurement = scaleMeasurements.get(position);
|
||||
ScaleMeasurement prevMeasurement = null;
|
||||
if (position + 1 < scaleMeasurements.size()) {
|
||||
prevMeasurement = scaleMeasurements.get(position + 1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < visibleMeasurements.size(); ++i) {
|
||||
visibleMeasurements.get(i).loadFrom(measurement, prevMeasurement);
|
||||
|
||||
SpannableStringBuilder string = new SpannableStringBuilder();
|
||||
string.append(visibleMeasurements.get(i).getValueAsString(false));
|
||||
visibleMeasurements.get(i).appendDiffValue(string, true);
|
||||
|
||||
stringCache[position][i] = string;
|
||||
}
|
||||
}
|
||||
|
||||
// Create view if needed
|
||||
LinearLayout row;
|
||||
if (convertView == null) {
|
||||
row = new LinearLayout(getContext());
|
||||
|
||||
final int screenSize = getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
|
||||
final int screenSize = getResources().getConfiguration()
|
||||
.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
|
||||
final boolean isSmallScreen =
|
||||
screenSize != Configuration.SCREENLAYOUT_SIZE_XLARGE
|
||||
&& screenSize != Configuration.SCREENLAYOUT_SIZE_LARGE;
|
||||
|
||||
for (int i = 0; i < visibleMeasurements.size(); ++i) {
|
||||
final int count = viewType == VIEW_TYPE_YEAR ? 1 : visibleMeasurements.size();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
TextView column = new TextView(getContext());
|
||||
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
1);
|
||||
layoutParams.width = 0;
|
||||
column.setLayoutParams(layoutParams);
|
||||
column.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
0, ViewGroup.LayoutParams.WRAP_CONTENT, 1));
|
||||
|
||||
if (viewType == VIEW_TYPE_MEASUREMENT) {
|
||||
column.setMinLines(2);
|
||||
column.setGravity(Gravity.CENTER_HORIZONTAL);
|
||||
|
||||
if (isSmallScreen) {
|
||||
column.setTextSize(COMPLEX_UNIT_DIP, 9);
|
||||
}
|
||||
row.addView(column);
|
||||
}
|
||||
}
|
||||
else {
|
||||
row = (LinearLayout) convertView;
|
||||
column.setPadding(0, 10, 0, 10);
|
||||
column.setGravity(Gravity.CENTER);
|
||||
column.setTextSize(COMPLEX_UNIT_DIP, 16);
|
||||
}
|
||||
|
||||
row.addView(column);
|
||||
}
|
||||
|
||||
return new ViewHolder(row);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
LinearLayout row = holder.measurementView;
|
||||
|
||||
final ScaleMeasurement measurement = scaleMeasurements.get(position);
|
||||
if (measurement == null) {
|
||||
ScaleMeasurement nextMeasurement = scaleMeasurements.get(position + 1);
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(nextMeasurement.getDateTime());
|
||||
|
||||
TextView column = (TextView) row.getChildAt(0);
|
||||
column.setText(String.format("%d", calendar.get(Calendar.YEAR)));
|
||||
return;
|
||||
}
|
||||
|
||||
ScaleMeasurement prevMeasurement = null;
|
||||
if (position + 1 < scaleMeasurements.size()) {
|
||||
prevMeasurement = scaleMeasurements.get(position + 1);
|
||||
if (prevMeasurement == null) {
|
||||
prevMeasurement = scaleMeasurements.get(position + 2);
|
||||
}
|
||||
}
|
||||
|
||||
// Fill view with data
|
||||
for (int i = 0; i < visibleMeasurements.size(); ++i) {
|
||||
final MeasurementView view = visibleMeasurements.get(i);
|
||||
view.loadFrom(measurement, prevMeasurement);
|
||||
|
||||
SpannableStringBuilder string = new SpannableStringBuilder();
|
||||
string.append(view.getValueAsString(false));
|
||||
view.appendDiffValue(string, true);
|
||||
|
||||
TextView column = (TextView) row.getChildAt(i);
|
||||
column.setText(stringCache[position][i]);
|
||||
column.setText(string);
|
||||
}
|
||||
|
||||
return row;
|
||||
row.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(getContext(), DataEntryActivity.class);
|
||||
intent.putExtra(DataEntryActivity.EXTRA_ID, measurement.getId());
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasStableIds() {
|
||||
return true;
|
||||
public int getItemCount() {
|
||||
return scaleMeasurements == null ? 0 : scaleMeasurements.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
return scaleMeasurements.get(position) != null ? VIEW_TYPE_MEASUREMENT : VIEW_TYPE_YEAR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -80,8 +80,9 @@ public class AboutPreferences extends PreferenceFragment {
|
||||
|
||||
@Override
|
||||
protected void log(int priority, String tag, String message, Throwable t) {
|
||||
writer.printf("%s %s %s: %s\n",
|
||||
format.format(new Date()), priorityToString(priority), tag, message);
|
||||
final long id = Thread.currentThread().getId();
|
||||
writer.printf("%s %s [%d] %s: %s\n",
|
||||
format.format(new Date()), priorityToString(priority), id, tag, message);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -232,6 +232,7 @@ public class BluetoothPreferences extends PreferenceFragment {
|
||||
prefBtDevice.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
stopDiscoveryAndLeScan();
|
||||
getDebugInfo(device);
|
||||
return false;
|
||||
}
|
||||
@@ -306,7 +307,7 @@ public class BluetoothPreferences extends PreferenceFragment {
|
||||
super.onStart();
|
||||
|
||||
// Restart discovery after e.g. orientation change
|
||||
if (btScanner.getDialog() != null) {
|
||||
if (btScanner.getDialog() != null && btScanner.getDialog().isShowing()) {
|
||||
startBluetoothDiscovery();
|
||||
}
|
||||
}
|
||||
|
@@ -31,11 +31,12 @@ public class DateMeasurementView extends MeasurementView {
|
||||
// Don't change key value, it may be stored persistent in preferences
|
||||
public static final String KEY = "date";
|
||||
|
||||
private static final DateFormat dateFormat = DateFormat.getDateInstance();
|
||||
private final DateFormat dateFormat;
|
||||
private Date date;
|
||||
|
||||
public DateMeasurementView(Context context) {
|
||||
super(context, R.string.label_date, R.drawable.ic_lastmonth);
|
||||
dateFormat = DateFormat.getDateInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -1,68 +1,24 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="5dp"
|
||||
android:gravity="center"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:gravity="left"
|
||||
android:layout_weight="1"
|
||||
android:orientation="horizontal">
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/subpageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:gravity="center"
|
||||
android:layout_weight="0"
|
||||
android:orientation="horizontal">
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:gravity="right"
|
||||
android:layout_weight="0.9"
|
||||
android:orientation="horizontal">
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/tableHeaderView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingBottom="5dp"
|
||||
android:paddingTop="5dp">
|
||||
</LinearLayout>
|
||||
android:paddingTop="5dp" />
|
||||
|
||||
<LinearLayout
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="0.9"
|
||||
android:gravity="bottom"
|
||||
android:orientation="horizontal">
|
||||
android:layout_height="2dp"
|
||||
android:background="?android:attr/listDivider" />
|
||||
|
||||
<ListView
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/tableDataView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginHorizontal="5dp">
|
||||
</ListView>
|
||||
</LinearLayout>
|
||||
android:scrollbars="vertical" />
|
||||
</LinearLayout>
|
||||
|
228
android_app/app/src/main/res/values-da/strings.xml
Normal file
228
android_app/app/src/main/res/values-da/strings.xml
Normal file
@@ -0,0 +1,228 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<string name="title_overview">Oversigt</string>
|
||||
<string name="title_graph">Diagram</string>
|
||||
<string name="title_table">Tabel</string>
|
||||
<string name="title_statistics">Statistik</string>
|
||||
<string name="title_users">Brugere</string>
|
||||
<string name="title_measurements">Målinger</string>
|
||||
<string name="title_about">Om</string>
|
||||
<string name="action_settings">Indstillinger</string>
|
||||
<string name="action_bluetooth_status">Bluetooth-status</string>
|
||||
<string name="label_cancel">Afslut</string>
|
||||
<string name="label_ok">Ok</string>
|
||||
<string name="label_yes">Ja</string>
|
||||
<string name="label_no">Nej</string>
|
||||
<string name="label_delete">Slet</string>
|
||||
<string name="label_add_user">Tilføj bruger</string>
|
||||
<string name="label_weight">Kropsvægt</string>
|
||||
<string name="label_bmi">Body Mass Index (BMI)</string>
|
||||
<string name="label_bmr">Basalstofskifte (BMR)</string>
|
||||
<string name="label_fat">Kropsfedt</string>
|
||||
<string name="label_water">Kropsvæske</string>
|
||||
<string name="Monday">Mandag</string>
|
||||
<string name="Tuesday">Tirsdag</string>
|
||||
<string name="Wednesday">Onsdag</string>
|
||||
<string name="Thursday">Torsdag</string>
|
||||
<string name="Friday">Fredag</string>
|
||||
<string name="Saturday">Lørdag</string>
|
||||
<string name="Sunday">Søndag</string>
|
||||
<string name="label_backup">Sikkerhedskopi</string>
|
||||
<string name="default_value_reminder_notify_text">Dato for indvejning</string>
|
||||
<string name="label_reminder_time">Tid</string>
|
||||
<string name="label_reminder_weekdays">Dage</string>
|
||||
<string name="label_website">Hjemmeside</string>
|
||||
<string name="label_license">Licens</string>
|
||||
<string name="error_exporting">Fejl i eksport</string>
|
||||
<string name="label_gender">Køn</string>
|
||||
<string name="label_male">Mand</string>
|
||||
<string name="label_female">Kvinde</string>
|
||||
<string name="label_goal_weight">Målvægt</string>
|
||||
<string name="label_goal_date">Måldato</string>
|
||||
<string name="label_title_user">Bruger</string>
|
||||
<string name="label_title_last_measurement">Seneste vejning</string>
|
||||
<string name="label_title_goal">Mål</string>
|
||||
<string name="label_date">Dato</string>
|
||||
<string name="label_time">Tid</string>
|
||||
<string name="label_birthday">Fødselsdag</string>
|
||||
<string name="label_user_name">Navn</string>
|
||||
<string name="label_height">Højde</string>
|
||||
<string name="label_scale_unit">Vægtenhed</string>
|
||||
<string name="label_days_left">Dage tilbage</string>
|
||||
<string name="label_goal_date_is">Måldatoen:</string>
|
||||
<string name="label_weight_difference">Vægtforskel</string>
|
||||
<plurals name="label_days">
|
||||
<item quantity="one">%d dag</item>
|
||||
<item quantity="other">%d dage</item>
|
||||
</plurals>
|
||||
<string name="label_last_week">Gennemsnit den seneste uge</string>
|
||||
<string name="label_last_month">Gennemsnit den seneste måned</string>
|
||||
<string name="label_muscle">Muskler</string>
|
||||
<string name="label_lbm">Fedtfri kropsvægt</string>
|
||||
<string name="label_waist">Taljemål</string>
|
||||
<string name="label_hip">Hofteomkreds</string>
|
||||
<string name="label_comment">Kommentar</string>
|
||||
<string name="label_whr">Talje-hofte ratio</string>
|
||||
<string name="label_whtr">Talje-højde ratio</string>
|
||||
<string name="label_bone">Knoglemasse</string>
|
||||
<string name="label_smartUserAssign">Smart tildeling af brugere</string>
|
||||
<string name="label_import">Import</string>
|
||||
<string name="label_export">Eksport</string>
|
||||
<string name="label_delete_all">Slet alle</string>
|
||||
<string name="error_value_required">Obligatorisk</string>
|
||||
<string name="error_value_range">Værdien er uden for intervallet</string>
|
||||
<string name="error_importing">Fejl ved import</string>
|
||||
<string name="error_user_name_required">Fejl: navn er påkrævet</string>
|
||||
<string name="error_height_required">Fejl: højde er påkrævet</string>
|
||||
<string name="error_initial_weight_required">Fejl: vægt er påkrævet</string>
|
||||
<string name="error_goal_weight_required">Fejl: målvægt er påkrævet</string>
|
||||
<string name="info_data_deleted">Data er slettet</string>
|
||||
<string name="info_data_all_deleted">Alle data er blevet slettet</string>
|
||||
<string name="info_data_exported">Eksportér til</string>
|
||||
<string name="info_data_imported">Importér fra</string>
|
||||
<string name="info_enter_value_in">Værdien i</string>
|
||||
<string name="info_enter_comment">Kommentar</string>
|
||||
<string name="info_is_visible">er synlig</string>
|
||||
<string name="info_is_not_visible">er ikke synlig</string>
|
||||
<string name="info_is_enable">aktiveret</string>
|
||||
<string name="info_is_not_enable">deaktiveret</string>
|
||||
<string name="info_is_not_available">ikke tilgængelig</string>
|
||||
<string name="info_bluetooth_try_connection">Forbinder til:</string>
|
||||
<string name="info_bluetooth_connection_lost">Ingen forbindelse til Bluetooth enhed</string>
|
||||
<string name="info_bluetooth_no_device">Ingen Bluetooth-enhed fundet</string>
|
||||
<string name="info_bluetooth_connection_successful">Forbindelse etableret</string>
|
||||
<string name="info_bluetooth_init">Initierer Bluetooth enhed</string>
|
||||
<string name="info_bluetooth_connection_error">Uventet Bluetooth fejl</string>
|
||||
<string name="info_new_data_added">Tilføjede %1$.2f%2$s [%3$s] til %4$s</string>
|
||||
<string name="info_enter_user_name">Dit navn</string>
|
||||
<string name="info_no_selected_user">Vælg en bruger under indstillinger</string>
|
||||
<string name="info_no_evaluation_available">Ingen evaluering til rådighed</string>
|
||||
<string name="question_really_delete">Ønsker du at slette data?</string>
|
||||
<string name="question_really_delete_all">Ønsker du at slette alle data?</string>
|
||||
<string name="question_really_delete_user">Ønsker du at slette brugeren?</string>
|
||||
<string name="label_bluetooth_title">Bluetooth</string>
|
||||
<string name="label_bluetooth_enable">Aktivér Bluetoth</string>
|
||||
<string name="label_bluetooth_searching">Etablerer Bluetooth forbindelse</string>
|
||||
<string name="label_enable_labels">Data labels</string>
|
||||
<string name="label_enable_points">Datapunkter</string>
|
||||
<string name="label_delete_confirmation">Bekræft sletning</string>
|
||||
<string name="label_category_measurement_database">Mållingsdatabase</string>
|
||||
<string name="label_maintainer">Udviklere</string>
|
||||
<string name="label_automatic">auto</string>
|
||||
<string name="label_reminder">Påmindelse</string>
|
||||
<string name="label_reminder_notify_text">Meddelelsestekst</string>
|
||||
<string name="info_on_date">på</string>
|
||||
<string name="label_bt_device_no_support">enheden er ikke understøttet</string>
|
||||
<string name="label_exportBackup">Exportér sikkerhedskopi</string>
|
||||
<string name="label_importBackup">Importér sikkerhedskopi</string>
|
||||
<string name="label_export_dir">Eksportmappe</string>
|
||||
<string name="label_not_found">blev ikke fundet</string>
|
||||
<string name="label_ignoreOutOfRange">Ignorér data udenfor interval</string>
|
||||
<string name="label_initial_weight">Startvægt</string>
|
||||
<string name="label_regression_line">Vægt tilbagegang</string>
|
||||
<string name="label_regression_line_degree">Grad af tilbagegang</string>
|
||||
<string name="label_goal_line">Mållinje</string>
|
||||
<string name="error_max_scale_users">Det maksimale antal brugere er nået</string>
|
||||
<string name="info_step_on_scale">Træd op på vægten med bare fødder for at foretagen en referencemåling</string>
|
||||
<string name="info_measuring">Måler vægt: %.2f</string>
|
||||
<string name="open_drawer">Åben</string>
|
||||
<string name="close_drawer">Luk</string>
|
||||
<string name="info_bluetooth_no_device_set">Ingen Bluetooth enhed er valgt</string>
|
||||
<string name="info_new_data_duplicated">En måling med samme dato og tidspunkt findes allerede</string>
|
||||
<string name="title_general">Generelt</string>
|
||||
<string name="error_user_name_too_short">Fejl: navnet skal være mindst tre tegn langt</string>
|
||||
<string name="label_theme">Tema</string>
|
||||
<string name="label_feedback_message_enjoying">Er du tilfreds med openScale?</string>
|
||||
<string name="label_feedback_message_rate_app">Vil du rate app'en på Google Play eller GitHub?</string>
|
||||
<string name="label_feedback_message_issue">Send feedback?</string>
|
||||
<string name="label_feedback_message_yes">Ja</string>
|
||||
<string name="label_feedback_message_no">Nej</string>
|
||||
<string name="label_feedback_message_positive">OK</string>
|
||||
<string name="label_feedback_message_negative">Nej tak</string>
|
||||
<string name="customactivityoncrash_error_activity_error_occurred_explanation">En uventet fejl er opstået.
|
||||
\n
|
||||
\nOpret venligst en ny sag
|
||||
\nhttps://github.com/oliexdev/openScale/issues</string>
|
||||
<string name="customactivityoncrash_error_activity_restart_app">Genstart app</string>
|
||||
<string name="customactivityoncrash_error_activity_close_app">Luk app</string>
|
||||
<string name="customactivityoncrash_error_activity_error_details">Fejlinformation</string>
|
||||
<string name="customactivityoncrash_error_activity_error_details_title">Fejlinformation</string>
|
||||
<string name="customactivityoncrash_error_activity_error_details_close">Luk</string>
|
||||
<string name="customactivityoncrash_error_activity_error_details_copy">Kopiér til udklipsholder</string>
|
||||
<string name="customactivityoncrash_error_activity_error_details_copied">Kopieret til udklipsholder</string>
|
||||
<string name="customactivityoncrash_error_activity_error_details_clipboard_label">Fejlinformation</string>
|
||||
<string name="save">Gem</string>
|
||||
|
||||
<string name="label_share">Del</string>
|
||||
|
||||
<string name="label_bluetooth_searching_finished">Søgning af sluttet</string>
|
||||
<string name="label_help">Hjælp</string>
|
||||
|
||||
<string name="toggle_expand">Fold ud</string>
|
||||
<string name="edit">Redigér</string>
|
||||
<string name="label_month_view">Månedsvis</string>
|
||||
|
||||
<string name="permission_not_granted">Tilladelse er ikke givet</string>
|
||||
<string name="permission_bluetooth_info">Tilladelse til lokalbestemmelse er påkrævet for at søge efter Bluetooth-enheder</string>
|
||||
<string name="permission_bluetooth_info_title">Information</string>
|
||||
|
||||
<string name="label_share_subject">CSV dataeksport (%s)</string>
|
||||
|
||||
<string name="label_next">Næste</string>
|
||||
<string name="label_set_default_order">Indstil standardsortéring</string>
|
||||
|
||||
<string name="label_weeks_view">ugentlig</string>
|
||||
<string name="label_mergeWithLastMeasurement">Flet med tidligere måling</string>
|
||||
<string name="label_export_overwrite">Overskriv tidligere eksport \"%s\"?</string>
|
||||
<string name="label_include_in_overview_graph">Vis i oversigtsdiagram</string>
|
||||
<string name="label_measurement_in_percent">Målt i %</string>
|
||||
<string name="label_estimate_measurement">Estimatmåling</string>
|
||||
<string name="label_estimation_formula">Estimeringsformel</string>
|
||||
|
||||
<string name="language_default">Systemstandard</string>
|
||||
<string name="label_language">Sprog</string>
|
||||
<string name="theme_light">Lyst</string>
|
||||
<string name="theme_dark">Mørkt</string>
|
||||
<string name="label_contribute_translation">Bidrag med oversættelse</string>
|
||||
<string name="label_add_or_fix_translation">Tilføj eller ret oversættelse</string>
|
||||
<string name="label_overview_graph">Oversigtsdiagram</string>
|
||||
<string name="label_percent">Procent</string>
|
||||
<string name="label_estimated">Skønsmæssigt</string>
|
||||
<string name="label_estimate_measurement_summary">Baseret på vægt, højde, alder, køn mv.</string>
|
||||
<string name="label_scale_not_supported">Denne personvægt er ikke understøttet</string>
|
||||
<string name="label_click_to_help_add_support">Klik for at hjælpe med at tilføje understøttelse af personvægten</string>
|
||||
<string name="label_auto_backup">Automatisk sikkerhedskopiering</string>
|
||||
<string name="label_auto_backup_schedule">Planlæg sikkerhedskopiering</string>
|
||||
<string name="label_overwrite_backup">Overskriv tidligere sikkerhedskopi</string>
|
||||
<string name="label_daily">daglig</string>
|
||||
<string name="label_weekly">ugentlig</string>
|
||||
<string name="label_monthly">månedsvis</string>
|
||||
<string name="label_development">Udvikling</string>
|
||||
<string name="label_debug_log">Gem fejlfindingslog</string>
|
||||
<string name="label_your_bluetooth_scale">Din Bluetooth personvægt</string>
|
||||
<string name="label_long_press_drag_reorder">Tryk, og træk til den ønskede position</string>
|
||||
<string name="label_click_measurement_configure">Klik på målinger for at konfigurere</string>
|
||||
<string name="label_select_measurement">Vælg målenhed</string>
|
||||
<string name="label_select_user">Vælg bruger</string>
|
||||
<string name="label_configure_widget">Konfigurér widget</string>
|
||||
<string name="label_add_measurement">Tilføj måling</string>
|
||||
<string name="label_visceral_fat">Visceralt fedt omkring indre organer</string>
|
||||
<string name="label_chest">Brystomkreds</string>
|
||||
<string name="label_thigh">Låromkreds</string>
|
||||
<string name="label_biceps">Bicepsomkreds</string>
|
||||
<string name="label_neck">Halsomkreds</string>
|
||||
<string name="label_fat_caliper">Hudfoldsmåling</string>
|
||||
<string name="label_caliper1_male">Bryst hudmåling</string>
|
||||
<string name="label_caliper2_male">Mave hudmåling</string>
|
||||
<string name="label_caliper3_male">Lår hudmåling</string>
|
||||
<string name="label_caliper1_female">Triceps hudmåling</string>
|
||||
<string name="label_caliper2_female">Mave hudmåling</string>
|
||||
<string name="label_caliper3_female">Hofte hudmåling</string>
|
||||
<string name="label_measure_unit">Målenhed</string>
|
||||
<string name="label_activity_level">Aktivitetsniveau</string>
|
||||
<string name="activity_level_sedentary">Stillesiddende</string>
|
||||
<string name="activity_level_mild">let</string>
|
||||
<string name="activity_level_moderate">Moderat</string>
|
||||
<string name="activity_level_heavy">hård</string>
|
||||
<string name="activity_level_extreme">Ekstrem</string>
|
||||
</resources>
|
@@ -21,7 +21,7 @@
|
||||
<string name="label_bmi">IMC</string>
|
||||
<string name="label_fat">Graisse corporelle</string>
|
||||
<string name="label_water">Pourcentage d\'eau</string>
|
||||
<string name="label_muscle">Pourcentage musculaire</string>
|
||||
<string name="label_muscle">Muscle</string>
|
||||
<string name="label_waist">Tour de taille</string>
|
||||
<string name="label_hip">Tour de hanches</string>
|
||||
<string name="label_comment">Commentaire</string>
|
||||
@@ -33,8 +33,8 @@
|
||||
<item quantity="one">%d jour</item>
|
||||
<item quantity="other">%d jours</item>
|
||||
</plurals>
|
||||
<string name="label_last_week">7 derniers jours</string>
|
||||
<string name="label_last_month">30 derniers jours</string>
|
||||
<string name="label_last_week">Moyenne sur la dernière semaine</string>
|
||||
<string name="label_last_month">Moyenne sur le mois dernier</string>
|
||||
<string name="label_weight_difference">Différence de poids</string>
|
||||
<string name="label_goal_date_is">La date de l\'objectif :</string>
|
||||
<string name="label_days_left">Jours restants</string>
|
||||
@@ -54,14 +54,14 @@
|
||||
|
||||
<string name="label_title_user">utilisateur</string>
|
||||
<string name="label_title_last_measurement">dernière mesure</string>
|
||||
<string name="label_title_goal">objectif</string>
|
||||
<string name="label_title_goal">Objectif</string>
|
||||
|
||||
<string name="label_import">Importer</string>
|
||||
<string name="label_export">Exporter</string>
|
||||
<string name="label_delete_all">Tout supprimer</string>
|
||||
|
||||
<string name="error_value_required">Valeur requise</string>
|
||||
<string name="error_value_range">La valeur n\'est pas dans la plage</string>
|
||||
<string name="error_value_range">Valeur hors plage</string>
|
||||
<string name="error_exporting">Erreur durant l\'exportation</string>
|
||||
<string name="error_importing">Erreur durant l\'importation</string>
|
||||
<string name="error_user_name_required">Erreur : Nom d\'utilisateur requis</string>
|
||||
@@ -70,8 +70,8 @@
|
||||
|
||||
<string name="info_data_deleted">L\'entrée a été supprimée</string>
|
||||
<string name="info_data_all_deleted">Toutes les entrées ont été supprimées</string>
|
||||
<string name="info_data_exported">Données exportées vers</string>
|
||||
<string name="info_data_imported">Donnéees importées depuis</string>
|
||||
<string name="info_data_exported">Exporté vers</string>
|
||||
<string name="info_data_imported">Importé depuis</string>
|
||||
<string name="info_enter_value_in">Valeur en</string>
|
||||
<string name="info_enter_comment">Commentaire optionnel</string>
|
||||
<string name="info_is_visible">est visible</string>
|
||||
@@ -103,10 +103,10 @@
|
||||
<string name="label_delete_confirmation">Supprimer la confirmation</string>
|
||||
|
||||
<string name="label_reminder">Rappel</string>
|
||||
<string name="label_reminder_weekdays">Jours de la semaine</string>
|
||||
<string name="label_reminder_weekdays">Jours</string>
|
||||
<string name="label_reminder_time">Heure</string>
|
||||
<string name="label_reminder_notify_text">Intitulé de la notification</string>
|
||||
<string name="default_value_reminder_notify_text">C\'est l\'heure de se peser!</string>
|
||||
<string name="default_value_reminder_notify_text">C\'est l\'heure de se peser !</string>
|
||||
|
||||
<string name="info_on_date">le</string>
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
<string name="save">Sauvegarder</string>
|
||||
|
||||
<string name="permission_not_granted">Permission non accordée</string>
|
||||
<string name="permission_bluetooth_info_title">Information</string>
|
||||
<string name="permission_bluetooth_info_title">Info</string>
|
||||
<string name="label_next">Suivant</string>
|
||||
<string name="title_about">À propos</string>
|
||||
<string name="label_share">Partager</string>
|
||||
@@ -176,10 +176,10 @@
|
||||
|
||||
<string name="label_lbm">Masse sans graisse</string>
|
||||
<string name="info_new_data_added">%1$.2f%2$s [%3$s] to %4$s rajouté</string>
|
||||
<string name="info_new_data_duplicated">des mesures à la même date et heure existent déjà</string>
|
||||
<string name="info_new_data_duplicated">une mesure à la même date et heure existe déjà</string>
|
||||
|
||||
<string name="label_mergeWithLastMeasurement">Fusionner avec la dernière mesure</string>
|
||||
<string name="label_bluetooth_searching">Détection de pèse personne en cours</string>
|
||||
<string name="label_bluetooth_searching">Détection de votre pèse-personne Bluetooth en cours</string>
|
||||
<string name="label_bluetooth_searching_finished">Détection finalisé</string>
|
||||
<string name="label_category_measurement_database">Base de données de mesures</string>
|
||||
|
||||
@@ -223,4 +223,30 @@
|
||||
<string name="label_click_to_help_add_support">Cliquer ici pour contribuer</string>
|
||||
<string name="label_development">Développement</string>
|
||||
<string name="label_debug_log">Enregistrez les données de débogage dans un fichier</string>
|
||||
<string name="label_add_measurement">Ajouter une mesure</string>
|
||||
<string name="label_long_press_drag_reorder">Effectuez un appui long suivi d\'un déplacement des mesures pour les réorganiser</string>
|
||||
<string name="label_click_measurement_configure">Cliquez sur une mesure pour la configurer</string>
|
||||
<string name="label_your_bluetooth_scale">Votre pèse-personne Bluetooth</string>
|
||||
<string name="label_select_measurement">Sélectionner une mesure</string>
|
||||
<string name="label_select_user">Sélectionner un utilisateur</string>
|
||||
<string name="label_configure_widget">Configurer le widget</string>
|
||||
<string name="label_visceral_fat">Graisse viscérale</string>
|
||||
<string name="label_chest">Tour de poitrine</string>
|
||||
<string name="label_thigh">Tour de cuisse</string>
|
||||
<string name="label_biceps">Tour des biceps</string>
|
||||
<string name="label_neck">Tour du cou</string>
|
||||
<string name="label_fat_caliper">Pince adipomètre</string>
|
||||
<string name="label_caliper1_male">Pli cutané de la poitrine</string>
|
||||
<string name="label_caliper2_male">Pli cutané abdominal</string>
|
||||
<string name="label_caliper3_male">Pli cutané de la cuisse</string>
|
||||
<string name="label_caliper1_female">Pli cutané du triceps</string>
|
||||
<string name="label_caliper2_female">Pli cutané abdominal</string>
|
||||
<string name="label_caliper3_female">Pli cutané à la hanche</string>
|
||||
<string name="label_measure_unit">Unité de mesure</string>
|
||||
<string name="label_activity_level">Niveau d\'activité</string>
|
||||
<string name="activity_level_sedentary">Sédentaire</string>
|
||||
<string name="activity_level_mild">Faible</string>
|
||||
<string name="activity_level_moderate">Modéré</string>
|
||||
<string name="activity_level_heavy">Important</string>
|
||||
<string name="activity_level_extreme">Extrême</string>
|
||||
</resources>
|
||||
|
246
android_app/app/src/main/res/values-hr/strings.xml
Normal file
246
android_app/app/src/main/res/values-hr/strings.xml
Normal file
@@ -0,0 +1,246 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources><string name="title_overview">Pregled</string>
|
||||
<string name="title_graph">Grafikon</string>
|
||||
<string name="title_table">Tablica</string>
|
||||
<string name="title_statistics">Statistika</string>
|
||||
<string name="title_users">Korisnici</string>
|
||||
<string name="title_measurements">Mjerenja</string>
|
||||
<string name="title_about">O programu</string>
|
||||
<string name="title_general">Općenito</string>
|
||||
|
||||
<string name="action_settings">Postavke</string>
|
||||
<string name="action_bluetooth_status">Bluetooth Status</string>
|
||||
<string name="open_drawer">otvori</string>
|
||||
<string name="close_drawer">zatvori</string>
|
||||
|
||||
<string name="label_cancel">Odustani</string>
|
||||
<string name="label_ok">U redu</string>
|
||||
<string name="label_yes">Da</string>
|
||||
<string name="label_no">Ne</string>
|
||||
<string name="label_delete">Izbriši</string>
|
||||
<string name="label_add_user">Dodaj korisnika</string>
|
||||
<string name="label_add_measurement">Dodaj mjerenje</string>
|
||||
<string name="label_share">Podjeli</string>
|
||||
<string name="label_share_subject">CSV izvoz (%s)</string>
|
||||
|
||||
<string name="label_weight">Težina</string>
|
||||
<string name="label_bmi">Indeks tjelesne mase (BMI)</string>
|
||||
<string name="label_bmr">Bazalni metabolizam (BMR)</string>
|
||||
<string name="label_fat">Masnoća</string>
|
||||
<string name="label_water">Voda</string>
|
||||
<string name="label_muscle">Mišići</string>
|
||||
<string name="label_lbm">Težina bez masnoća</string>
|
||||
<string name="label_waist">Opseg struka</string>
|
||||
<string name="label_hip">Opseg kukova</string>
|
||||
<string name="label_comment">Komentar</string>
|
||||
<string name="label_whtr">Omjer struka i visine</string>
|
||||
<string name="label_whr">Omjer struka i kukova</string>
|
||||
<string name="label_bone">Masa kostiju</string>
|
||||
<string name="label_smartUserAssign">Pametna dodjela korisnika</string>
|
||||
|
||||
<plurals name="label_days">
|
||||
<item quantity="one">Jedan</item>
|
||||
<item quantity="few">Nekoliko</item>
|
||||
<item quantity="other">Ostalo</item>
|
||||
</plurals>
|
||||
<string name="label_last_week">Prosjek prošlog tjedna</string>
|
||||
<string name="label_last_month">Prosjek prošlog mjeseca</string>
|
||||
<string name="label_weight_difference">Razlika težine</string>
|
||||
<string name="label_goal_date_is">Ciljani datum:</string>
|
||||
<string name="label_days_left">Preostalo dana</string>
|
||||
|
||||
<string name="label_date">Datum</string>
|
||||
<string name="label_time">Vrijeme</string>
|
||||
<string name="label_birthday">Datum rođenja</string>
|
||||
<string name="label_user_name">Ime</string>
|
||||
<string name="label_height">Visina</string>
|
||||
<string name="label_scale_unit">Mjerne jedinice</string>
|
||||
<string name="label_gender">Spol</string>
|
||||
<string name="label_male">Muški</string>
|
||||
<string name="label_female">Ženski</string>
|
||||
<string name="label_goal_weight">Ciljana težina</string>
|
||||
<string name="label_goal_date">Ciljani datum</string>
|
||||
|
||||
<string name="label_title_user">korisnik</string>
|
||||
<string name="label_title_last_measurement">zadnje mjerenje</string>
|
||||
<string name="label_title_goal">Cilj</string>
|
||||
|
||||
<string name="label_import">Uvoz</string>
|
||||
<string name="label_export">Izvoz</string>
|
||||
<string name="label_delete_all">Izbriši sve</string>
|
||||
|
||||
<string name="error_value_required">Potrebna vrijednost</string>
|
||||
<string name="error_value_range">Vrijednost izvan granica</string>
|
||||
<string name="error_exporting">Greška u izvozu</string>
|
||||
<string name="error_importing">Greška u uvozu</string>
|
||||
<string name="error_user_name_required">Greška: Unesite ime</string>
|
||||
<string name="error_user_name_too_short">Greška: Ime mora imati barem 3 znaka</string>
|
||||
<string name="error_height_required">Greška: Unesite težinu</string>
|
||||
<string name="error_initial_weight_required">Greška: Unesite početnu težinu</string>
|
||||
<string name="error_goal_weight_required">Greška: Unesite ciljanu težinu</string>
|
||||
|
||||
<string name="info_data_deleted">Stavka obrisana</string>
|
||||
<string name="info_data_all_deleted">Sve stavke obrisane</string>
|
||||
<string name="info_data_exported">Izvezeno u</string>
|
||||
<string name="info_data_imported">Uvezeno iz</string>
|
||||
<string name="info_enter_value_in">Vrijednost u</string>
|
||||
<string name="info_enter_comment">Komentar (neobavezan)</string>
|
||||
<string name="info_is_visible">je vidljivo</string>
|
||||
<string name="info_is_not_visible">nije vidljivo</string>
|
||||
<string name="info_is_enable">uključeno</string>
|
||||
<string name="info_is_not_enable">isključeno</string>
|
||||
<string name="info_is_not_available">nedostupno</string>
|
||||
<string name="info_bluetooth_try_connection">Spajanje na:</string>
|
||||
<string name="info_bluetooth_connection_lost">Izgubljena Bluetooth veza</string>
|
||||
<string name="info_bluetooth_no_device">Nije pronađen nijedan uređaj</string>
|
||||
<string name="info_bluetooth_no_device_set">Nije odabran nijedan uređaj</string>
|
||||
<string name="info_bluetooth_connection_successful">Veza uspostavljena</string>
|
||||
<string name="info_bluetooth_init">Inicijalizacija Bluetooth uređaja</string>
|
||||
<string name="info_bluetooth_connection_error">Neočekivana Bluetooth greška</string>
|
||||
<string name="info_new_data_added">%1$.2f%2$s [%3$s] do %4$s dodan</string>
|
||||
<string name="info_new_data_duplicated">mjerenje s istim datumom i vremenom već postoji</string>
|
||||
|
||||
<string name="info_enter_user_name">Vaše ime</string>
|
||||
<string name="info_no_selected_user">Ne postoji niti jedan korisnik. Stvorite ga u postavkama.</string>
|
||||
<string name="info_no_evaluation_available">Nije moguće procijeniti vrijednost</string>
|
||||
|
||||
<string name="question_really_delete">Izbrisati stavku?</string>
|
||||
<string name="question_really_delete_all">Izbrisati sve stavke svih korisnika ?</string>
|
||||
<string name="question_really_delete_user">Izbrisati korisnika?</string>
|
||||
|
||||
<string name="label_bluetooth_title">Bluetooth</string>
|
||||
<string name="label_bluetooth_enable">Povezivanje sa vagom kod pokretanja</string>
|
||||
<string name="label_mergeWithLastMeasurement">Spoji sa zadnjim mjerenjem</string>
|
||||
<string name="label_bluetooth_searching">Tražim Vašu Bluetooth vagu</string>
|
||||
<string name="label_bluetooth_searching_finished">Pretraga završena</string>
|
||||
|
||||
<string name="label_enable_labels">Oznake</string>
|
||||
<string name="label_enable_points">Točka</string>
|
||||
|
||||
<string name="label_delete_confirmation">Potvrda brisanja</string>
|
||||
|
||||
<string name="label_category_measurement_database">Baza podataka</string>
|
||||
|
||||
<string name="label_maintainer">Održavatelj</string>
|
||||
<string name="label_website">Web stranica</string>
|
||||
<string name="label_license">Licenca</string>
|
||||
|
||||
<string name="label_automatic">automatski</string>
|
||||
|
||||
<string name="label_theme">Tema</string>
|
||||
|
||||
<string name="label_reminder">Podsjetnik</string>
|
||||
<string name="label_reminder_weekdays">Dani</string>
|
||||
<string name="label_reminder_time">Vrijeme</string>
|
||||
<string name="label_reminder_notify_text">Tekst obavijesti</string>
|
||||
<string name="default_value_reminder_notify_text">Vaganje za</string>
|
||||
|
||||
<string name="info_on_date">u</string>
|
||||
|
||||
<string name="Monday">Ponedjeljak</string>
|
||||
<string name="Tuesday">Utorak</string>
|
||||
<string name="Wednesday">Srijeda</string>
|
||||
<string name="Thursday">Četvrtak</string>
|
||||
<string name="Friday">Petak</string>
|
||||
<string name="Saturday">Subota</string>
|
||||
<string name="Sunday">Nedjelja</string>
|
||||
<string name="label_bt_device_no_support">uređaj nije podržan</string>
|
||||
<string name="label_exportBackup">Izvoz sigurnosne kopije</string>
|
||||
<string name="label_importBackup">Uvoz sigurnosne kopije</string>
|
||||
<string name="label_backup">Sigurnosna kopija</string>
|
||||
<string name="label_export_dir">Mapa za izvoz</string>
|
||||
<string name="label_not_found">nije pronađeno</string>
|
||||
<string name="label_ignoreOutOfRange">Zanemari podatke izvan granica</string>
|
||||
<string name="label_initial_weight">Početna težina</string>
|
||||
<string name="label_regression_line">Linija regresije</string>
|
||||
<string name="label_regression_line_degree">Polinom regresije</string>
|
||||
<string name="label_goal_line">Ciljana linija</string>
|
||||
<string name="label_help">Pomoć</string>
|
||||
|
||||
<string name="label_feedback_message_enjoying">Preporučate openScale?</string>
|
||||
<string name="label_feedback_message_rate_app">A ocjena na Google Play ili GitHub ?</string>
|
||||
<string name="label_feedback_message_issue">Želite li dati svoje mišljenje ?</string>
|
||||
<string name="label_feedback_message_yes">Da</string>
|
||||
<string name="label_feedback_message_no">Baš i ne</string>
|
||||
<string name="label_feedback_message_positive">U redu</string>
|
||||
<string name="label_feedback_message_negative">Ne, hvala</string>
|
||||
|
||||
<string name="error_max_scale_users">Dosegnut maksimalni broj korisnika</string>
|
||||
<string name="info_step_on_scale">Stanite bosi na vagu radi referentnog mjerenja</string>
|
||||
<string name="info_measuring">Mjerenje težine: %.2f</string>
|
||||
|
||||
<string name="customactivityoncrash_error_activity_error_occurred_explanation">Došlo je do neočekivane pogreške
|
||||
\n
|
||||
\nMolimo da prijavite problem sa podacima o grešci na
|
||||
\nhttps://github.com/oliexdev/openScale/issues</string>
|
||||
<string name="customactivityoncrash_error_activity_restart_app">Ponovo pokreni</string>
|
||||
<string name="customactivityoncrash_error_activity_close_app">Zatvori</string>
|
||||
<string name="customactivityoncrash_error_activity_error_details">Detalji o pogrešci</string>
|
||||
<string name="customactivityoncrash_error_activity_error_details_title">Detalji o pogrešci</string>
|
||||
<string name="customactivityoncrash_error_activity_error_details_close">Zatvori</string>
|
||||
<string name="customactivityoncrash_error_activity_error_details_copy">Kopiraj u međuspremnik</string>
|
||||
<string name="customactivityoncrash_error_activity_error_details_copied">Kopirano u međuspremnik</string>
|
||||
<string name="customactivityoncrash_error_activity_error_details_clipboard_label">Informacije o pogrešci</string>
|
||||
<string name="toggle_expand">Proširi</string>
|
||||
<string name="edit">Uredi</string>
|
||||
<string name="save">Spremi</string>
|
||||
|
||||
<string name="label_month_view">Mjesec</string>
|
||||
<string name="label_weeks_view">Tjedan</string>
|
||||
|
||||
<string name="permission_not_granted">Nije dozvoljeno</string>
|
||||
<string name="permission_bluetooth_info">Dozvola pristupu lokaciji je potrebna za traženje Bluetooth uređaja. Može se opet zabraniti pristup nakon pronalaska uređaja.</string>
|
||||
<string name="permission_bluetooth_info_title">Info</string>
|
||||
<string name="label_next">Sljedeći</string>
|
||||
<string name="label_long_press_drag_reorder">Dugo pritisnite i povucite mjerenja za promjenu redoslijeda</string>
|
||||
<string name="label_click_measurement_configure">Odaberite mjerenje za uređivanje</string>
|
||||
<string name="label_set_default_order">Zadan redosljed</string>
|
||||
<string name="label_export_overwrite">Prebriši prijašnji izvoz \"%s\"?</string>
|
||||
<string name="label_include_in_overview_graph">Prikaži na pregledu grafa</string>
|
||||
<string name="label_measurement_in_percent">Mjerenja u %</string>
|
||||
<string name="label_estimate_measurement">Procjenjeno mjerenje</string>
|
||||
<string name="label_estimation_formula">Formula za procjenu</string>
|
||||
<string name="language_default">Zadana postavka</string>
|
||||
<string name="label_language">Jezik</string>
|
||||
<string name="theme_light">Svjetlo</string>
|
||||
<string name="theme_dark">Tamno</string>
|
||||
<string name="label_contribute_translation">Pomognite prevesti</string>
|
||||
<string name="label_add_or_fix_translation">Dodaj novi ili popravi postojeći</string>
|
||||
<string name="label_overview_graph">Pregled grafikona</string>
|
||||
<string name="label_percent">Posto</string>
|
||||
<string name="label_estimated">Procjena</string>
|
||||
<string name="label_estimate_measurement_summary">Temeljeno na težini, visini, dobi, spolu, itd.</string>
|
||||
<string name="label_auto_backup">Automatska sigurnosna kopija</string>
|
||||
<string name="label_auto_backup_schedule">Raspored sigurnosne kopije</string>
|
||||
<string name="label_overwrite_backup">Prebriši prijašnju sigurnosnu kopiju</string>
|
||||
<string name="label_daily">dnevno</string>
|
||||
<string name="label_weekly">tjedno</string>
|
||||
<string name="label_monthly">mjesečno</string>
|
||||
<string name="label_scale_not_supported">Da li je Vaša vaga podržana ?</string>
|
||||
<string name="label_click_to_help_add_support">Pomognite da bi ju podržali</string>
|
||||
<string name="label_development">Razvoj</string>
|
||||
<string name="label_debug_log">Spremi log zapis u datoteku</string>
|
||||
<string name="label_your_bluetooth_scale">Vaša Bluetooth vaga</string>
|
||||
<string name="label_select_measurement">Odaberite mjerenje</string>
|
||||
<string name="label_select_user">Odaberite korisnika</string>
|
||||
<string name="label_configure_widget">Podesiti widget</string>
|
||||
<string name="label_visceral_fat">Visceralne masnoće</string>
|
||||
<string name="label_chest">Opseg prsa</string>
|
||||
<string name="label_thigh">Opseg bedra</string>
|
||||
<string name="label_biceps">Opseg bicepsa</string>
|
||||
<string name="label_neck">Opseg vrata</string>
|
||||
<string name="label_fat_caliper">Kaliper za mjerenje nabora</string>
|
||||
<string name="label_caliper1_male">Nabor na prsima</string>
|
||||
<string name="label_caliper2_male">Nabor na trbuhu</string>
|
||||
<string name="label_caliper3_male">Nabor na bedru</string>
|
||||
<string name="label_caliper1_female">Nabor na tricepsu</string>
|
||||
<string name="label_caliper2_female">Nabor na trbuhu</string>
|
||||
<string name="label_caliper3_female">Nabor na kukovima</string>
|
||||
<string name="label_measure_unit">Mjerna jedinica</string>
|
||||
<string name="label_activity_level">Razina aktivnosti</string>
|
||||
<string name="activity_level_sedentary">Sjedeći</string>
|
||||
<string name="activity_level_mild">Blag</string>
|
||||
<string name="activity_level_moderate">Umjeren</string>
|
||||
<string name="activity_level_heavy">Jak</string>
|
||||
<string name="activity_level_extreme">Ekstreman</string>
|
||||
</resources>
|
247
android_app/app/src/main/res/values-sl/strings.xml
Normal file
247
android_app/app/src/main/res/values-sl/strings.xml
Normal file
@@ -0,0 +1,247 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources><string name="title_overview">Pregled</string>
|
||||
<string name="title_graph">Diagram</string>
|
||||
<string name="title_table">Tabela</string>
|
||||
<string name="title_statistics">Statistika</string>
|
||||
<string name="title_users">Uporabniki</string>
|
||||
<string name="title_measurements">Meritve</string>
|
||||
<string name="title_about">Informacije</string>
|
||||
<string name="title_general">Splošno</string>
|
||||
|
||||
<string name="action_settings">Nastavitve</string>
|
||||
<string name="action_bluetooth_status">Bluetooth nastavitve</string>
|
||||
<string name="open_drawer">odpri</string>
|
||||
<string name="close_drawer">zapri</string>
|
||||
|
||||
<string name="label_cancel">Prekliči</string>
|
||||
<string name="label_ok">OK</string>
|
||||
<string name="label_yes">Da</string>
|
||||
<string name="label_no">Ne</string>
|
||||
<string name="label_delete">Izbriši</string>
|
||||
<string name="label_add_user">Dodaj uporabnika</string>
|
||||
<string name="label_add_measurement">Dodaj meritev</string>
|
||||
<string name="label_share">Deli</string>
|
||||
<string name="label_share_subject">CSV izvoz podatkov (%s)</string>
|
||||
|
||||
<string name="label_weight">Teža</string>
|
||||
<string name="label_bmi">Indeks telesne mase (ITM)</string>
|
||||
<string name="label_bmr">Bazalni metabolizem (BM)</string>
|
||||
<string name="label_fat">Telesna maščoba</string>
|
||||
<string name="label_water">Vsebnost vode</string>
|
||||
<string name="label_muscle">Mišice</string>
|
||||
<string name="label_lbm">Čvrsta telesna masa</string>
|
||||
<string name="label_waist">Obseg pasu</string>
|
||||
<string name="label_hip">Obseg bokov</string>
|
||||
<string name="label_comment">Komentar</string>
|
||||
<string name="label_whtr">Razmerje trebuh - višina</string>
|
||||
<string name="label_whr">Razmerje trebuh - boki</string>
|
||||
<string name="label_bone">Masa kosti</string>
|
||||
<string name="label_smartUserAssign">Pametna določitev uporabnika</string>
|
||||
|
||||
<plurals name="label_days">
|
||||
<item quantity="one">%d dan</item>
|
||||
<item quantity="two">%d dneva</item>
|
||||
<item quantity="few">%d dnevi</item>
|
||||
<item quantity="other"/>
|
||||
</plurals>
|
||||
<string name="label_last_week">Povprečje prejšnjega tedna</string>
|
||||
<string name="label_last_month">Povprečje prejšnjega meseca</string>
|
||||
<string name="label_weight_difference">Razlika teže</string>
|
||||
<string name="label_goal_date_is">Datum cilja:</string>
|
||||
<string name="label_days_left">Preostali dnevi</string>
|
||||
|
||||
<string name="label_date">Datum</string>
|
||||
<string name="label_time">Čas</string>
|
||||
<string name="label_birthday">Rojstni dan</string>
|
||||
<string name="label_user_name">Ime</string>
|
||||
<string name="label_height">Višina</string>
|
||||
<string name="label_scale_unit">Enote</string>
|
||||
<string name="label_gender">Spol</string>
|
||||
<string name="label_male">Moški</string>
|
||||
<string name="label_female">Ženski</string>
|
||||
<string name="label_goal_weight">Ciljna teža</string>
|
||||
<string name="label_goal_date">Ciljni datum</string>
|
||||
|
||||
<string name="label_title_user">uporabnik</string>
|
||||
<string name="label_title_last_measurement">zadnja meritev</string>
|
||||
<string name="label_title_goal">Cilj</string>
|
||||
|
||||
<string name="label_import">Uvozi</string>
|
||||
<string name="label_export">Izvozi</string>
|
||||
<string name="label_delete_all">Izbriši vse</string>
|
||||
|
||||
<string name="error_value_required">Zahtevana vrednost</string>
|
||||
<string name="error_value_range">Vrednost je zunaj meje</string>
|
||||
<string name="error_exporting">Napaka pri izvozu</string>
|
||||
<string name="error_importing">Napaka pri uvozu</string>
|
||||
<string name="error_user_name_required">Napaka: zahtevno ime</string>
|
||||
<string name="error_user_name_too_short">Napaka: Ime mora imeti vsaj 3 znake</string>
|
||||
<string name="error_height_required">Napaka: Višina zahtevana</string>
|
||||
<string name="error_initial_weight_required">Napaka: Začetna teža zahtevana</string>
|
||||
<string name="error_goal_weight_required">Napaka: Ciljna teža zahtevana</string>
|
||||
|
||||
<string name="info_data_deleted">Vnos je izbrisan</string>
|
||||
<string name="info_data_all_deleted">Vsi vnosi so izbrisani</string>
|
||||
<string name="info_data_exported">Izvozi v</string>
|
||||
<string name="info_data_imported">Uvozi iz</string>
|
||||
<string name="info_enter_value_in">Vrednost med</string>
|
||||
<string name="info_enter_comment">Komentiraj(ni zahtevano)</string>
|
||||
<string name="info_is_visible">je viden</string>
|
||||
<string name="info_is_not_visible">ni viden</string>
|
||||
<string name="info_is_enable">omogočeno</string>
|
||||
<string name="info_is_not_enable">onemogočeno</string>
|
||||
<string name="info_is_not_available">ni na voljo</string>
|
||||
<string name="info_bluetooth_try_connection">Poveži z:</string>
|
||||
<string name="info_bluetooth_connection_lost">Bluetooth povezava izgubljena</string>
|
||||
<string name="info_bluetooth_no_device">Nobena Bluetooth naprava ni bila najdena</string>
|
||||
<string name="info_bluetooth_no_device_set">Nobena Bluetooth naprava ni izbrana</string>
|
||||
<string name="info_bluetooth_connection_successful">Povezava vzpostavljena</string>
|
||||
<string name="info_bluetooth_init">Zaženi Bluetooth napravo</string>
|
||||
<string name="info_bluetooth_connection_error">Nepričakovana Bluetooth napaka</string>
|
||||
<string name="info_new_data_added">%1$.2f%2$s [%3$s] to %4$s dodan</string>
|
||||
<string name="info_new_data_duplicated">meritev z istim datumom in časom že obstaja</string>
|
||||
|
||||
<string name="info_enter_user_name">Tvoje ime</string>
|
||||
<string name="info_no_selected_user">Trenutno še ne obstaja noben uporabnik. Prosim, ustvarite ga v nastavitvah.</string>
|
||||
<string name="info_no_evaluation_available">Vrednosti ni bilo mogoče določiti</string>
|
||||
|
||||
<string name="question_really_delete">Želite izbrisati vnos?</string>
|
||||
<string name="question_really_delete_all">Želite izbrisati vse vnose vseh uporabnikov?</string>
|
||||
<string name="question_really_delete_user">Izbriši uporabnika?</string>
|
||||
|
||||
<string name="label_bluetooth_title">Bluetooth</string>
|
||||
<string name="label_bluetooth_enable">Poveži se s tehtnico ob zagonu aplikacije</string>
|
||||
<string name="label_mergeWithLastMeasurement">Združi z zadnjimi meritvami</string>
|
||||
<string name="label_bluetooth_searching">Iščem Bluetooth tehtnico</string>
|
||||
<string name="label_bluetooth_searching_finished">Iskanje končano</string>
|
||||
|
||||
<string name="label_enable_labels">Podatkovna oznaka</string>
|
||||
<string name="label_enable_points">Podatek</string>
|
||||
|
||||
<string name="label_delete_confirmation">Izbriši potrditev</string>
|
||||
|
||||
<string name="label_category_measurement_database">Baza meritev</string>
|
||||
|
||||
<string name="label_maintainer">Vzdrževalec</string>
|
||||
<string name="label_website">Spletna stran</string>
|
||||
<string name="label_license">Licenca</string>
|
||||
|
||||
<string name="label_automatic">Avtomatsko</string>
|
||||
|
||||
<string name="label_theme">Tema</string>
|
||||
|
||||
<string name="label_reminder">Opomnik</string>
|
||||
<string name="label_reminder_weekdays">Dnevi</string>
|
||||
<string name="label_reminder_time">Čas</string>
|
||||
<string name="label_reminder_notify_text">Besedilo obvestila</string>
|
||||
<string name="default_value_reminder_notify_text">Čas za tehtanje</string>
|
||||
|
||||
<string name="info_on_date">v</string>
|
||||
|
||||
<string name="Monday">ponedeljek</string>
|
||||
<string name="Tuesday">torek</string>
|
||||
<string name="Wednesday">sreda</string>
|
||||
<string name="Thursday">četrtek</string>
|
||||
<string name="Friday">petek</string>
|
||||
<string name="Saturday">sobota</string>
|
||||
<string name="Sunday">nedelja</string>
|
||||
<string name="label_bt_device_no_support">Naprava ni podprta</string>
|
||||
<string name="label_exportBackup">Izvozi varnostno datoteko</string>
|
||||
<string name="label_importBackup">Uvozi varnostno datoteko</string>
|
||||
<string name="label_backup">Varnostna datoteka</string>
|
||||
<string name="label_export_dir">Izvozi mapo</string>
|
||||
<string name="label_not_found">nič ni bilo najdeno</string>
|
||||
<string name="label_ignoreOutOfRange">Ignoriraj vrednosti zunaj meje</string>
|
||||
<string name="label_initial_weight">Začetna teža</string>
|
||||
<string name="label_regression_line">Regresijska krivulja teže</string>
|
||||
<string name="label_regression_line_degree">Regresija polinomske stopnje</string>
|
||||
<string name="label_goal_line">Ciljna krivulja</string>
|
||||
<string name="label_help">Pomoč</string>
|
||||
|
||||
<string name="label_feedback_message_enjoying">Vam je všeč openScale?</string>
|
||||
<string name="label_feedback_message_rate_app">Bi želeli podati oceno na Google Play ali GitHub?</string>
|
||||
<string name="label_feedback_message_issue">Bi nam radi priskrbeli povratno mnenje?</string>
|
||||
<string name="label_feedback_message_yes">Da</string>
|
||||
<string name="label_feedback_message_no">Ne zares</string>
|
||||
<string name="label_feedback_message_positive">OK</string>
|
||||
<string name="label_feedback_message_negative">Ne, hvala</string>
|
||||
|
||||
<string name="error_max_scale_users">Doseženo je bilo maksimalno število sočasnih uporabnikov tehtnice</string>
|
||||
<string name="info_step_on_scale">Prosim, stopite bosi na tehtnico za referenčno meritev</string>
|
||||
<string name="info_measuring">Izmerjena teža: %.2f</string>
|
||||
|
||||
<string name="customactivityoncrash_error_activity_error_occurred_explanation">Zgodila se je nepričakovana napaka.
|
||||
\n
|
||||
\nProsim, ustvarite novo težavo(issue) vključno s podatki o napaki na:
|
||||
\nhttps://github.com/oliexdev/openScale/issues</string>
|
||||
<string name="customactivityoncrash_error_activity_restart_app">Ponovno zaženi aplikacijo</string>
|
||||
<string name="customactivityoncrash_error_activity_close_app">Zapri aplikacijo</string>
|
||||
<string name="customactivityoncrash_error_activity_error_details">Podatki o napaki</string>
|
||||
<string name="customactivityoncrash_error_activity_error_details_title">Podatki o napaki</string>
|
||||
<string name="customactivityoncrash_error_activity_error_details_close">Zapri</string>
|
||||
<string name="customactivityoncrash_error_activity_error_details_copy">Kopiraj v odložišče</string>
|
||||
<string name="customactivityoncrash_error_activity_error_details_copied">Kopirano v odložišče</string>
|
||||
<string name="customactivityoncrash_error_activity_error_details_clipboard_label">Informacije o napaki</string>
|
||||
<string name="toggle_expand">Spremeni v razširjen pogled</string>
|
||||
<string name="edit">Uredi</string>
|
||||
<string name="save">Shrani</string>
|
||||
|
||||
<string name="label_month_view">Mesečni pogled</string>
|
||||
<string name="label_weeks_view">Tedenski pogled</string>
|
||||
|
||||
<string name="permission_not_granted">Dovoljenja niso bila pridobljena</string>
|
||||
<string name="permission_bluetooth_info">Dovoljenje je potrebno za približno določanje lokacije Bluetooth naprave. Dovoljenje je lahko odvzeto takoj po tem, ko je naprava najdena.</string>
|
||||
<string name="permission_bluetooth_info_title">Informacije</string>
|
||||
<string name="label_next">Naprej</string>
|
||||
<string name="label_long_press_drag_reorder">Dolgo držite in potegnite meritve za prerazporejanja</string>
|
||||
<string name="label_click_measurement_configure">Kliknite meritve za rekonfiguracijo</string>
|
||||
<string name="label_set_default_order">Nastavi privzet vrstni red</string>
|
||||
<string name="label_export_overwrite">Zamenjaj prejšnji izvoz \"%s\"?</string>
|
||||
<string name="label_include_in_overview_graph">Vključi v pregledni graf</string>
|
||||
<string name="label_measurement_in_percent">Meritev v %</string>
|
||||
<string name="label_estimate_measurement">Ocena meritve</string>
|
||||
<string name="label_estimation_formula">Ocenjevalna formula</string>
|
||||
<string name="language_default">Privzete nastavitve sistema</string>
|
||||
<string name="label_language">Jezik</string>
|
||||
<string name="theme_light">Svetlo</string>
|
||||
<string name="theme_dark">Temno</string>
|
||||
<string name="label_contribute_translation">Prispevaj prevod</string>
|
||||
<string name="label_add_or_fix_translation">Dodaj ali popravi obstoječega</string>
|
||||
<string name="label_overview_graph">Pregledni graf</string>
|
||||
<string name="label_percent">Procent</string>
|
||||
<string name="label_estimated">Ocenjeno</string>
|
||||
<string name="label_estimate_measurement_summary">Bazirano na teži, višini, starosti, spolu, ...</string>
|
||||
<string name="label_auto_backup">Samodejna varnostna kopija</string>
|
||||
<string name="label_auto_backup_schedule">Urnik varnostnih kopij</string>
|
||||
<string name="label_overwrite_backup">Zamenjaj prejšnjo varnostno kopijo</string>
|
||||
<string name="label_daily">dnevno</string>
|
||||
<string name="label_weekly">tedensko</string>
|
||||
<string name="label_monthly">mesečno</string>
|
||||
<string name="label_scale_not_supported">Je vaša tehtnica že podprta?</string>
|
||||
<string name="label_click_to_help_add_support">Kliknite tukaj, če želite pomagati</string>
|
||||
<string name="label_development">Razvoj</string>
|
||||
<string name="label_debug_log">Shrani poročilo o napakah v datoteko</string>
|
||||
<string name="label_your_bluetooth_scale">Vaša Bluetooth tehtnica</string>
|
||||
<string name="label_select_measurement">Izberi meritev</string>
|
||||
<string name="label_select_user">Izberi uporabnika</string>
|
||||
<string name="label_configure_widget">Konfiguriraj pripomoček(widget)</string>
|
||||
<string name="label_visceral_fat">Visceralna maščoba</string>
|
||||
<string name="label_chest">Obseg prsnega koša</string>
|
||||
<string name="label_thigh">Obseg stegen</string>
|
||||
<string name="label_biceps">Obseg bicepsa</string>
|
||||
<string name="label_neck">Obseg vratu</string>
|
||||
<string name="label_fat_caliper">Kožna guba</string>
|
||||
<string name="label_caliper1_male">Prsna kožna guba</string>
|
||||
<string name="label_caliper2_male">Trebušna kožna guba</string>
|
||||
<string name="label_caliper3_male">Stegenska kožna guba</string>
|
||||
<string name="label_caliper1_female">Kožna guba tricepsa</string>
|
||||
<string name="label_caliper2_female">Trebušna kožna guba</string>
|
||||
<string name="label_caliper3_female">Kožna guba bokov</string>
|
||||
<string name="label_measure_unit">Enote meritev</string>
|
||||
<string name="label_activity_level">Stopnja aktivnosti</string>
|
||||
<string name="activity_level_sedentary">Sedeč</string>
|
||||
<string name="activity_level_mild">Blag</string>
|
||||
<string name="activity_level_moderate">Zmeren</string>
|
||||
<string name="activity_level_heavy">Hud</string>
|
||||
<string name="activity_level_extreme">Ekstremna</string>
|
||||
</resources>
|
@@ -40,8 +40,8 @@
|
||||
<plurals name="label_days">
|
||||
<item quantity="other">日</item>
|
||||
</plurals>
|
||||
<string name="label_last_week">過去7日</string>
|
||||
<string name="label_last_month">過去30日</string>
|
||||
<string name="label_last_week">平均過去一週</string>
|
||||
<string name="label_last_month">平均過去一個月</string>
|
||||
<string name="label_weight_difference">體重差距</string>
|
||||
<string name="label_goal_date_is">目標日期:</string>
|
||||
<string name="label_days_left">剩餘日數</string>
|
||||
@@ -216,4 +216,5 @@
|
||||
<string name="label_monthly">每月</string>
|
||||
<string name="label_development">開發</string>
|
||||
<string name="label_debug_log">除錯記錄存檔</string>
|
||||
</resources>
|
||||
<string name="label_add_measurement">增加測量</string>
|
||||
</resources>
|
||||
|
@@ -6,7 +6,9 @@
|
||||
<!-- Native names from https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes -->
|
||||
<item>Catalan (català)</item>
|
||||
<item>Chinese (traditional; 中文 (繁體))</item>
|
||||
<item>Croatian (hrvatski jezik)</item>
|
||||
<item>Czech (čeština)</item>
|
||||
<item>Danish (dansk)</item>
|
||||
<item>Dutch (Nederlands)</item>
|
||||
<item>English</item>
|
||||
<item>French (français)</item>
|
||||
@@ -21,6 +23,7 @@
|
||||
<item>Romanian (Română)</item>
|
||||
<item>Russian (русский)</item>
|
||||
<item>Slovak (Slovenčina)</item>
|
||||
<item>Slovenian (Slovenski Jezik)</item>
|
||||
<item>Spanish (Español)</item>
|
||||
<item>Swedish (Svenska)</item>
|
||||
<item>Turkish (Türkçe)</item>
|
||||
@@ -31,7 +34,9 @@
|
||||
<!-- The order below should match the language order above -->
|
||||
<item>ca</item>
|
||||
<item>zh-TW</item>
|
||||
<item>hr</item>
|
||||
<item>cs</item>
|
||||
<item>da</item>
|
||||
<item>nl</item>
|
||||
<item>en</item>
|
||||
<item>fr</item>
|
||||
@@ -46,6 +51,7 @@
|
||||
<item>ro</item>
|
||||
<item>ru</item>
|
||||
<item>sk</item>
|
||||
<item>sl</item>
|
||||
<item>es</item>
|
||||
<item>sv</item>
|
||||
<item>tr</item>
|
||||
|
@@ -5,7 +5,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.1.2'
|
||||
classpath 'com.android.tools.build:gradle:3.1.3'
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -253,6 +253,13 @@ void after_sleep_event()
|
||||
Serial.println("$I$ Successful sync to RTC clock");
|
||||
}
|
||||
|
||||
print_date_time();
|
||||
|
||||
Serial.println("$I$ openScale MCU ready!");
|
||||
}
|
||||
|
||||
void print_date_time()
|
||||
{
|
||||
Serial.print("$I$ Time: ");
|
||||
Serial.print(hour());
|
||||
Serial.write(':');
|
||||
@@ -266,11 +273,8 @@ void after_sleep_event()
|
||||
Serial.write('/');
|
||||
Serial.print(year());
|
||||
Serial.println();
|
||||
|
||||
Serial.println("$I$ openScale MCU ready!");
|
||||
}
|
||||
|
||||
|
||||
void check_display_activity()
|
||||
{
|
||||
if (no_activity_cycles > MAX_NO_ACTIVITY_CYCLES)
|
||||
@@ -392,6 +396,8 @@ void send_scale_data()
|
||||
Serial.print('\n');
|
||||
}
|
||||
|
||||
Serial.println("$F$ Scale data sent");
|
||||
|
||||
}
|
||||
|
||||
void clear_scale_data()
|
||||
@@ -461,8 +467,12 @@ void loop()
|
||||
seg_samples_3.getMedian(seg_value_3);
|
||||
seg_samples_4.getMedian(seg_value_4);
|
||||
|
||||
if (seg_value_4 == ' ') {
|
||||
if (seg_value_4 == ' ' || seg_value_4 == '1' || seg_value_4 == '2') {
|
||||
measured_weight = char_to_int(seg_value_1) + char_to_int(seg_value_2)*10 + char_to_int(seg_value_3)*100;
|
||||
|
||||
if (seg_value_4 == '1' || seg_value_4 == '2') {
|
||||
measured_weight += char_to_int(seg_value_4)*1000;
|
||||
}
|
||||
}
|
||||
|
||||
if (seg_value_4 == 'F') {
|
||||
@@ -494,12 +504,19 @@ void loop()
|
||||
switch(command)
|
||||
{
|
||||
case '0':
|
||||
Serial.println("$I$ openScale MCU Version 1.0");
|
||||
Serial.println("$I$ openScale MCU Version 1.1");
|
||||
break;
|
||||
case '1':
|
||||
Serial.println("$I$ Sending scale data!");
|
||||
send_scale_data();
|
||||
break;
|
||||
case '2':
|
||||
set_rtc_time();
|
||||
break;
|
||||
case '3':
|
||||
Serial.println("$I$ Print RTC Time");
|
||||
print_date_time();
|
||||
break;
|
||||
case '9':
|
||||
clear_scale_data();
|
||||
Serial.println("$I$ Scale data cleared!");
|
||||
@@ -508,6 +525,48 @@ void loop()
|
||||
}
|
||||
}
|
||||
|
||||
void set_rtc_time() {
|
||||
static time_t tLast;
|
||||
time_t t;
|
||||
tmElements_t tm;
|
||||
|
||||
setSyncProvider(RTC.get);
|
||||
|
||||
boolean param_finished = false;
|
||||
|
||||
while (!param_finished) {
|
||||
//check for input to set the RTC, minimum length is 12, i.e. yy,m,d,h,m,s
|
||||
if (Serial.available() >= 12) {
|
||||
//note that the tmElements_t Year member is an offset from 1970,
|
||||
//but the RTC wants the last two digits of the calendar year.
|
||||
//use the convenience macros from Time.h to do the conversions.
|
||||
int y = Serial.parseInt();
|
||||
if (y >= 100 && y < 1000)
|
||||
Serial.println("$E$ Error: Year must be two digits or four digits!");
|
||||
else {
|
||||
if (y >= 1000)
|
||||
tm.Year = CalendarYrToTm(y);
|
||||
else //(y < 100)
|
||||
tm.Year = y2kYearToTm(y);
|
||||
tm.Month = Serial.parseInt();
|
||||
tm.Day = Serial.parseInt();
|
||||
tm.Hour = Serial.parseInt();
|
||||
tm.Minute = Serial.parseInt();
|
||||
tm.Second = Serial.parseInt();
|
||||
t = makeTime(tm);
|
||||
RTC.set(t); //use the time_t value to ensure correct weekday is set
|
||||
setTime(t);
|
||||
Serial.println("$I$ RTC set to: ");
|
||||
print_date_time();
|
||||
//dump any extraneous input
|
||||
while (Serial.available() > 0) Serial.read();
|
||||
|
||||
param_finished = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int char_to_int(char c)
|
||||
{
|
||||
if (c == ' ')
|
||||
|
BIN
doc/custom_scale/hm_10/HM_10_Manual.pdf
Normal file
BIN
doc/custom_scale/hm_10/HM_10_Manual.pdf
Normal file
Binary file not shown.
BIN
doc/custom_scale/parts/smart_bluetooth_back.jpg
Normal file
BIN
doc/custom_scale/parts/smart_bluetooth_back.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 234 KiB |
BIN
doc/custom_scale/parts/smart_bluetooth_front.jpg
Normal file
BIN
doc/custom_scale/parts/smart_bluetooth_front.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 227 KiB |
BIN
doc/scales/accuway.jpg
Normal file
BIN
doc/scales/accuway.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 188 KiB |
BIN
doc/scales/excelvan_cf366ble.jpg
Normal file
BIN
doc/scales/excelvan_cf366ble.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 131 KiB |
Reference in New Issue
Block a user