oliexdev / openScale

Open-source weight and body metrics tracker, with support for Bluetooth scales
GNU General Public License v3.0
1.66k stars 290 forks source link

Crash when two datapoints have same date/time #1019

Closed JanKanis closed 4 months ago

JanKanis commented 7 months ago

Describe the bug I wanted to swap two datapoints, as the latter had an earlier date than the former. When I changed the date and values of the latter measurement to equal that of the former measurement the app craahed.

To Reproduce Steps to reproduce the behavior:

  1. Enter two measurements. Let the second have an earlier date than the first.
  2. Edit the second measurement, give it the same values and date as the first measurement. Save.
  3. App crashes with sql constraint exception.

Reproduced with latest version from f-droid, v 2.5.2

Expected behavior App should handle the exception and tell the user about it, not crash.

Debug log Stack trace:

Build version: 2.5.2 Build date: 1981-01-01 01:01:02 Current date: 2024-01-14 21:37:54 Device: Fairphone FP4 OS version: Android 13 (SDK 33)

Stack trace:
android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: scaleMeasurements.userId, scaleMeasurements.datetime (code 2067 SQLITE_CONSTRAINT_UNIQUE) at android.database.sqlite.SQLiteConnection.nativeExecuteForChangedRowCount(Native Method) at android.database.sqlite.SQLiteConnection.executeForChangedRowCount(SQLiteConnection.java:913) at android.database.sqlite.SQLiteSession.executeForChangedRowCount(SQLiteSession.java:756) at android.database.sqlite.SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:67) at androidx.sqlite.db.framework.FrameworkSQLiteStatement.executeUpdateDelete(FrameworkSQLiteStatement.kt:38) at androidx.room.EntityDeletionOrUpdateAdapter.handle(EntityDeletionOrUpdateAdapter.kt:62) at com.health.openscale.core.database.ScaleMeasurementDAO_Impl.update(ScaleMeasurementDAO_Impl.java:172) at com.health.openscale.core.OpenScale.updateScaleMeasurement(OpenScale.java:416) at com.health.openscale.gui.measurement.MeasurementEntryFragment.saveScaleData(MeasurementEntryFragment.java:346) at com.health.openscale.gui.measurement.MeasurementEntryFragment.onOptionsItemSelected(MeasurementEntryFragment.java:200) at androidx.fragment.app.Fragment.performOptionsItemSelected(Fragment.java:3284) at androidx.fragment.app.FragmentManager.dispatchOptionsItemSelected(FragmentManager.java:3168) at androidx.fragment.app.Fragment.performOptionsItemSelected(Fragment.java:3288) at androidx.fragment.app.FragmentManager.dispatchOptionsItemSelected(FragmentManager.java:3168) at androidx.fragment.app.FragmentManager$2.onMenuItemSelected(FragmentManager.java:503) at androidx.core.view.MenuHostHelper.onMenuItemSelected(MenuHostHelper.java:107) at androidx.activity.ComponentActivity.onMenuItemSelected(ComponentActivity.java:557) at androidx.fragment.app.FragmentActivity.onMenuItemSelected(FragmentActivity.java:264) at androidx.appcompat.app.AppCompatActivity.onMenuItemSelected(AppCompatActivity.java:269) at androidx.appcompat.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:110) at androidx.appcompat.app.ToolbarActionBar$2.onMenuItemClick(ToolbarActionBar.java:66) at androidx.appcompat.widget.Toolbar$1.onMenuItemClick(Toolbar.java:225) at androidx.appcompat.widget.ActionMenuView$MenuBuilderCallback.onMenuItemSelected(ActionMenuView.java:781) at androidx.appcompat.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:836) at androidx.appcompat.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:159) at androidx.appcompat.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:987) at androidx.appcompat.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:977) at androidx.appcompat.widget.ActionMenuView.invokeItem(ActionMenuView.java:625) at androidx.appcompat.view.menu.ActionMenuItemView.onClick(ActionMenuItemView.java:156) at android.view.View.performClick(View.java:7542) at android.view.View.performClickInternal(View.java:7519) at android.view.View.-$$Nest$mperformClickInternal(Unknown Source:0) at android.view.View$PerformClick.run(View.java:29476) at android.os.Handler.handleCallback(Handler.java:942) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loopOnce(Looper.java:201) at android.os.Looper.loop(Looper.java:288) at android.app.ActivityThread.main(ActivityThread.java:7924) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)

oliexdev commented 4 months ago

the time/date needs to be unique, it is not allowed to have two measurements with the exact date/time.

JanKanis commented 4 months ago

It's fine if measurements need to have a unique datetime, but duplicate datetimes should not crash the app, that should only show some kind of error to the user.