1
0
mirror of https://github.com/oliexdev/openScale.git synced 2025-08-24 09:13:04 +02:00

Merge branch 'master' into mgb_scales

# Conflicts:
#	android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java
This commit is contained in:
DreamNik
2017-11-05 14:36:05 +03:00
29 changed files with 1260 additions and 38 deletions

View File

@@ -27,6 +27,8 @@ import android.widget.Toast;
import com.health.openscale.R;
import com.health.openscale.core.alarm.AlarmHandler;
import com.health.openscale.core.bluetooth.BluetoothCommunication;
import com.health.openscale.core.bodymetric.EstimatedFatMetric;
import com.health.openscale.core.bodymetric.EstimatedWaterMetric;
import com.health.openscale.core.database.ScaleDatabase;
import com.health.openscale.core.database.ScaleUserDatabase;
import com.health.openscale.core.datatypes.ScaleData;
@@ -93,7 +95,7 @@ public class OpenScale {
scaleUser.body_height = body_height;
scaleUser.scale_unit = scale_unit;
scaleUser.gender = gender;
scaleUser.initial_weight = initial_weight;
scaleUser.setConvertedInitialWeight(initial_weight);
scaleUser.goal_weight = goal_weight;
scaleUser.goal_date = new SimpleDateFormat("dd.MM.yyyy").parse(goal_date);
@@ -150,7 +152,7 @@ public class OpenScale {
scaleUser.body_height = body_height;
scaleUser.scale_unit = scale_unit;
scaleUser.gender = gender;
scaleUser.initial_weight = initial_weight;
scaleUser.setConvertedInitialWeight(initial_weight);
scaleUser.goal_weight = goal_weight;
scaleUser.goal_date = new SimpleDateFormat("dd.MM.yyyy").parse(goal_date);
} catch (ParseException e) {
@@ -188,6 +190,18 @@ public class OpenScale {
}
}
if (prefs.getBoolean("estimateFatEnable", false)) {
EstimatedFatMetric fatMetric = EstimatedFatMetric.getEstimatedFatMetric(EstimatedFatMetric.FORMULA_FAT.valueOf(prefs.getString("estimateFatFormula", "BF_DEURENBERG_II")));
scaleData.setFat(fatMetric.getFat(getScaleUser(scaleData.getUserId()), scaleData));
}
if (prefs.getBoolean("estimateWaterEnable", false)) {
EstimatedWaterMetric waterMetric = EstimatedWaterMetric.getEstimatedWaterMetric(EstimatedWaterMetric.FORMULA_WATER.valueOf(prefs.getString("estimateWaterFormula", "TBW_BEHNKE")));
scaleData.setWater(waterMetric.getWater(getScaleUser(scaleData.getUserId()), scaleData));
}
if (scaleDB.insertEntry(scaleData)) {
ScaleUser scaleUser = getScaleUser(scaleData.getUserId());
@@ -212,7 +226,7 @@ public class OpenScale {
if (scaleUserData.size() > 0) {
lastWeight = scaleUserData.get(0).getWeight();
} else {
lastWeight = scaleUser.get(i).initial_weight;
lastWeight = scaleUser.get(i).getInitialWeight();
}
if ((lastWeight - range) <= weight && (lastWeight + range) >= weight) {

View File

@@ -38,7 +38,7 @@ public abstract class BluetoothCommunication {
BT_CONNECTION_LOST, BT_NO_DEVICE_FOUND, BT_UNEXPECTED_ERROR, BT_SCALE_MESSAGE
};
public enum BT_MACHINE_STATE {BT_INIT_STATE, BT_CMD_STATE, BT_CLEANUP_STATE}
public enum BT_DEVICE_ID {CUSTOM_OPENSCALE, MI_SCALE_V1, SANITAS_SBF70, MEDISANA_BS444, DIGOO_DGS038H, EXCELVANT_CF369BLE, YUNMAI_MINI,MGB}
public enum BT_DEVICE_ID {CUSTOM_OPENSCALE, MI_SCALE_V1, MI_SCALE_V2, SANITAS_SBF70, MEDISANA_BS444, DIGOO_DGS038H, EXCELVANT_CF369BLE, YUNMAI_MINI, YUNMAI_SE, MGB}
protected Context context;
@@ -81,6 +81,8 @@ public abstract class BluetoothCommunication {
return new BluetoothCustomOpenScale(context);
case MI_SCALE_V1:
return new BluetoothMiScale(context);
case MI_SCALE_V2:
return new BluetoothMiScale2(context);
case SANITAS_SBF70:
return new BluetoothSanitasSbf70(context);
case MEDISANA_BS444:
@@ -91,8 +93,10 @@ public abstract class BluetoothCommunication {
return new BluetoothExcelvanCF369BLE(context);
case YUNMAI_MINI:
return new BluetoothYunmaiMini(context);
case MGB:
return new BluetoothMGB(context);
case YUNMAI_SE:
return new BluetoothYunmaiSE(context);
case MGB:
return new BluetoothMGB(context);
}
return null;

View File

@@ -0,0 +1,262 @@
/* Copyright (C) 2014 olie.xdev <olie.xdev@googlemail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.health.openscale.core.bluetooth;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
import com.health.openscale.core.OpenScale;
import com.health.openscale.core.datatypes.ScaleData;
import com.health.openscale.core.datatypes.ScaleUser;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Random;
import java.util.UUID;
import static com.health.openscale.core.bluetooth.BluetoothCommunication.BT_STATUS_CODE.BT_UNEXPECTED_ERROR;
public class BluetoothMiScale2 extends BluetoothCommunication {
private final UUID WEIGHT_MEASUREMENT_SERVICE = UUID.fromString("0000181b-0000-1000-8000-00805f9b34fb");
private final UUID WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC = UUID.fromString("00002a2f-0000-3512-2118-0009af100700");
private final UUID WEIGHT_MEASUREMENT_TIME_CHARACTERISTIC = UUID.fromString("00002a2b-0000-1000-8000-00805f9b34fb");
private final UUID WEIGHT_MEASUREMENT_CONFIG = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
private final UUID WEIGHT_MEASUREMENT_BODY_COMPOSITION_FEATURE = UUID.fromString("00002a9b-0000-1000-8000-00805f9b34fb");
private final UUID WEIGHT_MEASUREMENT_BODY_COMPOSITION_MEASUREMENT = UUID.fromString("00002a9c-0000-1000-8000-00805f9b34fb");
private final UUID WEIGHT_CUSTOM_SERVICE = UUID.fromString("00001530-0000-3512-2118-0009af100700");
private final UUID WEIGHT_CUSTOM_CONFIG = UUID.fromString("00001542-0000-3512-2118-0009af100700");
public BluetoothMiScale2(Context context) {
super(context);
}
@Override
public String deviceName() {
return "Xiaomi Mi Scale v2";
}
@Override
public String defaultDeviceName() {
return "MIBCS";
}
@Override
public void onBluetoothDataChange(BluetoothGatt bluetoothGatt, BluetoothGattCharacteristic gattCharacteristic) {
final byte[] data = gattCharacteristic.getValue();
if (data != null && data.length > 0) {
Log.d("MIScale_v2", "DataChange hex data: "+ byteInHex(data));
// Stop command from mi scale received
if (data[0] == 0x03) {
setBtMachineState(BT_MACHINE_STATE.BT_CLEANUP_STATE);
}
if (data.length == 26) {
final byte[] firstWeight = Arrays.copyOfRange(data, 0, 10);
final byte[] secondWeight = Arrays.copyOfRange(data, 10, 20);
parseBytes(firstWeight);
parseBytes(secondWeight);
}
if (data.length == 13) {
parseBytes(data);
}
}
}
@Override
boolean nextInitCmd(int stateNr) {
switch (stateNr) {
case 0:
// set scale units
final ScaleUser selectedUser = OpenScale.getInstance(context).getSelectedScaleUser();
byte[] setUnitCmd = new byte[]{(byte)0x06, (byte)0x04, (byte)0x00, (byte) selectedUser.scale_unit};
writeBytes(WEIGHT_CUSTOM_SERVICE, WEIGHT_CUSTOM_CONFIG, setUnitCmd);
break;
case 1:
// set current time
Calendar currentDateTime = Calendar.getInstance();
int year = currentDateTime.get(Calendar.YEAR);
byte month = (byte)(currentDateTime.get(Calendar.MONTH)+1);
byte day = (byte)currentDateTime.get(Calendar.DAY_OF_MONTH);
byte hour = (byte)currentDateTime.get(Calendar.HOUR_OF_DAY);
byte min = (byte)currentDateTime.get(Calendar.MINUTE);
byte sec = (byte)currentDateTime.get(Calendar.SECOND);
byte[] dateTimeByte = {(byte)(year), (byte)(year >> 8), month, day, hour, min, sec, 0x03, 0x00, 0x00};
writeBytes(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_TIME_CHARACTERISTIC, dateTimeByte);
break;
case 2:
// set notification on for weight measurement history
setNotificationOn(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, WEIGHT_MEASUREMENT_CONFIG);
break;
default:
return false;
}
return true;
}
@Override
boolean nextBluetoothCmd(int stateNr) {
switch (stateNr) {
case 0:
// configure scale to get only last measurements
int uniqueNumber = getUniqueNumber();
byte[] userIdentifier = new byte[]{(byte)0x01, (byte)0xFF, (byte)0xFF, (byte) ((uniqueNumber & 0xFF00) >> 8), (byte) ((uniqueNumber & 0xFF) >> 0)};
writeBytes(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, userIdentifier);
break;
case 1:
// set notification off for weight measurement history
setNotificationOff(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, WEIGHT_MEASUREMENT_CONFIG);
break;
case 2:
// set notification on for weight measurement history
setNotificationOn(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, WEIGHT_MEASUREMENT_CONFIG);
break;
case 3:
// invoke receiving history data
writeBytes(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, new byte[]{0x02});
break;
default:
return false;
}
return true;
}
@Override
boolean nextCleanUpCmd(int stateNr) {
switch (stateNr) {
case 0:
// send stop command to mi scale
writeBytes(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, new byte[]{0x03});
break;
case 1:
// acknowledge that you received the last history data
int uniqueNumber = getUniqueNumber();
byte[] userIdentifier = new byte[]{(byte)0x04, (byte)0xFF, (byte)0xFF, (byte) ((uniqueNumber & 0xFF00) >> 8), (byte) ((uniqueNumber & 0xFF) >> 0)};
writeBytes(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, userIdentifier);
break;
case 2:
// set notification on for body composition measurement
setNotificationOn(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_BODY_COMPOSITION_MEASUREMENT, WEIGHT_MEASUREMENT_CONFIG);
break;
default:
return false;
}
return true;
}
private void parseBytes(byte[] weightBytes) {
try {
float weight = 0.0f;
final byte ctrlByte0 = weightBytes[0];
final byte ctrlByte1 = weightBytes[1];
final boolean isWeightRemoved = isBitSet(ctrlByte1, 7);
final boolean isDateInvalid = isBitSet(ctrlByte1, 6);
final boolean isStabilized = isBitSet(ctrlByte1, 5);
final boolean isLBSUnit = isBitSet(ctrlByte0, 0);
final boolean isCattyUnit = isBitSet(ctrlByte1, 6);
if (isStabilized && !isWeightRemoved && !isDateInvalid) {
final int year = ((weightBytes[3] & 0xFF) << 8) | (weightBytes[2] & 0xFF);
final int month = (int) weightBytes[4];
final int day = (int) weightBytes[5];
final int hours = (int) weightBytes[6];
final int min = (int) weightBytes[7];
final int sec = (int) weightBytes[8];
if (isLBSUnit || isCattyUnit) {
weight = (float) (((weightBytes[12] & 0xFF) << 8) | (weightBytes[11] & 0xFF)) / 100.0f;
} else {
weight = (float) (((weightBytes[12] & 0xFF) << 8) | (weightBytes[11] & 0xFF)) / 200.0f;
}
String date_string = year + "/" + month + "/" + day + "/" + hours + "/" + min;
Date date_time = new SimpleDateFormat("yyyy/MM/dd/HH/mm").parse(date_string);
// Is the year plausible? Check if the year is in the range of 20 years...
if (validateDate(date_time, 20)) {
ScaleData scaleBtData = new ScaleData();
scaleBtData.setWeight(weight);
scaleBtData.setDateTime(date_time);
addScaleData(scaleBtData);
} else {
Log.e("BluetoothMiScale", "Invalid Mi scale weight year " + year);
}
}
} catch (ParseException e) {
setBtStatus(BT_UNEXPECTED_ERROR, "Error while decoding bluetooth date string (" + e.getMessage() + ")");
}
}
private boolean validateDate(Date weightDate, int range) {
Calendar currentDatePos = Calendar.getInstance();
currentDatePos.add(Calendar.YEAR, range);
Calendar currentDateNeg = Calendar.getInstance();
currentDateNeg.add(Calendar.YEAR, -range);
if (weightDate.before(currentDatePos.getTime()) && weightDate.after(currentDateNeg.getTime())) {
return true;
}
return false;
}
private int getUniqueNumber() {
int uniqueNumber;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
uniqueNumber = prefs.getInt("uniqueNumber", 0x00);
if (uniqueNumber == 0x00) {
Random r = new Random();
uniqueNumber = r.nextInt(65535 - 100 + 1) + 100;
prefs.edit().putInt("uniqueNumber", uniqueNumber).commit();
}
int userId = prefs.getInt("selectedUserId", -1);
return uniqueNumber + userId;
}
}

View File

@@ -0,0 +1,168 @@
/* Copyright (C) 2017 olie.xdev <olie.xdev@googlemail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.health.openscale.core.bluetooth;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import com.health.openscale.core.OpenScale;
import com.health.openscale.core.datatypes.ScaleData;
import com.health.openscale.core.datatypes.ScaleUser;
import java.util.Date;
import java.util.Random;
import java.util.UUID;
public class BluetoothYunmaiSE extends BluetoothCommunication {
private final UUID WEIGHT_MEASUREMENT_SERVICE = UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb");
private final UUID WEIGHT_MEASUREMENT_CHARACTERISTIC = UUID.fromString("0000ffe4-0000-1000-8000-00805f9b34fb");
private final UUID WEIGHT_CMD_SERVICE = UUID.fromString("0000ffe5-0000-1000-8000-00805f9b34fb");
private final UUID WEIGHT_CMD_CHARACTERISTIC = UUID.fromString("0000ffe9-0000-1000-8000-00805f9b34fb");
private final UUID WEIGHT_MEASUREMENT_CONFIG = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
public BluetoothYunmaiSE(Context context) {
super(context);
}
@Override
public String deviceName() {
return "Yunmai SE";
}
@Override
public String defaultDeviceName() {
return "YUNMAI-ISSE-US";
}
@Override
public boolean checkDeviceName(String btDeviceName) {
if (btDeviceName.startsWith("YUNMAI-ISSE")) {
return true;
}
return false;
}
@Override
boolean nextInitCmd(int stateNr) {
switch (stateNr) {
case 0:
int user_id = getUniqueNumber();
final ScaleUser selectedUser = OpenScale.getInstance(context).getSelectedScaleUser();
byte sex = selectedUser.isMale() ? (byte)0x01 : (byte)0x02;
byte display_unit = selectedUser.scale_unit == 0 ? (byte) 0x01 : (byte) 0x02;
byte[] user_add_or_query = new byte[]{(byte)0x0d, (byte)0x12, (byte)0x10, (byte)0x01, (byte)0x00, (byte) 0x00, (byte) ((user_id & 0xFF00) >> 8), (byte) ((user_id & 0xFF) >> 0), (byte)selectedUser.body_height, (byte)sex, (byte) selectedUser.getAge(), (byte) 0x55, (byte) 0x5a, (byte) 0x00, (byte)0x00, (byte) display_unit, (byte) 0x03, (byte) 0x00 };
user_add_or_query[17] = xor_checksum(user_add_or_query);
writeBytes(WEIGHT_CMD_SERVICE, WEIGHT_CMD_CHARACTERISTIC, user_add_or_query);
break;
case 1:
long unix_time = new Date().getTime() / 1000;
byte[] set_time = new byte[]{(byte)0x0d, (byte) 0x0d, (byte) 0x11, (byte)((unix_time & 0xFF000000) >> 32), (byte)((unix_time & 0xFF0000) >> 16), (byte)((unix_time & 0xFF00) >> 8), (byte)((unix_time & 0xFF) >> 0), (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00};
set_time[12] = xor_checksum(set_time);
writeBytes(WEIGHT_CMD_SERVICE, WEIGHT_CMD_CHARACTERISTIC, set_time);
break;
case 2:
setNotificationOn(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_CHARACTERISTIC, WEIGHT_MEASUREMENT_CONFIG);
break;
case 3:
byte[] magic_bytes = new byte[]{(byte)0x0d, (byte)0x05, (byte)0x13, (byte)0x00, (byte)0x16};
writeBytes(WEIGHT_CMD_SERVICE, WEIGHT_CMD_CHARACTERISTIC, magic_bytes);
break;
default:
return false;
}
return true;
}
@Override
boolean nextBluetoothCmd(int stateNr) {
return false;
}
@Override
boolean nextCleanUpCmd(int stateNr) {
return false;
}
@Override
public void onBluetoothDataChange(BluetoothGatt bluetoothGatt, BluetoothGattCharacteristic gattCharacteristic) {
final byte[] data = gattCharacteristic.getValue();
if (data != null && data.length > 0) {
// if finished weighting?
if (data[3] == 0x02) {
parseBytes(data);
}
}
}
private void parseBytes(byte[] weightBytes) {
long unix_timestamp = ((weightBytes[5] & 0xFF) << 24) | ((weightBytes[6] & 0xFF) << 16) | ((weightBytes[7] & 0xFF) << 8) | (weightBytes[8] & 0xFF);
Date btDate = new Date();
btDate.setTime(unix_timestamp*1000);
float weight = (float) (((weightBytes[13] & 0xFF) << 8) | (weightBytes[14] & 0xFF)) / 100.0f;
ScaleData scaleBtData = new ScaleData();
final ScaleUser selectedUser = OpenScale.getInstance(context).getSelectedScaleUser();
scaleBtData.setConvertedWeight(weight, selectedUser.scale_unit);
scaleBtData.setDateTime(btDate);
addScaleData(scaleBtData);
}
private int getUniqueNumber() {
int uniqueNumber;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
uniqueNumber = prefs.getInt("uniqueNumber", 0x00);
if (uniqueNumber == 0x00) {
Random r = new Random();
uniqueNumber = r.nextInt(65535 - 100 + 1) + 100;
prefs.edit().putInt("uniqueNumber", uniqueNumber).commit();
}
int userId = prefs.getInt("selectedUserId", -1);
return uniqueNumber + userId;
}
private byte xor_checksum(byte[] data) {
byte checksum = 0x00;
for (int i=0; i<data.length-1; i++) {
checksum ^= data[i];
}
return checksum;
}
}

View File

@@ -0,0 +1,35 @@
/* Copyright (C) 2017 olie.xdev <olie.xdev@googlemail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.health.openscale.core.bodymetric;
import com.health.openscale.core.datatypes.ScaleData;
import com.health.openscale.core.datatypes.ScaleUser;
public class BFBJoN extends EstimatedFatMetric {
@Override
public String getName() {
return "British Journal of Nutrition (1991)";
}
@Override
public float getFat(ScaleUser user, ScaleData data) {
if (user.isMale()) {
return (data.getBMI(user.body_height) * 1.2f) + (user.getAge() * 0.23f) - 16.2f;
}
return (data.getBMI(user.body_height) * 1.2f) + (user.getAge() * 0.23f) - 5.4f;
}
}

View File

@@ -0,0 +1,35 @@
/* Copyright (C) 2017 olie.xdev <olie.xdev@googlemail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.health.openscale.core.bodymetric;
import com.health.openscale.core.datatypes.ScaleData;
import com.health.openscale.core.datatypes.ScaleUser;
public class BFDeurenberg extends EstimatedFatMetric {
@Override
public String getName() {
return "Deurenberg et. al (1998)";
}
@Override
public float getFat(ScaleUser user, ScaleData data) {
if (user.getAge() >= 16) {
return (1.2f * data.getBMI(user.body_height)) + (0.23f*user.getAge()) - (10.8f*(1-user.gender)) - 5.4f;
}
return (1.294f * data.getBMI(user.body_height)) + (0.20f*user.getAge()) - (11.4f*(1-user.gender)) - 8.0f;
}
}

View File

@@ -0,0 +1,35 @@
/* Copyright (C) 2017 olie.xdev <olie.xdev@googlemail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.health.openscale.core.bodymetric;
import com.health.openscale.core.datatypes.ScaleData;
import com.health.openscale.core.datatypes.ScaleUser;
public class BFEddy extends EstimatedFatMetric {
@Override
public String getName() {
return "Eddy et. al (1976)";
}
@Override
public float getFat(ScaleUser user, ScaleData data) {
if (user.isMale()) {
return (1.281f* data.getBMI(user.body_height)) - 10.13f;
}
return (1.48f* data.getBMI(user.body_height)) - 7.0f;
}
}

View File

@@ -0,0 +1,37 @@
/* Copyright (C) 2017 olie.xdev <olie.xdev@googlemail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.health.openscale.core.bodymetric;
import com.health.openscale.core.datatypes.ScaleData;
import com.health.openscale.core.datatypes.ScaleUser;
public class BFGallagher extends EstimatedFatMetric {
@Override
public String getName() {
return "Gallagher et. al [non-asian] (2000)";
}
@Override
public float getFat(ScaleUser user, ScaleData data) {
if (user.isMale()) {
// non-asian male
return 64.5f - 848.0f * (1.0f / data.getBMI(user.body_height)) + 0.079f * user.getAge() - 16.4f + 0.05f * user.getAge() + 39.0f * (1.0f / data.getBMI(user.body_height));
}
// non-asian female
return 64.5f - 848.0f * (1.0f / data.getBMI(user.body_height)) + 0.079f * user.getAge();
}
}

View File

@@ -0,0 +1,37 @@
/* Copyright (C) 2017 olie.xdev <olie.xdev@googlemail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.health.openscale.core.bodymetric;
import com.health.openscale.core.datatypes.ScaleData;
import com.health.openscale.core.datatypes.ScaleUser;
public class BFGallagherAsian extends EstimatedFatMetric {
@Override
public String getName() {
return "Gallagher et. al [asian] (2000)";
}
@Override
public float getFat(ScaleUser user, ScaleData data) {
if (user.isMale()) {
// asian male
return 51.9f - 740.0f * (1.0f / data.getBMI(user.body_height)) + 0.029f * user.getAge();
}
// asian female
return 64.8f - 752.0f * (1.0f / data.getBMI(user.body_height)) + 0.016f * user.getAge();
}
}

View File

@@ -0,0 +1,43 @@
/* Copyright (C) 2017 olie.xdev <olie.xdev@googlemail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.health.openscale.core.bodymetric;
import com.health.openscale.core.datatypes.ScaleData;
import com.health.openscale.core.datatypes.ScaleUser;
public abstract class EstimatedFatMetric {
public enum FORMULA_FAT { BF_DEURENBERG, BF_BJoN, BF_EDDY, BF_GALLAGHER, BF_GALLAGHER_ASIAN };
public static EstimatedFatMetric getEstimatedFatMetric( FORMULA_FAT fatMetric) {
switch (fatMetric) {
case BF_DEURENBERG:
return new BFDeurenberg();
case BF_BJoN:
return new BFBJoN();
case BF_EDDY:
return new BFEddy();
case BF_GALLAGHER:
return new BFGallagher();
case BF_GALLAGHER_ASIAN:
return new BFGallagherAsian();
}
return null;
}
public abstract String getName();
public abstract float getFat(ScaleUser user, ScaleData data);
}

View File

@@ -0,0 +1,41 @@
/* Copyright (C) 2017 olie.xdev <olie.xdev@googlemail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.health.openscale.core.bodymetric;
import com.health.openscale.core.datatypes.ScaleData;
import com.health.openscale.core.datatypes.ScaleUser;
public abstract class EstimatedWaterMetric {
public enum FORMULA_WATER { TBW_BEHNKE, TBW_DELWAIDECRENIER, TBW_HUMEWEYERS, TBW_LEESONGKIM };
public static EstimatedWaterMetric getEstimatedWaterMetric( FORMULA_WATER waterMetric) {
switch (waterMetric) {
case TBW_BEHNKE:
return new TBWBehnke();
case TBW_DELWAIDECRENIER:
return new TBWDelwaideCrenier();
case TBW_HUMEWEYERS:
return new TBWHumeWeyers();
case TBW_LEESONGKIM:
return new TBWLeeSongKim();
}
return null;
}
public abstract String getName();
public abstract float getWater(ScaleUser user, ScaleData data);
}

View File

@@ -0,0 +1,35 @@
/* Copyright (C) 2017 olie.xdev <olie.xdev@googlemail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.health.openscale.core.bodymetric;
import com.health.openscale.core.datatypes.ScaleData;
import com.health.openscale.core.datatypes.ScaleUser;
public class TBWBehnke extends EstimatedWaterMetric {
@Override
public String getName() {
return "Behnke et. al (1963)";
}
@Override
public float getWater(ScaleUser user, ScaleData data) {
if (user.isMale()) {
return 0.72f * (0.204f * user.body_height * user.body_height) / 100.0f;
}
return 0.72f * (0.18f * user.body_height * user.body_height) / 100.0f;
}
}

View File

@@ -0,0 +1,31 @@
/* Copyright (C) 2017 olie.xdev <olie.xdev@googlemail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.health.openscale.core.bodymetric;
import com.health.openscale.core.datatypes.ScaleData;
import com.health.openscale.core.datatypes.ScaleUser;
public class TBWDelwaideCrenier extends EstimatedWaterMetric {
@Override
public String getName() {
return "Delwaide-Crenier et. al (1973)";
}
@Override
public float getWater(ScaleUser user, ScaleData data) {
return 0.72f * (-1.976f + 0.907f * data.getWeight());
}
}

View File

@@ -0,0 +1,35 @@
/* Copyright (C) 2017 olie.xdev <olie.xdev@googlemail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.health.openscale.core.bodymetric;
import com.health.openscale.core.datatypes.ScaleData;
import com.health.openscale.core.datatypes.ScaleUser;
public class TBWHumeWeyers extends EstimatedWaterMetric {
@Override
public String getName() {
return "Hume & Weyers (1971)";
}
@Override
public float getWater(ScaleUser user, ScaleData data) {
if (user.isMale()) {
return (0.194786f * user.body_height) + (0.296785f * data.getWeight()) - 14.012934f;
}
return (0.34454f * user.body_height) + (0.183809f * data.getWeight()) - 35.270121f;
}
}

View File

@@ -0,0 +1,35 @@
/* Copyright (C) 2017 olie.xdev <olie.xdev@googlemail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.health.openscale.core.bodymetric;
import com.health.openscale.core.datatypes.ScaleData;
import com.health.openscale.core.datatypes.ScaleUser;
public class TBWLeeSongKim extends EstimatedWaterMetric {
@Override
public String getName() {
return "Lee, Song, Kim, Lee et. al (1999)";
}
@Override
public float getWater(ScaleUser user, ScaleData data) {
if (user.isMale()) {
return -28.3497f + (0.243057f * user.body_height) + (0.366248f * data.getWeight());
}
return -26.6224f + (0.262513f * user.body_height) + (0.232948f * data.getWeight());
}
}

View File

@@ -113,7 +113,7 @@ public class ScaleUserDatabase extends SQLiteOpenHelper {
values.put(COLUMN_NAME_BODY_HEIGHT, scaleUser.body_height);
values.put(COLUMN_NAME_SCALE_UNIT, scaleUser.scale_unit);
values.put(COLUMN_NAME_GENDER, scaleUser.gender);
values.put(COLUMN_NAME_INITIAL_WEIGHT, scaleUser.initial_weight);
values.put(COLUMN_NAME_INITIAL_WEIGHT, scaleUser.getInitialWeight());
values.put(COLUMN_NAME_GOAL_WEIGHT, scaleUser.goal_weight);
values.put(COLUMN_NAME_GOAL_DATE, formatDateTime.format(scaleUser.goal_date));
@@ -146,7 +146,7 @@ public class ScaleUserDatabase extends SQLiteOpenHelper {
values.put(COLUMN_NAME_BODY_HEIGHT, scaleUser.body_height);
values.put(COLUMN_NAME_SCALE_UNIT, scaleUser.scale_unit);
values.put(COLUMN_NAME_GENDER, scaleUser.gender);
values.put(COLUMN_NAME_INITIAL_WEIGHT, scaleUser.initial_weight);
values.put(COLUMN_NAME_INITIAL_WEIGHT, scaleUser.getInitialWeight());
values.put(COLUMN_NAME_GOAL_WEIGHT, scaleUser.goal_weight);
values.put(COLUMN_NAME_GOAL_DATE, formatDateTime.format(scaleUser.goal_date));
@@ -222,7 +222,7 @@ public class ScaleUserDatabase extends SQLiteOpenHelper {
scaleUser.birthday = formatDateTime.parse(birthday);
scaleUser.goal_date = formatDateTime.parse(goal_date);
scaleUser.initial_weight = Math.round(initial_weight * 100.0f) / 100.0f;
scaleUser.setInitialWeight(Math.round(initial_weight * 100.0f) / 100.0f);
scaleUser.goal_weight = Math.round(goal_weight * 100.0f) / 100.0f;
} catch (ParseException ex) {
Log.e("ScaleDatabase", "Can't parse the date time string: " + ex.getMessage());

View File

@@ -21,6 +21,8 @@ import java.util.Date;
public class ScaleUser {
public static final String[] UNIT_STRING = new String[] {"kg", "lb", "st"};
private static float KG_LB = 2.20462f;
private static float KG_ST = 0.157473f;
public int id;
public String user_name;
@@ -28,7 +30,7 @@ public class ScaleUser {
public int body_height;
public int scale_unit;
public int gender;
public float initial_weight;
private float initial_weight;
public float goal_weight;
public Date goal_date;
@@ -62,6 +64,47 @@ public class ScaleUser {
return userAge;
}
public void setInitialWeight(float weight) {
this.initial_weight = weight;
}
public void setConvertedInitialWeight(float weight) {
switch (ScaleUser.UNIT_STRING[scale_unit]) {
case "kg":
this.initial_weight = weight;
break;
case "lb":
this.initial_weight = weight / KG_LB;
break;
case "st":
this.initial_weight = weight / KG_ST;
break;
}
}
public float getInitialWeight() {
return initial_weight;
}
public float getConvertedInitialWeight() {
float converted_weight = 0.0f;
switch (ScaleUser.UNIT_STRING[scale_unit]) {
case "kg":
converted_weight = initial_weight;
break;
case "lb":
converted_weight = initial_weight * KG_LB;
break;
case "st":
converted_weight = initial_weight * KG_ST;
break;
}
return converted_weight;
}
@Override
public String toString()
{

View File

@@ -133,7 +133,7 @@ public class UserSettingsActivity extends Activity {
txtBodyHeight.setText(Integer.toString(scaleUser.body_height));
txtBirthday.setText(dateFormat.format(scaleUser.birthday));
txtGoalDate.setText(dateFormat.format(scaleUser.goal_date));
txtInitialWeight.setText(scaleUser.initial_weight+"");
txtInitialWeight.setText(Math.round(scaleUser.getConvertedInitialWeight()*100.0f)/100.0f + "");
txtGoalWeight.setText(scaleUser.goal_weight+"");
switch (scaleUser.scale_unit)

View File

@@ -19,18 +19,37 @@ import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.MultiSelectListPreference;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceGroup;
import android.preference.PreferenceManager;
import android.widget.Toast;
import com.health.openscale.R;
import com.health.openscale.core.OpenScale;
import com.health.openscale.core.bodymetric.EstimatedFatMetric;
import com.health.openscale.core.bodymetric.EstimatedWaterMetric;
public class MeasurementPreferences extends PreferenceFragment {
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class MeasurementPreferences extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener {
public static final String PREFERENCE_KEY_DELETE_ALL = "deleteAll";
public static final String PREFERENCE_KEY_ESTIMATE_FAT = "estimateFatEnable";
public static final String PREFERENCE_KEY_ESTIMATE_FAT_FORMULA = "estimateFatFormula";
public static final String PREFERENCE_KEY_ESTIMATE_WATER = "estimateWaterEnable";
public static final String PREFERENCE_KEY_ESTIMATE_WATER_FORMULA = "estimateWaterFormula";
private Preference deleteAll;
private CheckBoxPreference estimateFatEnable;
private ListPreference estimateFatFormula;
private CheckBoxPreference estimateWaterEnable;
private ListPreference estimateWaterFormula;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -40,6 +59,109 @@ public class MeasurementPreferences extends PreferenceFragment {
deleteAll = (Preference) findPreference(PREFERENCE_KEY_DELETE_ALL);
deleteAll.setOnPreferenceClickListener(new onClickListenerDeleteAll());
estimateFatEnable = (CheckBoxPreference) findPreference(PREFERENCE_KEY_ESTIMATE_FAT);
estimateFatFormula = (ListPreference) findPreference(PREFERENCE_KEY_ESTIMATE_FAT_FORMULA);
estimateWaterEnable = (CheckBoxPreference) findPreference(PREFERENCE_KEY_ESTIMATE_WATER);
estimateWaterFormula = (ListPreference) findPreference(PREFERENCE_KEY_ESTIMATE_WATER_FORMULA);
updateFatListPreferences();
updateWaterListPreferences();
initSummary(getPreferenceScreen());
}
public void updateFatListPreferences() {
ArrayList<String> listEntries = new ArrayList();
ArrayList<String> listEntryValues = new ArrayList();
for (EstimatedFatMetric.FORMULA_FAT formulaFat : EstimatedFatMetric.FORMULA_FAT.values()) {
EstimatedFatMetric fatMetric = EstimatedFatMetric.getEstimatedFatMetric(formulaFat);
listEntries.add(fatMetric.getName());
listEntryValues.add(formulaFat.toString());
}
estimateFatFormula.setEntries(listEntries.toArray(new CharSequence[listEntries.size()]));
estimateFatFormula.setEntryValues(listEntryValues.toArray(new CharSequence[listEntryValues.size()]));
}
public void updateWaterListPreferences() {
ArrayList<String> listEntries = new ArrayList();
ArrayList<String> listEntryValues = new ArrayList();
for (EstimatedWaterMetric.FORMULA_WATER formulaWater : EstimatedWaterMetric.FORMULA_WATER.values()) {
EstimatedWaterMetric waterMetric = EstimatedWaterMetric.getEstimatedWaterMetric(formulaWater);
listEntries.add(waterMetric.getName());
listEntryValues.add(formulaWater.toString());
}
estimateWaterFormula.setEntries(listEntries.toArray(new CharSequence[listEntries.size()]));
estimateWaterFormula.setEntryValues(listEntryValues.toArray(new CharSequence[listEntryValues.size()]));
}
private void initSummary(Preference p) {
if (p instanceof PreferenceGroup) {
PreferenceGroup pGrp = (PreferenceGroup) p;
for (int i = 0; i < pGrp.getPreferenceCount(); i++) {
initSummary(pGrp.getPreference(i));
}
} else {
updatePrefSummary(p);
}
}
@Override
public void onResume() {
super.onResume();
getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
updatePrefSummary(findPreference(key));
}
private void updatePrefSummary(Preference p) {
if (estimateFatEnable.isChecked()) {
estimateFatFormula.setEnabled(true);
} else {
estimateFatFormula.setEnabled(false);
}
if (estimateWaterEnable.isChecked()) {
estimateWaterFormula.setEnabled(true);
} else {
estimateWaterFormula.setEnabled(false);
}
estimateFatFormula.setSummary(EstimatedFatMetric.getEstimatedFatMetric(EstimatedFatMetric.FORMULA_FAT.valueOf(estimateFatFormula.getValue())).getName());
estimateWaterFormula.setSummary(EstimatedWaterMetric.getEstimatedWaterMetric(EstimatedWaterMetric.FORMULA_WATER.valueOf(estimateWaterFormula.getValue())).getName());
if (p instanceof EditTextPreference) {
EditTextPreference editTextPref = (EditTextPreference) p;
if (p.getTitle().toString().contains("assword"))
{
p.setSummary("******");
} else {
p.setSummary(editTextPref.getText());
}
}
if (p instanceof MultiSelectListPreference) {
MultiSelectListPreference editMultiListPref = (MultiSelectListPreference) p;
CharSequence[] entries = editMultiListPref.getEntries();
CharSequence[] entryValues = editMultiListPref.getEntryValues();
List<String> currentEntries = new ArrayList<>();
Set<String> currentEntryValues = editMultiListPref.getValues();
for (int i = 0; i < entries.length; i++)
if (currentEntryValues.contains(entryValues[i]))
currentEntries.add(entries[i].toString());
p.setSummary(currentEntries.toString());
}
}
private class onClickListenerDeleteAll implements Preference.OnPreferenceClickListener {

View File

@@ -26,13 +26,27 @@ import com.health.openscale.core.evaluation.EvaluationSheet;
public class FatMeasurementView extends MeasurementView {
private boolean estimateFatEnable;
public FatMeasurementView(Context context) {
super(context, context.getResources().getString(R.string.label_fat), ContextCompat.getDrawable(context, R.drawable.ic_fat));
}
@Override
public boolean isEditable() {
if (estimateFatEnable && getMeasurementMode() == MeasurementViewMode.ADD) {
return false;
}
return true;
}
@Override
public void updateValue(ScaleData updateData) {
setValueOnView(updateData.getFat());
if (estimateFatEnable && getMeasurementMode() == MeasurementViewMode.ADD) {
setValueOnView((getContext().getString(R.string.label_automatic)));
} else {
setValueOnView(updateData.getFat());
}
}
@Override
@@ -48,6 +62,7 @@ public class FatMeasurementView extends MeasurementView {
@Override
public void updatePreferences(SharedPreferences preferences) {
setVisible(preferences.getBoolean("fatEnable", true));
estimateFatEnable = preferences.getBoolean("estimateFatEnable", false);
}
@Override

View File

@@ -21,16 +21,19 @@ import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.support.v4.content.ContextCompat;
import android.text.Html;
import android.text.InputType;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Space;
import android.widget.TableLayout;
import android.widget.TableRow;
@@ -59,6 +62,9 @@ public abstract class MeasurementView extends TableLayout {
private ImageView iconView;
private TextView nameView;
private TextView valueView;
private LinearLayout incdecLayout;
private Button incView;
private Button decView;
private ImageView editModeView;
private ImageView indicatorView;
@@ -90,17 +96,22 @@ public abstract class MeasurementView extends TableLayout {
iconView = new ImageView(context);
nameView = new TextView(context);
valueView = new TextView(context);
incView = new Button(context);
decView = new Button(context);
editModeView = new ImageView(context);
indicatorView = new ImageView(context);
evaluatorRow = new TableRow(context);
evaluatorView = new LinearGaugeView(context);
incdecLayout = new LinearLayout(context);
measurementRow.setLayoutParams(new TableRow.LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT, 1.0f));
measurementRow.setGravity(Gravity.CENTER);
measurementRow.addView(iconView);
measurementRow.addView(nameView);
measurementRow.addView(valueView);
measurementRow.addView(incdecLayout);
measurementRow.addView(editModeView);
measurementRow.addView(indicatorView);
@@ -113,7 +124,7 @@ public abstract class MeasurementView extends TableLayout {
nameView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 15);
nameView.setTextColor(Color.BLACK);
nameView.setLines(2);
nameView.setLayoutParams(new TableRow.LayoutParams(0, LayoutParams.WRAP_CONTENT, 0.60f));
nameView.setLayoutParams(new TableRow.LayoutParams(0, LayoutParams.WRAP_CONTENT, 0.55f));
valueView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 15);
valueView.setTextColor(Color.BLACK);
@@ -121,6 +132,50 @@ public abstract class MeasurementView extends TableLayout {
valueView.setPadding(0,0,20,0);
valueView.setLayoutParams(new TableRow.LayoutParams(0, LayoutParams.MATCH_PARENT, 0.29f));
incdecLayout.setOrientation(VERTICAL);
incdecLayout.addView(incView);
incdecLayout.addView(decView);
incdecLayout.setVisibility(View.GONE);
incdecLayout.setPadding(0,0,0,0);
incdecLayout.setLayoutParams(new TableRow.LayoutParams(0, LayoutParams.MATCH_PARENT, 0.05f));
incView.setText("+");
incView.setBackgroundColor(Color.TRANSPARENT);
incView.setPadding(0,0,0,0);
incView.setLayoutParams(new TableRow.LayoutParams(LayoutParams.MATCH_PARENT, 0, 0.50f));
incView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
incValue();
}
});
incView.setOnTouchListener(new RepeatListener(400, 100, new OnClickListener() {
@Override
public void onClick(View view) {
incValue();
}
}));
incView.setVisibility(View.GONE);
decView.setText("-");
decView.setBackgroundColor(Color.TRANSPARENT);
decView.setPadding(0,0,0,0);
decView.setLayoutParams(new TableRow.LayoutParams(LayoutParams.MATCH_PARENT, 0, 0.50f));
decView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
decValue();
}
});
decView.setOnTouchListener(new RepeatListener(400, 100, new OnClickListener() {
@Override
public void onClick(View view) {
decValue();
}
}));
decView.setVisibility(View.GONE);
editModeView.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_editable));
editModeView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
editModeView.setVisibility(View.GONE);
@@ -153,8 +208,27 @@ public abstract class MeasurementView extends TableLayout {
if (value.length() == 0) {
return -1;
}
try {
return Float.valueOf(value);
} catch (NumberFormatException e) {
return -1;
}
}
return Float.valueOf(value);
public void incValue() {
float incValue = getValue() + 0.1f;
if (incValue <= getMaxValue()) {
setValueOnView(incValue);
}
}
public void decValue() {
float decValue = getValue() - 0.1f;
if (decValue >= 0) {
setValueOnView(decValue);
}
}
public String getValueAsString() {
@@ -176,13 +250,26 @@ public abstract class MeasurementView extends TableLayout {
case VIEW:
indicatorView.setVisibility(View.VISIBLE);
editModeView.setVisibility(View.GONE);
incdecLayout.setVisibility(View.GONE);
incView.setVisibility(View.GONE);
decView.setVisibility(View.GONE);
break;
case EDIT:
case ADD:
editModeView.setVisibility(View.VISIBLE);
incView.setVisibility(View.VISIBLE);
decView.setVisibility(View.VISIBLE);
incdecLayout.setVisibility(View.VISIBLE);
if (!isEditable()) {
editModeView.setImageDrawable(ContextCompat.getDrawable(getContext(), R.drawable.ic_noteeditable));
incView.setVisibility(View.GONE);
decView.setVisibility(View.GONE);
}
if (getUnit() == null) {
incView.setVisibility(View.GONE);
decView.setVisibility(View.GONE);
}
indicatorView.setVisibility(View.GONE);
@@ -334,7 +421,7 @@ public abstract class MeasurementView extends TableLayout {
input.setInputType(getInputType());
input.setHint(getHintText());
input.setText(value);
input.setSelection(value.length());
input.setSelectAllOnFocus(true);
builder.setView(input);
builder.setPositiveButton(getResources().getString(R.string.label_ok), null);
@@ -396,5 +483,65 @@ public abstract class MeasurementView extends TableLayout {
}
}
}
private class RepeatListener implements OnTouchListener {
private Handler handler = new Handler();
private int initialInterval;
private final int normalInterval;
private final OnClickListener clickListener;
private Runnable handlerRunnable = new Runnable() {
@Override
public void run() {
handler.postDelayed(this, normalInterval);
clickListener.onClick(downView);
}
};
private View downView;
/**
* RepeatListener cyclically runs a clickListener, emulating keyboard-like behaviour. First
* click is fired immediately, next one after the initialInterval, and subsequent ones after the normalInterval.
*
* @param initialInterval The interval after first click event
* @param normalInterval The interval after second and subsequent click events
* @param clickListener The OnClickListener, that will be called periodically
*/
public RepeatListener(int initialInterval, int normalInterval,
OnClickListener clickListener) {
if (clickListener == null)
throw new IllegalArgumentException("null runnable");
if (initialInterval < 0 || normalInterval < 0)
throw new IllegalArgumentException("negative interval");
this.initialInterval = initialInterval;
this.normalInterval = normalInterval;
this.clickListener = clickListener;
}
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
handler.removeCallbacks(handlerRunnable);
handler.postDelayed(handlerRunnable, initialInterval);
downView = view;
downView.setPressed(true);
clickListener.onClick(view);
return true;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
handler.removeCallbacks(handlerRunnable);
downView.setPressed(false);
downView = null;
return true;
}
return false;
}
}
}

View File

@@ -70,7 +70,7 @@ public class TimeMeasurementView extends MeasurementView {
@Override
public String getUnit() {
return "";
return null;
}
@Override

View File

@@ -26,13 +26,27 @@ import com.health.openscale.core.evaluation.EvaluationSheet;
public class WaterMeasurementView extends MeasurementView {
private boolean estimateWaterEnable;
public WaterMeasurementView(Context context) {
super(context, context.getResources().getString(R.string.label_water), ContextCompat.getDrawable(context, R.drawable.ic_water));
}
@Override
public boolean isEditable() {
if (estimateWaterEnable && getMeasurementMode() == MeasurementViewMode.ADD) {
return false;
}
return true;
}
@Override
public void updateValue(ScaleData updateData) {
setValueOnView(updateData.getWater());
if (estimateWaterEnable && getMeasurementMode() == MeasurementViewMode.ADD) {
setValueOnView((getContext().getString(R.string.label_automatic)));
} else {
setValueOnView(updateData.getWater());
}
}
@Override
@@ -48,6 +62,7 @@ public class WaterMeasurementView extends MeasurementView {
@Override
public void updatePreferences(SharedPreferences preferences) {
setVisible(preferences.getBoolean("waterEnable", true));
estimateWaterEnable = preferences.getBoolean("estimateWaterEnable", false);
}
@Override

View File

@@ -34,10 +34,6 @@ public class WeightMeasurementView extends MeasurementView {
@Override
public void updateValue(ScaleData updateData) {
setValueOnView(updateData.getConvertedWeight(getScaleUser().scale_unit));
if (getMeasurementMode() == MeasurementViewMode.ADD) {
getInputDialog().show();
}
}
@Override

View File

@@ -135,4 +135,15 @@
<string name="label_bmr">Grundumsatz (BMR)</string>
<string name="info_measuring">Messe Gewicht: %.2f</string>
<string name="info_new_data_added">%1$.2f%2$s [%3$s] zu %4$s hinzugefügt</string>
<string name="error_max_scale_users">Maximale Anzahl gleichzeitiger Benutzer erreicht.</string>
<string name="info_step_on_scale">Bitte steigen Sie barfuß auf die Waage zur Referenzmessung</string>
<string name="label_automatic">auto</string>
<string name="label_category_body_metrics_estimation">Körpermetriken abschätzen</string>
<string name="label_category_display">Anzeige</string>
<string name="label_category_measurement_database">Messwertedatenbank</string>
<string name="label_category_misc">Verschiedenes</string>
<string name="label_estimate_fat">Körperfettschätzung</string>
<string name="label_estimate_fat_formula">Körperfettformel</string>
<string name="label_estimate_water">Körperwasserschätzung</string>
<string name="label_estimate_water_formula">Körperwasserformel</string>
</resources>

View File

@@ -104,7 +104,7 @@
<string name="label_enable_points">Bod na údajový bod</string>
<string name="label_delete_confirmation">Potvrďte vymazanie</string>
<string name="label_reminder">Pripomienka</string>
<string name="label_reminder_weekdays">Pracovné dni</string>
<string name="label_reminder_weekdays">Dni v týždni</string>
<string name="label_reminder_time">Čas</string>
<string name="label_reminder_notify_text">Text upozornenia</string>
<string name="default_value_reminder_notify_text">Čas na váženie</string>
@@ -114,7 +114,7 @@
<string name="info_your_muscle">Vaše percento svalov v tele bolo</string>
<string name="info_your_waist">Váš obvod pása bol</string>
<string name="info_your_hip">Váš obvod bokov bol</string>
<string name="info_on_date">do</string>
<string name="info_on_date">v deň</string>
<string name="Monday">Pondelok</string>
<string name="Tuesday">Utorok</string>
<string name="Wednesday">Streda</string>

View File

@@ -120,6 +120,18 @@
<string name="label_delete_confirmation">Delete confirmation</string>
<string name="label_estimate_fat">Estimate body fat</string>
<string name="label_estimate_water">Estimate body water</string>
<string name="label_category_display">Display</string>
<string name="label_category_body_metrics_estimation">Body metrics estimation</string>
<string name="label_category_measurement_database">Measurement database</string>
<string name="label_category_misc">Miscellaneous</string>
<string name="label_estimate_fat_formula">Body fat formula</string>
<string name="label_estimate_water_formula">Body water formula</string>
<string name="label_automatic">auto</string>
<string name="label_reminder">Reminder</string>
<string name="label_reminder_weekdays">Weekdays</string>
<string name="label_reminder_time">Time</string>

View File

@@ -1,10 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<CheckBoxPreference android:title="@string/label_enable_labels" android:summaryOn="@string/info_is_visible" android:summaryOff="@string/info_is_not_visible" android:key="labelsEnable" android:defaultValue="true"/>
<CheckBoxPreference android:title="@string/label_enable_points" android:summaryOn="@string/info_is_visible" android:summaryOff="@string/info_is_not_visible" android:key="pointsEnable" android:defaultValue="true"/>
<CheckBoxPreference android:title="@string/label_delete_confirmation" android:summaryOn="@string/info_is_enable" android:summaryOff="@string/info_is_not_enable" android:key="deleteConfirmationEnable" android:defaultValue="true" />
<CheckBoxPreference android:title="@string/label_average_data" android:summaryOn="@string/info_is_enable" android:summaryOff="@string/info_is_not_enable" android:key="averageData" android:defaultValue="true" />
<CheckBoxPreference android:title="@string/label_goal_line" android:summaryOn="@string/info_is_visible" android:summaryOff="@string/info_is_not_visible" android:key="goalLine" android:defaultValue="true" />
<CheckBoxPreference android:title="@string/label_regression_line" android:summaryOn="@string/info_is_visible" android:summaryOff="@string/info_is_not_visible" android:key="regressionLine" android:defaultValue="false" />
<EditTextPreference android:title="@string/label_regression_line_degree" android:key="regressionLineOrder" android:defaultValue="1" />
<PreferenceCategory android:title="@string/label_category_display">
<CheckBoxPreference android:title="@string/label_enable_labels" android:summaryOn="@string/info_is_visible" android:summaryOff="@string/info_is_not_visible" android:key="labelsEnable" android:defaultValue="true"/>
<CheckBoxPreference android:title="@string/label_enable_points" android:summaryOn="@string/info_is_visible" android:summaryOff="@string/info_is_not_visible" android:key="pointsEnable" android:defaultValue="true"/>
<CheckBoxPreference android:title="@string/label_goal_line" android:summaryOn="@string/info_is_visible" android:summaryOff="@string/info_is_not_visible" android:key="goalLine" android:defaultValue="true" />
<CheckBoxPreference android:title="@string/label_regression_line" android:summaryOn="@string/info_is_visible" android:summaryOff="@string/info_is_not_visible" android:key="regressionLine" android:defaultValue="false" />
<EditTextPreference android:title="@string/label_regression_line_degree" android:key="regressionLineOrder" android:defaultValue="1" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/label_category_misc">
<CheckBoxPreference android:title="@string/label_delete_confirmation" android:summaryOn="@string/info_is_enable" android:summaryOff="@string/info_is_not_enable" android:key="deleteConfirmationEnable" android:defaultValue="true" />
<CheckBoxPreference android:title="@string/label_average_data" android:summaryOn="@string/info_is_enable" android:summaryOff="@string/info_is_not_enable" android:key="averageData" android:defaultValue="true" />
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -1,10 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<CheckBoxPreference android:title="@string/label_fat" android:summaryOn="@string/info_is_enable" android:summaryOff="@string/info_is_not_enable" android:key="fatEnable" android:defaultValue="true"/>
<CheckBoxPreference android:title="@string/label_water" android:summaryOn="@string/info_is_enable" android:summaryOff="@string/info_is_not_enable" android:key="waterEnable" android:defaultValue="true"/>
<CheckBoxPreference android:title="@string/label_muscle" android:summaryOn="@string/info_is_enable" android:summaryOff="@string/info_is_not_enable" android:key="muscleEnable" android:defaultValue="true"/>
<CheckBoxPreference android:title="@string/label_bone" android:summaryOn="@string/info_is_enable" android:summaryOff="@string/info_is_not_enable" android:key="boneEnable" android:defaultValue="false"/>
<CheckBoxPreference android:title="@string/label_waist" android:summaryOn="@string/info_is_enable" android:summaryOff="@string/info_is_not_enable" android:key="waistEnable" android:defaultValue="false"/>
<CheckBoxPreference android:title="@string/label_hip" android:summaryOn="@string/info_is_enable" android:summaryOff="@string/info_is_not_enable" android:key="hipEnable" android:defaultValue="false"/>
<Preference android:title="@string/label_delete_all" android:key="deleteAll"></Preference>
<PreferenceCategory android:title="@string/label_category_display">
<CheckBoxPreference android:title="@string/label_fat" android:summaryOn="@string/info_is_enable" android:summaryOff="@string/info_is_not_enable" android:key="fatEnable" android:defaultValue="true"/>
<CheckBoxPreference android:title="@string/label_water" android:summaryOn="@string/info_is_enable" android:summaryOff="@string/info_is_not_enable" android:key="waterEnable" android:defaultValue="true"/>
<CheckBoxPreference android:title="@string/label_muscle" android:summaryOn="@string/info_is_enable" android:summaryOff="@string/info_is_not_enable" android:key="muscleEnable" android:defaultValue="true"/>
<CheckBoxPreference android:title="@string/label_bone" android:summaryOn="@string/info_is_enable" android:summaryOff="@string/info_is_not_enable" android:key="boneEnable" android:defaultValue="false"/>
<CheckBoxPreference android:title="@string/label_waist" android:summaryOn="@string/info_is_enable" android:summaryOff="@string/info_is_not_enable" android:key="waistEnable" android:defaultValue="false"/>
<CheckBoxPreference android:title="@string/label_hip" android:summaryOn="@string/info_is_enable" android:summaryOff="@string/info_is_not_enable" android:key="hipEnable" android:defaultValue="false"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/label_category_body_metrics_estimation">
<CheckBoxPreference android:title="@string/label_estimate_fat" android:summaryOn="@string/info_is_enable" android:summaryOff="@string/info_is_not_enable" android:key="estimateFatEnable" android:defaultValue="false"/>
<ListPreference android:title="@string/label_estimate_fat_formula" android:key="estimateFatFormula" android:defaultValue="BF_GALLAGHER"/>
<CheckBoxPreference android:title="@string/label_estimate_water" android:summaryOn="@string/info_is_enable" android:summaryOff="@string/info_is_not_enable" android:key="estimateWaterEnable" android:defaultValue="false"/>
<ListPreference android:title="@string/label_estimate_water_formula" android:key="estimateWaterFormula" android:defaultValue="TBW_LEESONGKIM"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/label_category_measurement_database">
<Preference android:title="@string/label_delete_all" android:key="deleteAll"></Preference>
</PreferenceCategory>
</PreferenceScreen>