1
0
mirror of https://github.com/oliexdev/openScale.git synced 2025-08-29 11:10:35 +02:00

Merge branch 'master' into funkwit-master

This commit is contained in:
OliE
2018-07-20 16:21:42 +02:00
9 changed files with 303 additions and 26 deletions

View File

@@ -0,0 +1,106 @@
/* Copyright (C) 2018 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.gui;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.support.test.InstrumentationRegistry;
import android.support.test.espresso.ViewInteraction;
import android.support.test.espresso.contrib.PickerActions;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.DatePicker;
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.gui.activities.BaseAppCompatActivity;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import org.hamcrest.TypeSafeMatcher;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Calendar;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.closeSoftKeyboard;
import static android.support.test.espresso.action.ViewActions.replaceText;
import static android.support.test.espresso.action.ViewActions.scrollTo;
import static android.support.test.espresso.matcher.ViewMatchers.withClassName;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static org.hamcrest.Matchers.allOf;
import static org.junit.Assert.assertEquals;
@LargeTest
@RunWith(AndroidJUnit4.class)
public class AddMeasurementTest {
private static final double DELTA = 1e-15;
private static Context context;
private static OpenScale openScale;
private static final ScaleUser male = TestData.getMaleUser();
private static final ScaleUser female = TestData.getFemaleUser();
@Rule
public ActivityTestRule<MainActivity> mActivityTestRule = new ActivityTestRule<>(MainActivity.class, false, true);
@BeforeClass
public static void initTest() {
context = InstrumentationRegistry.getTargetContext();
openScale = OpenScale.getInstance();
openScale.addScaleUser(male);
openScale.addScaleUser(female);
// Set first start to true to get the user add dialog
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
prefs.edit()
.putBoolean("firstStart", false)
.putString(BaseAppCompatActivity.PREFERENCE_LANGUAGE, "en")
.putInt("selectedUserId", male.getId())
.commit();
}
@AfterClass
public static void addMeasurementVerification() {
openScale.deleteScaleUser(male.getId());
openScale.deleteScaleUser(female.getId());
}
@Test
public void addMeasurementTest() {
onView(withId(R.id.action_add_measurement)).perform(click());
ScaleMeasurement measurement = TestData.getMeasurement(1);
}
}

View File

@@ -61,7 +61,7 @@ import static org.junit.Assert.assertEquals;
@LargeTest
@RunWith(AndroidJUnit4.class)
public class UserAddTest {
public class AddUserTest {
private static final double DELTA = 1e-15;
private Context context;
@@ -82,7 +82,7 @@ public class UserAddTest {
}
@After
public void verifyUserAdd() {
public void addUserVerification() {
ScaleUser user = OpenScale.getInstance().getSelectedScaleUser();
assertEquals("test", user.getUserName());
@@ -107,10 +107,12 @@ public class UserAddTest {
goalDate.set(Calendar.HOUR_OF_DAY, 0);
assertEquals(goalDate.getTime().getTime(), user.getGoalDate().getTime());
OpenScale.getInstance().deleteScaleUser(user.getId());
}
@Test
public void userAddTest() {
public void addUserTest() {
mActivityTestRule.launchActivity(null);
ViewInteraction editText = onView(

View File

@@ -0,0 +1,108 @@
/* Copyright (C) 2018 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.gui;
import com.health.openscale.core.datatypes.ScaleMeasurement;
import com.health.openscale.core.datatypes.ScaleUser;
import com.health.openscale.core.utils.Converters;
import java.util.Calendar;
import java.util.Date;
import java.util.Random;
public class TestData {
private static Random rand = new Random();
public static ScaleUser getMaleUser() {
ScaleUser male = new ScaleUser();
male.setUserName("Bob");
male.setGender(Converters.Gender.MALE);
male.setInitialWeight(80.0f);
male.setScaleUnit(Converters.WeightUnit.KG);
male.setActivityLevel(Converters.ActivityLevel.MILD);
male.setBodyHeight(180.0f);
male.setGoalWeight(60.0f);
male.setMeasureUnit(Converters.MeasureUnit.CM);
male.setBirthday(getDateFromYears(-20));
male.setGoalDate(getDateFromYears(2));
return male;
}
public static ScaleUser getFemaleUser() {
ScaleUser female = new ScaleUser();
female.setUserName("Alice");
female.setGender(Converters.Gender.FEMALE);
female.setInitialWeight(70.0f);
female.setScaleUnit(Converters.WeightUnit.LB);
female.setActivityLevel(Converters.ActivityLevel.EXTREME);
female.setBodyHeight(160.0f);
female.setGoalWeight(50.0f);
female.setMeasureUnit(Converters.MeasureUnit.INCH);
female.setBirthday(getDateFromYears(-25));
female.setGoalDate(getDateFromYears(1));
return female;
}
public static ScaleMeasurement getMeasurement(int nr) {
ScaleMeasurement measurement = new ScaleMeasurement();
rand.setSeed(nr);
measurement.setDateTime(getDateFromDays(nr));
measurement.setWeight(100.0f + getRandNumberInRange(0,50));
measurement.setFat(30.0f + getRandNumberInRange(0,30));
measurement.setWater(50.0f + getRandNumberInRange(0,20));
measurement.setMuscle(40.0f + getRandNumberInRange(0,15));
measurement.setLbm(20.0f + getRandNumberInRange(0,10));
measurement.setBone(8.0f + getRandNumberInRange(0,50));
measurement.setWaist(50.0f + getRandNumberInRange(0,50));
measurement.setHip(60.0f + getRandNumberInRange(0,50));
measurement.setChest(80.0f + getRandNumberInRange(0,50));
measurement.setThigh(40.0f + getRandNumberInRange(0,50));
measurement.setBiceps(30.0f + getRandNumberInRange(0,50));
measurement.setNeck(15.0f + getRandNumberInRange(0,50));
measurement.setCaliper1(5.0f + getRandNumberInRange(0,10));
measurement.setCaliper2(10.0f + getRandNumberInRange(0,10));
measurement.setCaliper3(7.0f + getRandNumberInRange(0,10));
measurement.setComment("my comment " + nr);
return measurement;
}
private static Date getDateFromYears(int years) {
Calendar currentTime = Calendar.getInstance();
currentTime.add(Calendar.YEAR, years);
return currentTime.getTime();
}
private static Date getDateFromDays(int days) {
Calendar currentTime = Calendar.getInstance();
currentTime.add(Calendar.DAY_OF_YEAR, days);
return currentTime.getTime();
}
private static float getRandNumberInRange(int min, int max) {
return (float)(rand.nextInt(max*10 - min*10) + min*10) / 10.0f;
}
}

View File

@@ -101,6 +101,10 @@ public abstract class BluetoothCommunication {
return bluetoothGatt.getServices();
}
protected boolean hasBluetoothGattService(UUID service) {
return bluetoothGatt != null && bluetoothGatt.getService(service) != null;
}
/**
* Register a callback Bluetooth handler that notify any BT_STATUS_CODE changes for GUI/CORE.
*
@@ -271,6 +275,7 @@ public abstract class BluetoothCommunication {
BluetoothGattCharacteristic gattCharacteristic = bluetoothGatt.getService(service)
.getCharacteristic(characteristic);
Timber.d("Read characteristic %s", characteristic);
bluetoothGatt.readCharacteristic(gattCharacteristic);
}
@@ -278,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);
}
@@ -369,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) {
@@ -654,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;
@@ -681,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
@@ -710,8 +729,7 @@ public abstract class BluetoothCommunication {
synchronized (lock) {
onBluetoothDataRead(gatt, characteristic, status);
openRequest = false;
handleRequests();
postDelayedHandleRequests();
}
}
@@ -733,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();
}
}
}

View File

@@ -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;
}

View File

@@ -31,7 +31,9 @@ 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
@@ -53,7 +55,17 @@ public class BluetoothOneByone extends BluetoothCommunication {
switch (stateNr) {
case 0:
lastWeight = 0;
setIndicationOn(WEIGHT_MEASUREMENT_SERVICE_BODY_COMPOSITION, WEIGHT_MEASUREMENT_CHARACTERISTIC_BODY_COMPOSITION, WEIGHT_MEASUREMENT_CONFIG);
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();
@@ -94,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) {
@@ -117,4 +138,21 @@ public class BluetoothOneByone extends BluetoothCommunication {
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);
}
}
}

View File

@@ -37,6 +37,7 @@ import android.widget.LinearLayout;
import android.widget.PopupMenu;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.health.openscale.R;
import com.health.openscale.core.OpenScale;
@@ -321,9 +322,16 @@ public class GraphFragment extends Fragment implements FragmentUpdateListener {
floatingActionBar.removeAllViews();
PolynomialFitter polyFitter = new PolynomialFitter(
Math.min(Integer.parseInt(prefs.getString("regressionLineOrder", "1")),
100));
int regressLineOrder = 1;
try {
regressLineOrder = Integer.parseInt(prefs.getString("regressionLineOrder", "1"));
} catch (NumberFormatException e) {
Toast.makeText(getContext(), getString(R.string.error_value_required) + ":" + e.getMessage(), Toast.LENGTH_LONG).show();
prefs.edit().putString("regressionLineOrder", "1").commit();
}
PolynomialFitter polyFitter = new PolynomialFitter(Math.min(regressLineOrder, 100));
for (MeasurementView view : measurementViews) {
if (view instanceof FloatMeasurementView) {

View File

@@ -307,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();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB