mirror of
https://github.com/oliexdev/openScale.git
synced 2025-08-31 03:59:56 +02:00
Merge branch 'master' of https://github.com/oliexdev/openScale
This commit is contained in:
@@ -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,16 +296,21 @@ public abstract class BluetoothCommunication {
|
||||
protected void setIndicationOn(UUID service, UUID characteristic, UUID descriptor) {
|
||||
Timber.d("Set indication on for %s", characteristic);
|
||||
|
||||
BluetoothGattCharacteristic gattCharacteristic =
|
||||
bluetoothGatt.getService(service).getCharacteristic(characteristic);
|
||||
bluetoothGatt.setCharacteristicNotification(gattCharacteristic, true);
|
||||
try {
|
||||
BluetoothGattCharacteristic gattCharacteristic =
|
||||
bluetoothGatt.getService(service).getCharacteristic(characteristic);
|
||||
bluetoothGatt.setCharacteristicNotification(gattCharacteristic, true);
|
||||
|
||||
synchronized (lock) {
|
||||
descriptorRequestQueue.add(
|
||||
new GattObjectValue<>(
|
||||
gattCharacteristic.getDescriptor(descriptor),
|
||||
BluetoothGattDescriptor.ENABLE_INDICATION_VALUE));
|
||||
handleRequests();
|
||||
synchronized (lock) {
|
||||
descriptorRequestQueue.add(
|
||||
new GattObjectValue<>(
|
||||
gattCharacteristic.getDescriptor(descriptor),
|
||||
BluetoothGattDescriptor.ENABLE_INDICATION_VALUE));
|
||||
handleRequests();
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
Timber.e(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,16 +323,21 @@ public abstract class BluetoothCommunication {
|
||||
protected void setNotificationOn(UUID service, UUID characteristic, UUID descriptor) {
|
||||
Timber.d("Set notification on for %s", characteristic);
|
||||
|
||||
BluetoothGattCharacteristic gattCharacteristic =
|
||||
bluetoothGatt.getService(service).getCharacteristic(characteristic);
|
||||
bluetoothGatt.setCharacteristicNotification(gattCharacteristic, true);
|
||||
try {
|
||||
BluetoothGattCharacteristic gattCharacteristic =
|
||||
bluetoothGatt.getService(service).getCharacteristic(characteristic);
|
||||
bluetoothGatt.setCharacteristicNotification(gattCharacteristic, true);
|
||||
|
||||
synchronized (lock) {
|
||||
descriptorRequestQueue.add(
|
||||
new GattObjectValue<>(
|
||||
gattCharacteristic.getDescriptor(descriptor),
|
||||
BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE));
|
||||
handleRequests();
|
||||
synchronized (lock) {
|
||||
descriptorRequestQueue.add(
|
||||
new GattObjectValue<>(
|
||||
gattCharacteristic.getDescriptor(descriptor),
|
||||
BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE));
|
||||
handleRequests();
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
Timber.e(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,15 +525,32 @@ 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;
|
||||
}
|
||||
|
||||
bluetoothGatt.close();
|
||||
bluetoothGatt = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 run() {
|
||||
synchronized (lock) {
|
||||
openRequest = false;
|
||||
handleRequests();
|
||||
}
|
||||
}
|
||||
}, 60);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDescriptorWrite(BluetoothGatt gatt,
|
||||
BluetoothGattDescriptor descriptor,
|
||||
int status) {
|
||||
synchronized (lock) {
|
||||
openRequest = false;
|
||||
handleRequests();
|
||||
}
|
||||
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,12 +42,33 @@ public class BluetoothCustomOpenScale extends BluetoothCommunication {
|
||||
|
||||
@Override
|
||||
public String driverName() {
|
||||
return "Custom Open Scale";
|
||||
return "Custom openScale";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nextInitCmd(int stateNr) {
|
||||
return false;
|
||||
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
|
||||
@@ -62,211 +81,94 @@ 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);
|
||||
|
||||
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);
|
||||
if (character == '\n') {
|
||||
parseBtString(string_data);
|
||||
string_data = new String();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ScaleMeasurement parseBtString(String btString) throws IOException {
|
||||
ScaleMeasurement scaleBtData = new ScaleMeasurement();
|
||||
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");
|
||||
}
|
||||
|
||||
String btMsg = btString.substring(3, btString.length()); // message string
|
||||
|
||||
switch (btString.charAt(1)) {
|
||||
case 'I':
|
||||
Timber.i("MCU Information: %s", btMsg);
|
||||
break;
|
||||
case 'E':
|
||||
Timber.e("MCU Error: %s", btMsg);
|
||||
break;
|
||||
case 'S':
|
||||
Timber.i("MCU stored data size: %s", btMsg);
|
||||
break;
|
||||
case 'D':
|
||||
String[] csvField = btMsg.split(",");
|
||||
|
||||
try {
|
||||
int checksum = 0;
|
||||
|
||||
checksum ^= Integer.parseInt(csvField[0]);
|
||||
checksum ^= Integer.parseInt(csvField[1]);
|
||||
checksum ^= Integer.parseInt(csvField[2]);
|
||||
checksum ^= Integer.parseInt(csvField[3]);
|
||||
checksum ^= Integer.parseInt(csvField[4]);
|
||||
checksum ^= Integer.parseInt(csvField[5]);
|
||||
checksum ^= (int) Float.parseFloat(csvField[6]);
|
||||
checksum ^= (int) Float.parseFloat(csvField[7]);
|
||||
checksum ^= (int) Float.parseFloat(csvField[8]);
|
||||
checksum ^= (int) Float.parseFloat(csvField[9]);
|
||||
|
||||
int btChecksum = Integer.parseInt(csvField[10]);
|
||||
|
||||
if (checksum == btChecksum) {
|
||||
scaleBtData.setId(-1);
|
||||
scaleBtData.setUserId(Integer.parseInt(csvField[0]));
|
||||
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));
|
||||
|
||||
scaleBtData.setWeight(Float.parseFloat(csvField[6]));
|
||||
scaleBtData.setFat(Float.parseFloat(csvField[7]));
|
||||
scaleBtData.setWater(Float.parseFloat(csvField[8]));
|
||||
scaleBtData.setMuscle(Float.parseFloat(csvField[9]));
|
||||
|
||||
return scaleBtData;
|
||||
} else {
|
||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error calculated checksum (" + checksum + ") and received checksum (" + btChecksum + ") is different");
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error while decoding bluetooth date string (" + e.getMessage() + ")");
|
||||
} catch (NumberFormatException e) {
|
||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error while decoding a number of bluetooth string (" + e.getMessage() + ")");
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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: " + btString);
|
||||
}
|
||||
|
||||
String btMsg = btString.substring(3, btString.length()); // message string
|
||||
|
||||
switch (btString.charAt(1)) {
|
||||
case 'I':
|
||||
Timber.d("MCU Information: %s", btMsg);
|
||||
break;
|
||||
case 'E':
|
||||
Timber.e("MCU Error: %s", btMsg);
|
||||
break;
|
||||
case 'S':
|
||||
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(",");
|
||||
|
||||
try {
|
||||
int checksum = 0;
|
||||
|
||||
checksum ^= Integer.parseInt(csvField[0]);
|
||||
checksum ^= Integer.parseInt(csvField[1]);
|
||||
checksum ^= Integer.parseInt(csvField[2]);
|
||||
checksum ^= Integer.parseInt(csvField[3]);
|
||||
checksum ^= Integer.parseInt(csvField[4]);
|
||||
checksum ^= Integer.parseInt(csvField[5]);
|
||||
checksum ^= (int) Float.parseFloat(csvField[6]);
|
||||
checksum ^= (int) Float.parseFloat(csvField[7]);
|
||||
checksum ^= (int) Float.parseFloat(csvField[8]);
|
||||
checksum ^= (int) Float.parseFloat(csvField[9]);
|
||||
|
||||
int btChecksum = Integer.parseInt(csvField[10]);
|
||||
|
||||
if (checksum == btChecksum) {
|
||||
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));
|
||||
|
||||
scaleBtData.setWeight(Float.parseFloat(csvField[6]));
|
||||
scaleBtData.setFat(Float.parseFloat(csvField[7]));
|
||||
scaleBtData.setWater(Float.parseFloat(csvField[8]));
|
||||
scaleBtData.setMuscle(Float.parseFloat(csvField[9]));
|
||||
|
||||
addScaleData(scaleBtData);
|
||||
} else {
|
||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error calculated checksum (" + checksum + ") and received checksum (" + btChecksum + ") is different");
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error while decoding bluetooth date string (" + e.getMessage() + ")");
|
||||
} catch (NumberFormatException e) {
|
||||
setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "Error while decoding a number of bluetooth string (" + e.getMessage() + ")");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
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;
|
||||
|
||||
scaleBtData.setWeight(weight);
|
||||
ScaleMeasurement scaleBtData = new ScaleMeasurement();
|
||||
scaleBtData.setWeight(weight);
|
||||
|
||||
addScaleData(scaleBtData);
|
||||
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,7 +353,12 @@ public class ScaleMeasurement implements Cloneable {
|
||||
}
|
||||
|
||||
public void setComment(String comment) {
|
||||
this.comment = comment;
|
||||
if (comment == null) {
|
||||
this.comment = "";
|
||||
}
|
||||
else {
|
||||
this.comment = comment;
|
||||
}
|
||||
}
|
||||
|
||||
public float getBMI(float body_height) {
|
||||
|
@@ -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()) {
|
||||
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.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
|
||||
headerIcon.getLayoutParams().height = pxImageDp(20);
|
||||
|
||||
tableHeaderView.addView(headerIcon);
|
||||
|
||||
visibleMeasurements.add(measurement);
|
||||
if (!measurement.isVisible()) {
|
||||
continue;
|
||||
}
|
||||
ImageView headerIcon = new ImageView(tableView.getContext());
|
||||
headerIcon.setImageDrawable(measurement.getIcon());
|
||||
headerIcon.setLayoutParams(new TableRow.LayoutParams(0, iconHeight, 1));
|
||||
headerIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
|
||||
|
||||
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);
|
||||
}
|
||||
final int screenSize = getResources().getConfiguration()
|
||||
.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
|
||||
final boolean isSmallScreen =
|
||||
screenSize != Configuration.SCREENLAYOUT_SIZE_XLARGE
|
||||
&& screenSize != Configuration.SCREENLAYOUT_SIZE_LARGE;
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return scaleMeasurements.get(position).getId();
|
||||
}
|
||||
final int count = viewType == VIEW_TYPE_YEAR ? 1 : visibleMeasurements.size();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
TextView column = new TextView(getContext());
|
||||
column.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
0, ViewGroup.LayoutParams.WRAP_CONTENT, 1));
|
||||
|
||||
@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 boolean isSmallScreen =
|
||||
screenSize != Configuration.SCREENLAYOUT_SIZE_XLARGE
|
||||
&& screenSize != Configuration.SCREENLAYOUT_SIZE_LARGE;
|
||||
|
||||
for (int i = 0; i < visibleMeasurements.size(); ++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);
|
||||
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 {
|
||||
column.setPadding(0, 10, 0, 10);
|
||||
column.setGravity(Gravity.CENTER);
|
||||
column.setTextSize(COMPLEX_UNIT_DIP, 16);
|
||||
}
|
||||
|
||||
row.addView(column);
|
||||
}
|
||||
else {
|
||||
row = (LinearLayout) convertView;
|
||||
|
||||
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="2dp"
|
||||
android:background="?android:attr/listDivider" />
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/tableDataView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="0.9"
|
||||
android:gravity="bottom"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ListView
|
||||
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'
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user