diff --git a/android_app/app/src/main/java/com/health/openscale/core/OpenScale.java b/android_app/app/src/main/java/com/health/openscale/core/OpenScale.java index d59db68d..c570e6da 100644 --- a/android_app/app/src/main/java/com/health/openscale/core/OpenScale.java +++ b/android_app/app/src/main/java/com/health/openscale/core/OpenScale.java @@ -16,6 +16,11 @@ package com.health.openscale.core; +import android.content.Context; +import android.os.Handler; +import android.os.Message; +import android.util.Log; + import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; @@ -27,11 +32,6 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; -import android.content.Context; -import android.os.Handler; -import android.os.Message; -import android.util.Log; - public class OpenScale { private static OpenScale instance; @@ -144,6 +144,14 @@ public class OpenScale { scaleDBEntries = scaleDB.getAllDBEntries(); } + public int[] getCountsOfMonth(int year) { + return scaleDB.getCountsOfAllMonth(year); + } + + public ArrayList getAllDataOfMonth(int year, int month) { + return scaleDB.getAllDBEntriesOfMonth(year, month); + } + public void startBluetoothServer(String deviceName) { Log.d("OpenScale", "Bluetooth Server started! I am searching for device ..."); @@ -166,9 +174,6 @@ public class OpenScale { } } - /** - * The Handler that gets information back from the BluetoothChatService - */ private final Handler btHandler = new Handler() { @Override public void handleMessage(Message msg) { diff --git a/android_app/app/src/main/java/com/health/openscale/core/ScaleDatabase.java b/android_app/app/src/main/java/com/health/openscale/core/ScaleDatabase.java index 2384bbb1..5e5c033d 100644 --- a/android_app/app/src/main/java/com/health/openscale/core/ScaleDatabase.java +++ b/android_app/app/src/main/java/com/health/openscale/core/ScaleDatabase.java @@ -16,20 +16,20 @@ package com.health.openscale.core; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.Locale; - import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; + public class ScaleDatabase extends SQLiteOpenHelper { private static final int DATABASE_VERSION = 1; private static final String DATABASE_NAME = "openScaleDatabase.db"; @@ -96,13 +96,106 @@ public class ScaleDatabase extends SQLiteOpenHelper { Log.e("ScaleDatabase", "An error occured while inserting a new entry into the scale database"); } } - + + public int[] getCountsOfAllMonth(int year) { + int [] numOfMonth = new int[12]; + + SQLiteDatabase db = getReadableDatabase(); + + Calendar start_cal = Calendar.getInstance(); + Calendar end_cal = Calendar.getInstance(); + + for (int i=0; i<12; i++) { + start_cal.set(year, i, 1, 0, 0, 0); + end_cal.set(year, i, 1, 0, 0, 0); + end_cal.add(Calendar.MONTH, 1); + + Cursor cursorScaleDB = db.query( + TABLE_NAME, // The table to query + new String[]{"count(*)"}, // The columns to return + COLUMN_NAME_DATE_TIME + " >= ? AND " + COLUMN_NAME_DATE_TIME + " < ? ", // The columns for the WHERE clause + new String[]{formatDateTime.format(start_cal.getTime()), formatDateTime.format(end_cal.getTime())}, // The values for the WHERE clause + null, // don't group the rows + null, // don't filter by row groups + null // The sort order + ); + + cursorScaleDB.moveToFirst(); + + numOfMonth[i] = cursorScaleDB.getInt(0); + } + + return numOfMonth; + } + + + public ArrayList getAllDBEntriesOfMonth(int year, int month) { + SQLiteDatabase db = getReadableDatabase(); + ArrayList scaleDBEntries = new ArrayList(); + + String[] projection = { + COLUMN_NAME_ID, + COLUMN_NAME_DATE_TIME, + COLUMN_NAME_WEIGHT, + COLUMN_NAME_FAT, + COLUMN_NAME_WATER, + COLUMN_NAME_MUSCLE + }; + + String sortOrder = COLUMN_NAME_DATE_TIME + " DESC"; + + Calendar start_cal = Calendar.getInstance(); + Calendar end_cal = Calendar.getInstance(); + + start_cal.set(year, month, 1, 0, 0, 0); + end_cal.set(year, month, 1, 0, 0, 0); + end_cal.add(Calendar.MONTH, 1); + + Cursor cursorScaleDB = db.query( + TABLE_NAME, // The table to query + projection, // The columns to return + COLUMN_NAME_DATE_TIME + " >= ? AND " + COLUMN_NAME_DATE_TIME + " < ? ", // The columns for the WHERE clause + new String[]{formatDateTime.format(start_cal.getTime()), formatDateTime.format(end_cal.getTime())}, // The values for the WHERE clause + null, // don't group the rows + null, // don't filter by row groups + sortOrder // The sort order + ); + + try { + cursorScaleDB.moveToFirst(); + + while (!cursorScaleDB.isAfterLast()) { + ScaleData dataEntry = new ScaleData(); + + dataEntry.id = cursorScaleDB.getLong(cursorScaleDB.getColumnIndexOrThrow(COLUMN_NAME_ID)); + String date_time = cursorScaleDB.getString(cursorScaleDB.getColumnIndexOrThrow(COLUMN_NAME_DATE_TIME)); + dataEntry.weight = cursorScaleDB.getFloat(cursorScaleDB.getColumnIndexOrThrow(COLUMN_NAME_WEIGHT)); + dataEntry.fat = cursorScaleDB.getFloat(cursorScaleDB.getColumnIndexOrThrow(COLUMN_NAME_FAT)); + dataEntry.water = cursorScaleDB.getFloat(cursorScaleDB.getColumnIndexOrThrow(COLUMN_NAME_WATER)); + dataEntry.muscle = cursorScaleDB.getFloat(cursorScaleDB.getColumnIndexOrThrow(COLUMN_NAME_MUSCLE)); + + Date date = formatDateTime.parse(date_time); + + dataEntry.date_time = date; + + scaleDBEntries.add(dataEntry); + + cursorScaleDB.moveToNext(); + } + } catch (ParseException ex) { + Log.e("ScaleDatabase", "Can't parse the date time string: " + ex.getMessage()); + } + catch ( IllegalArgumentException ex) { + Log.e("ScaleDatabase", "Illegal argument while reading from scale database: " + ex.getMessage()); + } + + return scaleDBEntries; + } + public ArrayList getAllDBEntries() { SQLiteDatabase db = getReadableDatabase(); ArrayList scaleDBEntries = new ArrayList(); - - // Define a projection that specifies which columns from the database - // you will actually use after this query. + String[] projection = { COLUMN_NAME_ID, COLUMN_NAME_DATE_TIME, @@ -112,7 +205,6 @@ public class ScaleDatabase extends SQLiteOpenHelper { COLUMN_NAME_MUSCLE }; - // How you want the results sorted in the resulting Cursor String sortOrder = COLUMN_NAME_DATE_TIME + " DESC"; Cursor cursorScaleDB = db.query( diff --git a/android_app/app/src/main/java/com/health/openscale/gui/GraphFragment.java b/android_app/app/src/main/java/com/health/openscale/gui/GraphFragment.java index b760b43c..020c5119 100644 --- a/android_app/app/src/main/java/com/health/openscale/gui/GraphFragment.java +++ b/android_app/app/src/main/java/com/health/openscale/gui/GraphFragment.java @@ -16,32 +16,51 @@ package com.health.openscale.gui; -import java.text.DateFormat; -import java.util.ArrayList; -import java.util.List; - -import lecho.lib.hellocharts.model.Axis; -import lecho.lib.hellocharts.model.AxisValue; -import lecho.lib.hellocharts.model.Line; -import lecho.lib.hellocharts.model.LineChartData; -import lecho.lib.hellocharts.model.PointValue; -import lecho.lib.hellocharts.view.LineChartView; import android.graphics.Color; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.TextView; import com.health.openscale.R; +import com.health.openscale.core.OpenScale; import com.health.openscale.core.ScaleData; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.Locale; + +import lecho.lib.hellocharts.gesture.ZoomType; +import lecho.lib.hellocharts.model.Axis; +import lecho.lib.hellocharts.model.AxisValue; +import lecho.lib.hellocharts.model.Column; +import lecho.lib.hellocharts.model.ColumnChartData; +import lecho.lib.hellocharts.model.ColumnValue; +import lecho.lib.hellocharts.model.Line; +import lecho.lib.hellocharts.model.LineChartData; +import lecho.lib.hellocharts.model.PointValue; +import lecho.lib.hellocharts.model.SimpleValueFormatter; +import lecho.lib.hellocharts.model.Viewport; +import lecho.lib.hellocharts.util.Utils; +import lecho.lib.hellocharts.view.ColumnChartView; +import lecho.lib.hellocharts.view.LineChartView; + public class GraphFragment extends Fragment implements FragmentUpdateListener { private View graphView; - private LineChartView chartView; - - public GraphFragment() { + private LineChartView chartTop; + private ColumnChartView chartBottom; + private TextView txtYear; + private OpenScale openScale; + + private Calendar yearCal; + + public GraphFragment() { + yearCal = Calendar.getInstance(); } @Override @@ -49,71 +68,177 @@ public class GraphFragment extends Fragment implements FragmentUpdateListener { { graphView = inflater.inflate(R.layout.fragment_graph, container, false); - chartView = (LineChartView) graphView.findViewById(R.id.data_chart); - + chartTop = (LineChartView) graphView.findViewById(R.id.chart_top); + chartBottom = (ColumnChartView) graphView.findViewById(R.id.chart_bottom); + + txtYear = (TextView) graphView.findViewById(R.id.txtYear); + txtYear.setText(Integer.toString(yearCal.get(Calendar.YEAR))); + + graphView.findViewById(R.id.btnLeftYear).setOnClickListener(new View.OnClickListener() { + public void onClick(View view) { + yearCal.roll(Calendar.YEAR, false); + txtYear.setText(Integer.toString(yearCal.get(Calendar.YEAR))); + updateOnView(null); + } + }); + + graphView.findViewById(R.id.btnRightYear).setOnClickListener(new View.OnClickListener() { + public void onClick(View view) { + yearCal.roll(Calendar.YEAR, true); + txtYear.setText(Integer.toString(yearCal.get(Calendar.YEAR))); + updateOnView(null); + } + }); + openScale = OpenScale.getInstance(graphView.getContext()); + return graphView; } @Override public void updateOnView(ArrayList scaleDBEntries) - { - if (scaleDBEntries.isEmpty()) { - LineChartData data = new LineChartData(); - chartView.setLineChartData(data); - - return; - } - - List axisValues = new ArrayList(); - List valuesWeight = new ArrayList(); - List valuesFat = new ArrayList(); - List valuesWater = new ArrayList(); - List valuesMuscle = new ArrayList(); - List lines = new ArrayList(); - - - for(ScaleData scaleEntry: scaleDBEntries) - { - valuesWeight.add(new PointValue(scaleEntry.date_time.getTime(), scaleEntry.weight)); - valuesFat.add(new PointValue(scaleEntry.date_time.getTime(), scaleEntry.fat)); - valuesWater.add(new PointValue(scaleEntry.date_time.getTime(), scaleEntry.water)); - valuesMuscle.add(new PointValue(scaleEntry.date_time.getTime(), scaleEntry.muscle)); - - axisValues.add(new AxisValue(scaleEntry.date_time.getTime(), DateFormat.getDateInstance(DateFormat.SHORT).format(scaleEntry.date_time).toCharArray())); - } - - Line lineWeight = new Line(valuesWeight).setColor(Color.GREEN); - Line lineFat = new Line(valuesFat).setColor(Color.RED); - Line lineWater = new Line(valuesWater).setColor(Color.BLUE); - Line lineMuscle = new Line(valuesMuscle).setColor(Color.GRAY); - - lines.add(lineWeight); - lines.add(lineFat); - lines.add(lineWater); - lines.add(lineMuscle); - - lineWeight.setHasLabels(true); - lineWeight.setHasLabelsOnlyForSelected(true); - lineFat.setHasLabelsOnlyForSelected(true); - - LineChartData data = new LineChartData(); - - Axis axisX = new Axis(axisValues); - Axis axisY = new Axis(); - - axisY.setHasLines(true); - - axisX.setName(getResources().getString(R.string.label_x_axis)); - axisY.setName(getResources().getString(R.string.label_y_axis)); - - axisX.setTextColor(Color.BLACK); - axisY.setTextColor(Color.BLACK); - - data.setAxisXBottom(axisX); - data.setAxisYLeft(axisY); - - data.setLines(lines); - - chartView.setLineChartData(data); + { + generateColumnData(); } + + private void generateLineData(Calendar cal) + { + ArrayList scaleDBEntries = openScale.getAllDataOfMonth(yearCal.get(Calendar.YEAR), cal.get(Calendar.MONTH)); + + SimpleDateFormat day_date = new SimpleDateFormat("dd", Locale.getDefault()); + + cal.set(Calendar.DAY_OF_MONTH, 1); + int max_days = cal.getActualMaximum(Calendar.DAY_OF_MONTH); + + List axisValues = new ArrayList(); + + for (int i=0; i valuesWeight = new ArrayList(); + List valuesFat = new ArrayList(); + List valuesWater = new ArrayList(); + List valuesMuscle = new ArrayList(); + List lines = new ArrayList(); + + Calendar calDB = Calendar.getInstance(); + + for(ScaleData scaleEntry: scaleDBEntries) + { + calDB.setTime(scaleEntry.date_time); + + valuesWeight.add(new PointValue(calDB.get(Calendar.DAY_OF_MONTH), scaleEntry.weight)); + valuesFat.add(new PointValue(calDB.get(Calendar.DAY_OF_MONTH), scaleEntry.fat)); + valuesWater.add(new PointValue(calDB.get(Calendar.DAY_OF_MONTH), scaleEntry.water)); + valuesMuscle.add(new PointValue(calDB.get(Calendar.DAY_OF_MONTH), scaleEntry.muscle)); + } + + + Line lineWeight = new Line(valuesWeight). + setColor(Utils.COLOR_VIOLET). + setCubic(true). + setHasLabels(true). + setFormatter(new SimpleValueFormatter(1, false, null, null)); + Line lineFat = new Line(valuesFat). + setColor(Utils.COLOR_ORANGE). + setCubic(true). + setHasLabels(true). + setFormatter(new SimpleValueFormatter(1, false, null, null)); + Line lineWater = new Line(valuesWater). + setColor(Utils.COLOR_BLUE). + setCubic(true). + setHasLabels(true). + setFormatter(new SimpleValueFormatter(1, false, null, null)); + Line lineMuscle = new Line(valuesMuscle). + setColor(Utils.COLOR_GREEN). + setCubic(true). + setHasLabels(true). + setFormatter(new SimpleValueFormatter(1, false, null, null)); + + lines.add(lineWeight); + lines.add(lineFat); + lines.add(lineWater); + lines.add(lineMuscle); + + LineChartData lineData = new LineChartData(lines); + lineData.setAxisXBottom(new Axis(axisValues). + setHasLines(true). + setTextColor(Color.BLACK). + setName(getResources().getString(R.string.label_x_axis)) + ); + + lineData.setAxisYLeft(new Axis(). + setHasLines(true). + setMaxLabelChars(3). + setTextColor(Color.BLACK). + setName(getResources().getString(R.string.label_y_axis)) + ); + + + + chartTop.setLineChartData(lineData); + chartTop.setViewportCalculationEnabled(false); + + Viewport v = new Viewport(0, 110, max_days, 0); + chartTop.setMaximumViewport(v); + chartTop.setCurrentViewport(v, true); + + chartTop.setZoomType(ZoomType.HORIZONTAL); + } + + private void generateColumnData() + { + int[] numOfMonth = openScale.getCountsOfMonth(yearCal.get(Calendar.YEAR)); + + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.MONTH, Calendar.JANUARY); + + SimpleDateFormat month_date = new SimpleDateFormat("MMM", Locale.getDefault()); + + List axisValues = new ArrayList(); + List columns = new ArrayList(); + + for (int i=0; i<12; i++) { + String month_name = month_date.format(cal.getTime()); + + axisValues.add(new AxisValue(i, month_name.toCharArray())); + List values = new ArrayList(); + values.add(new ColumnValue(numOfMonth[i], Utils.COLORS[i % Utils.COLORS.length])); + + columns.add(new Column(values).setHasLabelsOnlyForSelected(true)); + + cal.add(Calendar.MONTH, 1); + } + + ColumnChartData columnData = new ColumnChartData(columns); + + columnData.setAxisXBottom(new Axis(axisValues).setHasLines(true).setTextColor(Color.BLACK)); + + chartBottom.setColumnChartData(columnData); + chartBottom.setValueSelectionEnabled(true); + chartBottom.setZoomType(ZoomType.HORIZONTAL); + chartBottom.setOnValueTouchListener(new ValueTouchListener()); + + generateLineData(cal); + } + + private class ValueTouchListener implements ColumnChartView.ColumnChartOnValueTouchListener { + @Override + public void onValueTouched(int selectedLine, int selectedValue, ColumnValue value) { + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.MONTH, Calendar.JANUARY); + cal.add(Calendar.MONTH, selectedLine); + + generateLineData(cal); + } + + @Override + public void onNothingTouched() { + + } + } } diff --git a/android_app/app/src/main/java/com/health/openscale/gui/OverviewFragment.java b/android_app/app/src/main/java/com/health/openscale/gui/OverviewFragment.java index b35a8e80..adf44172 100644 --- a/android_app/app/src/main/java/com/health/openscale/gui/OverviewFragment.java +++ b/android_app/app/src/main/java/com/health/openscale/gui/OverviewFragment.java @@ -15,18 +15,9 @@ */ package com.health.openscale.gui; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.List; - -import lecho.lib.hellocharts.model.ArcValue; -import lecho.lib.hellocharts.model.PieChartData; -import lecho.lib.hellocharts.util.Utils; -import lecho.lib.hellocharts.view.PieChartView; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -36,6 +27,16 @@ import com.health.openscale.R; import com.health.openscale.core.OpenScale; import com.health.openscale.core.ScaleData; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; + +import lecho.lib.hellocharts.model.ArcValue; +import lecho.lib.hellocharts.model.PieChartData; +import lecho.lib.hellocharts.model.SimpleValueFormatter; +import lecho.lib.hellocharts.util.Utils; +import lecho.lib.hellocharts.view.PieChartView; + public class OverviewFragment extends Fragment implements FragmentUpdateListener { private View overviewView; private PieChartView pieChart; @@ -83,6 +84,7 @@ public class OverviewFragment extends Fragment implements FragmentUpdateListener PieChartData pieChartData = new PieChartData(arcValues); pieChartData.setHasLabels(true); + pieChartData.setFormatter(new SimpleValueFormatter(1, false, null, " %".toCharArray())); pieChartData.setHasCenterCircle(true); pieChartData.setCenterText1(Float.toString(lastEntry.weight) + " " + getResources().getString(R.string.weight_unit)); pieChartData.setCenterText1FontSize(35); diff --git a/android_app/app/src/main/res/layout/fragment_graph.xml b/android_app/app/src/main/res/layout/fragment_graph.xml index be01e97e..c4d06dc1 100644 --- a/android_app/app/src/main/res/layout/fragment_graph.xml +++ b/android_app/app/src/main/res/layout/fragment_graph.xml @@ -13,13 +13,63 @@ android:layout_height="fill_parent" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" - android:orientation="vertical" > - - + android:orientation="vertical" + android:id="@+id/linearLayout"> - + + +