diff --git a/app/src/main/assets/activity_stacked.svg b/app/src/main/assets/activity_stacked.svg new file mode 100644 index 000000000..882ecdd75 --- /dev/null +++ b/app/src/main/assets/activity_stacked.svg @@ -0,0 +1,102 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/assets/ic_intensity_hollow.svg b/app/src/main/assets/ic_intensity_hollow.svg new file mode 100644 index 000000000..0fa7d66a1 --- /dev/null +++ b/app/src/main/assets/ic_intensity_hollow.svg @@ -0,0 +1,59 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/app/src/main/assets/ic_shoe_prints_many.svg b/app/src/main/assets/ic_shoe_prints_many.svg new file mode 100644 index 000000000..3462b3820 --- /dev/null +++ b/app/src/main/assets/ic_shoe_prints_many.svg @@ -0,0 +1,70 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityListingAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityListingAdapter.java index 70095e535..cac1e5e23 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityListingAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityListingAdapter.java @@ -1,59 +1,273 @@ package nodomain.freeyourgadget.gadgetbridge.activities.charts; import android.content.Context; +import android.graphics.Color; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.github.mikephil.charting.charts.PieChart; +import com.github.mikephil.charting.data.PieData; +import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.PieEntry; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.text.DecimalFormat; +import java.util.ArrayList; import java.util.Date; import java.util.concurrent.TimeUnit; +import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.adapter.AbstractActivityListingAdapter; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySession; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; -public class ActivityListingAdapter extends AbstractActivityListingAdapter { +public class ActivityListingAdapter extends AbstractActivityListingAdapter { + public static final String ACTIVE_STEPS_CHART_COLOR = "#3498db"; + public static final String DISTANCE_CHART_COLOR = "#f1c40f"; + public static final String ACTIVE_TIME_CHART_COLOR = "#e74c3c"; + protected static final Logger LOG = LoggerFactory.getLogger(AbstractWeekChartFragment.class); + private final int SESSION_SUMMARY = ActivitySession.SESSION_SUMMARY; + ActivityUser activityUser = new ActivityUser(); + int stepsGoal = activityUser.getStepsGoal(); + int distanceMeters = activityUser.getDistanceMeters(); + long activeTimeMillis = activityUser.getActiveTimeMinutes() * 60 * 1000L; + public ActivityListingAdapter(Context context) { super(context); } @Override - protected String getDateLabel(StepAnalysis.StepSession item) { + protected View fill_dashboard(ActivitySession item, int position, View view, ViewGroup parent, Context context) { + LayoutInflater inflater = (LayoutInflater) context + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = inflater.inflate(R.layout.activity_list_dashboard_item, parent, false); + + PieChart ActiveStepsChart; + PieChart DistanceChart; + PieChart ActiveTimeChart; + + ActiveStepsChart = view.findViewById(R.id.activity_dashboard_piechart1); + setUpChart(ActiveStepsChart); + setStepsData(item, ActiveStepsChart, context); + + DistanceChart = view.findViewById(R.id.activity_dashboard_piechart2); + setUpChart(DistanceChart); + setDistanceData(item, DistanceChart, context); + + ActiveTimeChart = view.findViewById(R.id.activity_dashboard_piechart3); + setUpChart(ActiveTimeChart); + setDurationData(item, ActiveTimeChart, context); + + TextView stepLabel = view.findViewById(R.id.line_layout_step_label); + TextView stepTotalLabel = view.findViewById(R.id.line_layout_total_step_label); + + TextView distanceLabel = view.findViewById(R.id.line_layout_distance_label); + + TextView hrLabel = view.findViewById(R.id.heartrate_widget_label); + + TextView intensityLabel = view.findViewById(R.id.intensity_widget_label); + TextView intensity2Label = view.findViewById(R.id.line_layout_intensity2_label); + + + TextView durationLabel = view.findViewById(R.id.line_layout_duration_label); + TextView sessionCountLabel = view.findViewById(R.id.line_layout_count_label); + + LinearLayout durationLayout = view.findViewById(R.id.line_layout_duration); + LinearLayout countLayout = view.findViewById(R.id.line_layout_count); + + + View hrLayout = view.findViewById(R.id.heartrate_widget_icon); + LinearLayout stepsLayout = view.findViewById(R.id.line_layout_step); + LinearLayout stepsTotalLayout = view.findViewById(R.id.line_layout_total_step); + + + LinearLayout distanceLayout = view.findViewById(R.id.line_layout_distance); + View intensityLayout = view.findViewById(R.id.intensity_widget_icon); + View intensity2Layout = view.findViewById(R.id.line_layout_intensity2); + + stepLabel.setText(getStepLabel(item)); + stepTotalLabel.setText(getStepTotalLabel(item)); + + distanceLabel.setText(getDistanceLabel(item)); + hrLabel.setText(getHrLabel(item)); + intensityLabel.setText(getIntensityLabel(item)); + intensity2Label.setText(getIntensityLabel(item)); + durationLabel.setText(getDurationLabel(item)); + sessionCountLabel.setText(getSessionCountLabel(item)); + + if (!hasHR(item)) { + hrLayout.setVisibility(View.GONE); + hrLabel.setVisibility(View.GONE); + } else { + hrLayout.setVisibility(View.VISIBLE); + hrLabel.setVisibility(View.VISIBLE); + } + + if (!hasIntensity(item)) { + intensityLayout.setVisibility(View.GONE); + intensity2Layout.setVisibility(View.GONE); + intensityLabel.setVisibility(View.GONE); + intensity2Label.setVisibility(View.GONE); + } else { + intensityLayout.setVisibility(View.VISIBLE); + intensity2Layout.setVisibility(View.VISIBLE); + intensityLabel.setVisibility(View.VISIBLE); + intensity2Label.setVisibility(View.VISIBLE); + } + + if (!hasDistance(item)) { + distanceLayout.setVisibility(View.GONE); + } else { + distanceLayout.setVisibility(View.VISIBLE); + } + + if (!hasSteps(item)) { + stepsLayout.setVisibility(View.GONE); + } else { + stepsLayout.setVisibility(View.VISIBLE); + } + + if (!hasTotalSteps(item)) { + stepsTotalLayout.setVisibility(View.GONE); + countLayout.setVisibility(View.GONE); + durationLayout.setVisibility(View.GONE); + } else { + stepsTotalLayout.setVisibility(View.VISIBLE); + countLayout.setVisibility(View.VISIBLE); + durationLayout.setVisibility(View.VISIBLE); + } + + return view; + } + + private void setStepsData(ActivitySession item, PieChart DashboardChart, Context context) { + ArrayList entries = new ArrayList<>(); + int steps = item.getActiveSteps(); + entries.add(new PieEntry((float) steps)); + + if (steps < stepsGoal) { + entries.add(new PieEntry((float) (stepsGoal - steps))); + } + + DashboardChart.setCenterText(String.format("%d%%\n%s", (int) (steps * 100 / stepsGoal), context.getString(R.string.activity_list_summary_active_steps))); + + PieDataSet dataSet = new PieDataSet(entries, ""); + dataSet.setSliceSpace(0f); + dataSet.setSelectionShift(5f); + + dataSet.setColors(Color.parseColor(ACTIVE_STEPS_CHART_COLOR), Color.LTGRAY); + PieData data = new PieData(dataSet); + data.setValueTextSize(0f); + data.setValueTextColor(Color.WHITE); + DashboardChart.setData(data); + DashboardChart.invalidate(); + } + + private void setDistanceData(ActivitySession item, PieChart DashboardChart, Context context) { + ArrayList entries = new ArrayList<>(); + float distance = item.getDistance(); + entries.add(new PieEntry(distance)); + + if (distance < distanceMeters) { + entries.add(new PieEntry((float) (distanceMeters - distance))); + } + + DashboardChart.setCenterText(String.format("%d%%\n%s", (int) (distance * 100 / distanceMeters), context.getString(R.string.distance))); + PieDataSet dataSet = new PieDataSet(entries, ""); + dataSet.setSliceSpace(0f); + dataSet.setSelectionShift(5f); + + dataSet.setColors(Color.parseColor(DISTANCE_CHART_COLOR), Color.LTGRAY); + PieData data = new PieData(dataSet); + data.setValueTextSize(0f); + data.setValueTextColor(Color.WHITE); + DashboardChart.setData(data); + DashboardChart.invalidate(); + } + + private void setDurationData(ActivitySession item, PieChart DashboardChart, Context context) { + ArrayList entries = new ArrayList<>(); + long duration = item.getEndTime().getTime() - item.getStartTime().getTime(); + entries.add(new PieEntry((float) duration)); + + if (duration < activeTimeMillis) { + entries.add(new PieEntry((float) (activeTimeMillis - duration))); + } + + DashboardChart.setCenterText(String.format("%d%%\n%s", (int) (duration * 100 / activeTimeMillis), context.getString(R.string.activity_list_summary_active_time))); + PieDataSet dataSet = new PieDataSet(entries, ""); + dataSet.setSliceSpace(0f); + dataSet.setSelectionShift(5f); + dataSet.setColors(Color.parseColor(ACTIVE_TIME_CHART_COLOR), Color.LTGRAY); + PieData data = new PieData(dataSet); + data.setValueTextSize(0f); + data.setValueTextColor(Color.WHITE); + DashboardChart.setData(data); + DashboardChart.invalidate(); + } + + private void setUpChart(PieChart DashboardChart) { + DashboardChart.setNoDataText(""); + DashboardChart.getLegend().setEnabled(false); + DashboardChart.setDrawHoleEnabled(true); + DashboardChart.setHoleColor(Color.WHITE); + DashboardChart.getDescription().setText(""); + DashboardChart.setTransparentCircleColor(Color.WHITE); + DashboardChart.setTransparentCircleAlpha(110); + DashboardChart.setHoleRadius(70f); + DashboardChart.setTransparentCircleRadius(75f); + DashboardChart.setDrawCenterText(true); + DashboardChart.setRotationEnabled(true); + DashboardChart.setHighlightPerTapEnabled(true); + DashboardChart.setCenterTextOffset(0, 0); + } + + @Override + protected String getDateLabel(ActivitySession item) { return ""; } @Override - protected boolean hasGPS(StepAnalysis.StepSession item) { + protected boolean hasGPS(ActivitySession item) { return false; } @Override - protected boolean hasDate(StepAnalysis.StepSession item) { + protected boolean hasDate(ActivitySession item) { return false; } @Override - protected String getTimeFrom(StepAnalysis.StepSession item) { + protected String getTimeFrom(ActivitySession item) { Date time = item.getStartTime(); return DateTimeUtils.formatTime(time.getHours(), time.getMinutes()); } @Override - protected String getTimeTo(StepAnalysis.StepSession item) { + protected String getTimeTo(ActivitySession item) { Date time = item.getEndTime(); return DateTimeUtils.formatTime(time.getHours(), time.getMinutes()); } @Override - protected String getActivityName(StepAnalysis.StepSession item) { + protected String getActivityName(ActivitySession item) { return ActivityKind.asString(item.getActivityKind(), getContext()); } @Override - protected String getStepLabel(StepAnalysis.StepSession item) { - return String.valueOf(item.getSteps()); + protected String getStepLabel(ActivitySession item) { + return String.valueOf(item.getActiveSteps()); } @Override - protected String getDistanceLabel(StepAnalysis.StepSession item) { + protected String getDistanceLabel(ActivitySession item) { float distance = item.getDistance(); String unit = "###m"; if (distance > 2000) { @@ -61,69 +275,74 @@ public class ActivityListingAdapter extends AbstractActivityListingAdapter 0) { - return true; - } else { - return false; - } + protected String getSessionCountLabel(ActivitySession item) { + return String.valueOf(item.getSessionCount()); } @Override - public boolean hasIntensity(StepAnalysis.StepSession item) { - if (item.getIntensity() > 0) { - return true; - } else { - return false; - } + public boolean hasHR(ActivitySession item) { + return item.getHeartRateAverage() > 0; } @Override - protected boolean hasDistance(StepAnalysis.StepSession item) { - if (item.getDistance() > 0) { - return true; - } else { - return false; - } + public boolean hasIntensity(ActivitySession item) { + return item.getIntensity() > 0; } @Override - protected boolean hasSteps(StepAnalysis.StepSession item) { - if (item.getSteps() > 0) { - return true; - } else { - return false; - } + protected boolean hasDistance(ActivitySession item) { + return item.getDistance() > 0; } + @Override + protected boolean hasSteps(ActivitySession item) { + return item.getActiveSteps() > 0; + } @Override - protected int getIcon(StepAnalysis.StepSession item) { + protected boolean hasTotalSteps(ActivitySession item) { + return item.getTotalDaySteps() > 0; + } + + @Override + protected boolean isSummary(ActivitySession item) { + int sessionType = item.getSessionType(); + return sessionType == SESSION_SUMMARY; + } + + @Override + protected boolean isEmptySummary(ActivitySession item) { + return item.getIsEmptySummary(); + } + + @Override + protected String getStepTotalLabel(ActivitySession item) { + return String.valueOf(item.getTotalDaySteps()); + } + + @Override + protected int getIcon(ActivitySession item) { return ActivityKind.getIconId(item.getActivityKind()); } - - } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityListingChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityListingChartFragment.java index 26b3ccacb..bc5add220 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityListingChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityListingChartFragment.java @@ -42,14 +42,14 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySession; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; - public class ActivityListingChartFragment extends AbstractChartFragment { protected static final Logger LOG = LoggerFactory.getLogger(ActivityListingChartFragment.class); int tsDateFrom; + private View rootView; - private List activitySamples; private ActivityListingAdapter stepListAdapter; private TextView stepsDateView; @@ -57,18 +57,16 @@ public class ActivityListingChartFragment extends AbstractChartFragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { rootView = inflater.inflate(R.layout.fragment_steps_list, container, false); - + getChartsHost().enableSwipeRefresh(false); ListView stepsList = rootView.findViewById(R.id.itemListView); - View headerView = inflater.inflate(R.layout.heartrate_average_widget,null, false); - stepsList.addHeaderView(headerView); stepListAdapter = new ActivityListingAdapter(getContext()); stepsList.setAdapter(stepListAdapter); stepsDateView = rootView.findViewById(R.id.stepsDateView); refresh(); - return rootView; } + @Override public String getTitle() { return "Steps list"; @@ -86,36 +84,40 @@ public class ActivityListingChartFragment extends AbstractChartFragment { } } - @Override protected ChartsData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device) { - //trying to fit found peg into square hole of the Gb Charts fragment system - //get the data + List activitySamples; activitySamples = getSamples(db, device); - return null; + List stepSessions = null; + StepAnalysis stepAnalysis = new StepAnalysis(); + boolean isEmptySummary = false; + + if (activitySamples != null) { + stepSessions = stepAnalysis.calculateStepSessions(activitySamples); + if (stepSessions.toArray().length == 0) { + isEmptySummary = true; + } + stepSessions = stepAnalysis.calculateSummary(stepSessions, isEmptySummary); + } + return new MyChartsData(stepSessions); } @Override protected void updateChartsnUIThread(ChartsData chartsData) { - //top displays selected date - stepsDateView.setText(DateTimeUtils.formatDate(new Date(tsDateFrom * 1000L))); - //calculate active sessions - StepAnalysis stepAnalysis = new StepAnalysis(); - if (activitySamples != null) { - List stepSessions = stepAnalysis.calculateStepSessions(activitySamples); - if (stepSessions.toArray().length == 0) { - stepSessions = create_empty_record(); - getChartsHost().enableSwipeRefresh(true); //try to enable pull to refresh, might be needed - } else { - getChartsHost().enableSwipeRefresh(false); //disable pull to refresh as it collides with swipable view - } - //push to the adapter - stepListAdapter.setItems(stepSessions, true); + MyChartsData mcd = (MyChartsData) chartsData; + if (mcd.getStepSessions().toArray().length == 0) { + getChartsHost().enableSwipeRefresh(true); //try to enable pull to refresh, might be needed + } else { + getChartsHost().enableSwipeRefresh(false); //disable pull to refresh as it collides with swipable view } + + stepsDateView.setText(DateTimeUtils.formatDate(new Date(tsDateFrom * 1000L))); + stepListAdapter.setItems(mcd.getStepSessions(), true); } @Override protected void renderCharts() { + } @Override @@ -135,16 +137,17 @@ public class ActivityListingChartFragment extends AbstractChartFragment { return getAllSamples(db, device, tsFrom, tsTo); } - private List create_empty_record() { - //have an "Unknown Activity" in the list in case there are no active sessions - List result = new ArrayList<>(); - int tsTo = tsDateFrom + 24 * 60 * 60 - 1; - if (DateUtils.isToday(tsDateFrom * 1000L)) { - Calendar day = Calendar.getInstance(); - day.set(Calendar.SECOND, 0); - tsTo = (int) (day.getTimeInMillis() / 1000); + private static class MyChartsData extends ChartsData { + private final List stepSessions; + + MyChartsData(List stepSessions) { + this.stepSessions = stepSessions; + } + + public List getStepSessions() { + return stepSessions; } - result.add(new StepAnalysis.StepSession(new Date(tsDateFrom * 1000L), new Date(tsTo * 1000L), 0, 0, 0, 0, ActivityKind.TYPE_UNKNOWN)); - return result; } + + } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java index 6d4601f4e..0ad73b27a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java @@ -21,7 +21,6 @@ import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.os.Bundle; -import android.util.Pair; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -244,7 +243,7 @@ public class SleepChartFragment extends AbstractChartFragment { int min = Collections.min(heartRateValues); int max = Collections.max(heartRateValues); int count = heartRateValues.toArray().length; - float sum = calculateHRSum(heartRateValues); + float sum = calculateSumOfInts(heartRateValues); float average = sum / count; return Triple.of(average, min, max); } @@ -257,7 +256,7 @@ public class SleepChartFragment extends AbstractChartFragment { return result; } - private float calculateHRSum(List samples) { + private float calculateSumOfInts(List samples) { float result = 0; for (Integer sample : samples) { result += sample; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/StepAnalysis.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/StepAnalysis.java index a7049734b..9b8b1a9e1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/StepAnalysis.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/StepAnalysis.java @@ -28,12 +28,14 @@ import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySession; public class StepAnalysis { protected static final Logger LOG = LoggerFactory.getLogger(StepAnalysis.class); + private int totalDailySteps = 0; - public List calculateStepSessions(List samples) { - List result = new ArrayList<>(); + public List calculateStepSessions(List samples) { + List result = new ArrayList<>(); ActivityUser activityUser = new ActivityUser(); double STEP_LENGTH_M; final int MIN_SESSION_LENGTH = 60 * GBApplication.getPrefs().getInt("chart_list_min_session_length", 5); @@ -41,6 +43,7 @@ public class StepAnalysis { final int MIN_STEPS_PER_MINUTE = GBApplication.getPrefs().getInt("chart_list_min_steps_per_minute", 40); int stepLengthCm = activityUser.getStepLengthCm(); int heightCm = activityUser.getHeightCm(); + totalDailySteps = 0; if (stepLengthCm == 0 && heightCm != 0) { STEP_LENGTH_M = heightCm * 0.43 * 0.01; @@ -68,6 +71,11 @@ public class StepAnalysis { HeartRateUtils heartRateUtilsInstance = HeartRateUtils.getInstance(); for (ActivitySample sample : samples) { + int steps = sample.getSteps(); + if (steps > 0) { + totalDailySteps += steps; + } + if (sample.getKind() != ActivityKind.TYPE_SLEEP //anything but sleep counts && !(sample instanceof TrailingActivitySample)) { //trailing samples have wrong date and make trailing activity have 0 duration if (heartRateUtilsInstance.isValidHeartRateValue(sample.getHeartRate())) { @@ -119,7 +127,7 @@ public class StepAnalysis { float distance = (float) (activeSteps * STEP_LENGTH_M); sessionEnd = new Date((sample.getTimestamp() - durationSinceLastActiveStep) * 1000L); activityKind = detect_activity_kind(session_length, activeSteps, heartRateAverage, activeIntensity); - result.add(new StepSession(sessionStart, sessionEnd, activeSteps, heartRateAverage, activeIntensity, distance, activityKind)); + result.add(new ActivitySession(sessionStart, sessionEnd, activeSteps, heartRateAverage, activeIntensity, distance, activityKind)); } sessionStart = null; } @@ -139,12 +147,69 @@ public class StepAnalysis { float distance = (float) (activeSteps * STEP_LENGTH_M); sessionEnd = getDateFromSample(previousSample); activityKind = detect_activity_kind(session_length, activeSteps, heartRateAverage, activeIntensity); - result.add(new StepSession(sessionStart, sessionEnd, activeSteps, heartRateAverage, activeIntensity, distance, activityKind)); + result.add(new ActivitySession(sessionStart, sessionEnd, activeSteps, heartRateAverage, activeIntensity, distance, activityKind)); } } return result; } + public List calculateSummary(List sessions, boolean empty) { + + HeartRateUtils heartRateUtilsInstance = HeartRateUtils.getInstance(); + Date startTime = null; + Date endTime = null; + int stepsSum = 0; + int heartRateAverage = 0; + List heartRateSum = new ArrayList<>(); + int distanceSum = 0; + float intensitySum = 0; + int sessionCount; + int durationSum = 0; + + for (ActivitySession session : sessions) { + startTime = session.getStartTime(); + endTime = session.getEndTime(); + durationSum += endTime.getTime() - startTime.getTime(); + stepsSum += session.getActiveSteps(); + distanceSum += session.getDistance(); + heartRateSum.add(session.getHeartRateAverage()); + intensitySum += session.getIntensity(); + } + + sessionCount = sessions.toArray().length; + if (heartRateSum.toArray().length > 0) { + heartRateAverage = calculateSumOfInts(heartRateSum) / heartRateSum.toArray().length; + } + if (!heartRateUtilsInstance.isValidHeartRateValue(heartRateAverage)) { + heartRateAverage = 0; + } + startTime = new Date(0); + endTime = new Date(durationSum); + + ActivitySession stepSessionSummary = new ActivitySession(startTime, endTime, + stepsSum, heartRateAverage, intensitySum, distanceSum, 0); + + stepSessionSummary.setSessionCount(sessionCount); + stepSessionSummary.setSessionType(ActivitySession.SESSION_SUMMARY); + stepSessionSummary.setEmptySummary(empty); + + + stepSessionSummary.setTotalDaySteps(totalDailySteps); + + List newList = new ArrayList<>(); + newList.add(stepSessionSummary); + newList.addAll(sessions); + return newList; + } + + private int calculateSumOfInts(List samples) { + int result = 0; + for (Integer sample : samples) { + result += sample; + } + return result; + } + private int detect_activity_kind(int session_length, int activeSteps, int heartRateAverage, float intensity) { final int MIN_STEPS_PER_MINUTE_FOR_RUN = GBApplication.getPrefs().getInt("chart_list_min_steps_per_minute_for_run", 120); int spm = (int) (activeSteps / (session_length / 60)); @@ -160,61 +225,7 @@ public class StepAnalysis { return ActivityKind.TYPE_ACTIVITY; } - private boolean isStep(ActivitySample sample) { - return sample.getKind() == ActivityKind.TYPE_WALKING || sample.getKind() == ActivityKind.TYPE_RUNNING || sample.getKind() == ActivityKind.TYPE_ACTIVITY; - } - private Date getDateFromSample(ActivitySample sample) { return new Date(sample.getTimestamp() * 1000L); } - - public static class StepSession { - private final Date startTime; - private final Date endTime; - private final int steps; - private final int heartRateAverage; - private final float intensity; - private final float distance; - private final int activityKind; - - StepSession(Date startTime, - Date endTime, - int steps, int heartRateAverage, float intensity, float distance, int activityKind) { - this.startTime = startTime; - this.endTime = endTime; - this.steps = steps; - this.heartRateAverage = heartRateAverage; - this.intensity = intensity; - this.distance = distance; - this.activityKind = activityKind; - } - - public Date getStartTime() { - return startTime; - } - - public Date getEndTime() { - return endTime; - } - - public int getSteps() { - return steps; - } - - public int getHeartRateAverage() { - return heartRateAverage; - } - - public int getActivityKind() { - return activityKind; - } - - public float getIntensity() { - return intensity; - } - - public float getDistance() { - return distance; - } - } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/AbstractActivityListingAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/AbstractActivityListingAdapter.java index 59f203823..bdb36225f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/AbstractActivityListingAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/AbstractActivityListingAdapter.java @@ -35,6 +35,8 @@ import java.util.ArrayList; import java.util.List; import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.activities.charts.StepAnalysis; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySession; /** * Adapter for displaying generic ItemWithDetails instances. @@ -43,6 +45,7 @@ public abstract class AbstractActivityListingAdapter extends ArrayAdapter private final Context context; private final List items; + private final int SESSION_SUMMARY = ActivitySession.SESSION_SUMMARY; private int backgroundColor = 0; private int alternateColor = 0; private boolean zebraStripes = true; @@ -67,15 +70,27 @@ public abstract class AbstractActivityListingAdapter extends ArrayAdapter return typedValue.data; } + @Override public View getView(int position, View view, ViewGroup parent) { T item = getItem(position); - view = null; //this is ugly (probably we get no recycling), but it is required to keep the layout nice. We have only few items, so this should be OK. - if (view == null) { - LayoutInflater inflater = (LayoutInflater) context - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - view = inflater.inflate(R.layout.activity_list_item, parent, false); + + if (isSummary(item)) { + view = fill_dashboard(item, position, view, parent, context); + } else { + view = fill_item(item, position, view, parent); } + + return view; + + + } + + private View fill_item(T item, int position, View view, ViewGroup parent) { + view = null; + LayoutInflater inflater = (LayoutInflater) context + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = inflater.inflate(R.layout.activity_list_item, parent, false); TextView timeFrom = view.findViewById(R.id.line_layout_time_from); TextView timeTo = view.findViewById(R.id.line_layout_time_to); TextView activityName = view.findViewById(R.id.line_layout_activity_name); @@ -160,6 +175,9 @@ public abstract class AbstractActivityListingAdapter extends ArrayAdapter return view; } + + protected abstract View fill_dashboard(T item, int position, View view, ViewGroup parent, Context context); + protected abstract String getDateLabel(T item); protected abstract boolean hasGPS(T item); @@ -182,6 +200,8 @@ public abstract class AbstractActivityListingAdapter extends ArrayAdapter protected abstract String getDurationLabel(T item); + protected abstract String getSessionCountLabel(T item); + protected abstract boolean hasHR(T item); protected abstract boolean hasIntensity(T item); @@ -190,6 +210,14 @@ public abstract class AbstractActivityListingAdapter extends ArrayAdapter protected abstract boolean hasSteps(T item); + protected abstract boolean hasTotalSteps(T item); + + protected abstract boolean isSummary(T item); + + protected abstract boolean isEmptySummary(T item); + + protected abstract String getStepTotalLabel(T item); + public void setZebraStripes(boolean enable) { zebraStripes = enable; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/ActivitySummariesAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/ActivitySummariesAdapter.java index 2562a95b5..ed37e98b1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/ActivitySummariesAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/ActivitySummariesAdapter.java @@ -18,6 +18,8 @@ package nodomain.freeyourgadget.gadgetbridge.adapter; import android.content.Context; import android.text.format.DateUtils; +import android.view.View; +import android.view.ViewGroup; import android.widget.Toast; import java.util.Calendar; @@ -134,6 +136,11 @@ public class ActivitySummariesAdapter extends AbstractActivityListingAdapter + + + + + diff --git a/app/src/main/res/drawable/ic_filter_none.xml b/app/src/main/res/drawable/ic_filter_none.xml new file mode 100644 index 000000000..050d129b3 --- /dev/null +++ b/app/src/main/res/drawable/ic_filter_none.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_functions.xml b/app/src/main/res/drawable/ic_functions.xml new file mode 100644 index 000000000..088ba5f2f --- /dev/null +++ b/app/src/main/res/drawable/ic_functions.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/ic_intensity.xml b/app/src/main/res/drawable/ic_intensity.xml index 7063d2bc0..70449795f 100644 --- a/app/src/main/res/drawable/ic_intensity.xml +++ b/app/src/main/res/drawable/ic_intensity.xml @@ -1,15 +1,10 @@ - + android:fillColor="@android:color/white" + android:pathData="M11.57,13.16c-1.36,0.28 -2.17,1.16 -2.17,2.41 0,1.34 1.11,2.42 2.49,2.42 2.05,0 3.71,-1.66 3.71,-3.71 0,-1.07 -0.15,-2.12 -0.46,-3.12 -0.79,1.07 -2.2,1.72 -3.57,2zM13.5,0.67s0.74,2.65 0.74,4.8c0,2.06 -1.35,3.73 -3.41,3.73 -2.07,0 -3.63,-1.67 -3.63,-3.73l0.03,-0.36C5.21,7.51 4,10.62 4,14c0,4.42 3.58,8 8,8s8,-3.58 8,-8C20,8.61 17.41,3.8 13.5,0.67zM12,20c-3.31,0 -6,-2.69 -6,-6 0,-1.53 0.3,-3.04 0.86,-4.43 1.01,1.01 2.41,1.63 3.97,1.63 2.66,0 4.75,-1.83 5.28,-4.43C17.34,8.97 18,11.44 18,14c0,3.31 -2.69,6 -6,6z" /> diff --git a/app/src/main/res/drawable/ic_intensity_hollow.xml b/app/src/main/res/drawable/ic_intensity_hollow.xml new file mode 100644 index 000000000..b39e3ceb2 --- /dev/null +++ b/app/src/main/res/drawable/ic_intensity_hollow.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_shoe_print.xml b/app/src/main/res/drawable/ic_shoe_print.xml new file mode 100644 index 000000000..67ed513d8 --- /dev/null +++ b/app/src/main/res/drawable/ic_shoe_print.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_shoe_prints_many.xml b/app/src/main/res/drawable/ic_shoe_prints_many.xml new file mode 100644 index 000000000..b0b68a7bb --- /dev/null +++ b/app/src/main/res/drawable/ic_shoe_prints_many.xml @@ -0,0 +1,15 @@ + + + + diff --git a/app/src/main/res/layout/activity_list_dashboard_item.xml b/app/src/main/res/layout/activity_list_dashboard_item.xml new file mode 100644 index 000000000..188a546f3 --- /dev/null +++ b/app/src/main/res/layout/activity_list_dashboard_item.xml @@ -0,0 +1,379 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_list_item.xml b/app/src/main/res/layout/activity_list_item.xml index 71c21b1f5..5ba85a6bc 100644 --- a/app/src/main/res/layout/activity_list_item.xml +++ b/app/src/main/res/layout/activity_list_item.xml @@ -134,7 +134,7 @@ android:layout_height="19dp" android:layout_gravity="bottom|start" android:contentDescription="@string/candidate_item_device_image" - app:srcCompat="@drawable/ic_shoe" /> + app:srcCompat="@drawable/ic_shoe_print" /> - + + + diff --git a/app/src/main/res/layout/intensity_total_widget.xml b/app/src/main/res/layout/intensity_total_widget.xml index 9369d17ac..42ba95e05 100644 --- a/app/src/main/res/layout/intensity_total_widget.xml +++ b/app/src/main/res/layout/intensity_total_widget.xml @@ -14,14 +14,13 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - app:srcCompat="@drawable/ic_activity_unknown" /> + app:srcCompat="@drawable/ic_intensity_hollow" /> Steps per week Activity Activity list + Active steps + Distance + Active time + Movement\nIntensity + Activities Lack of sleep: %1$s Overslept: %1$s