1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-12-29 03:55:49 +01:00

Merge branch 'master' of https://codeberg.org/Freeyourgadget/Gadgetbridge into fossil-hybrid-new-firmware

This commit is contained in:
Daniel Dakhno 2020-10-14 02:02:00 +02:00
commit 7ecb5543f7
21 changed files with 975 additions and 91 deletions

View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="22.620001"
height="22.620001"
tint="#7E7E7E"
viewportWidth="25"
viewportHeight="25"
version="1.1"
id="svg1967"
sodipodi:docname="activity.svg"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<metadata
id="metadata1973">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs1971" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1042"
id="namedview1969"
showgrid="false"
showguides="true"
inkscape:guide-bbox="true"
inkscape:zoom="20.08"
inkscape:cx="22.086921"
inkscape:cy="16.68033"
inkscape:window-x="0"
inkscape:window-y="38"
inkscape:window-maximized="1"
inkscape:current-layer="svg1967"
inkscape:document-rotation="0">
<sodipodi:guide
position="22.62,162.31"
orientation="0,-1"
id="guide2551" />
<sodipodi:guide
position="11.17728,160.50592"
orientation="1,0"
id="guide2553" />
</sodipodi:namedview>
<g
id="g5608"
transform="matrix(0.6107681,0,0,0.6107681,3.9807832,3.9807832)"
style="stroke-width:1.63728">
<path
fill="#000000"
d="m 12,18 a 6,6 0 0 1 -6,-6 6,6 0 0 1 6,-6 6,6 0 0 1 6,6 6,6 0 0 1 -6,6 M 20,15.31 23.31,12 20,8.69 V 4 H 15.31 L 12,0.69 8.69,4 H 4 V 8.69 L 0.69,12 4,15.31 V 20 H 8.69 L 12,23.31 15.31,20 H 20 Z"
id="path1965"
style="stroke-width:2.82858" />
<path
fill="currentColor"
d="m 4.4762317,12.388718 h 3.561751 l 2.0837443,-5.0286723 1.932068,5.4893343 1.823648,-2.726785 h 6.48905 l -0.488068,1.38717 H 14.61543 l -2.938854,4.509156 -1.60202,-4.54569 -0.9505983,2.403348 -3.506487,-4.01e-4 z"
id="path2361"
sodipodi:nodetypes="ccccccccccccc"
style="stroke-width:1.63728"
inkscape:transform-center-x="1.0957646"
inkscape:transform-center-y="-1.4792654" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
height="24"
viewBox="0 0 24 24"
width="24"
version="1.1"
id="svg6"
sodipodi:docname="ic_heartrate.svg"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1042"
id="namedview8"
showgrid="false"
showguides="true"
inkscape:guide-bbox="true"
inkscape:zoom="22.185475"
inkscape:cx="15.252703"
inkscape:cy="16.702097"
inkscape:window-x="0"
inkscape:window-y="38"
inkscape:window-maximized="1"
inkscape:current-layer="svg6">
<sodipodi:guide
position="13.81042,14.118536"
orientation="0,-1"
id="guide843" />
<sodipodi:guide
position="12.237736,12.731366"
orientation="0,-1"
id="guide845" />
<sodipodi:guide
position="10.840426,11.852413"
orientation="0,-1"
id="guide847" />
<sodipodi:guide
position="10.569979,10.364953"
orientation="0,-1"
id="guide849" />
</sodipodi:namedview>
<path
d="M0 0h24v24H0V0z"
fill="none"
id="path2" />
<path
d="M16.5 3c-1.74 0-3.41.81-4.5 2.09C10.91 3.81 9.24 3 7.5 3 4.42 3 2 5.42 2 8.5c0 3.78 3.4 6.86 8.55 11.54L12 21.35l1.45-1.32C18.6 15.36 22 12.28 22 8.5 22 5.42 19.58 3 16.5 3zm-4.4 15.55l-.1.1-.1-.1C7.14 14.24 4 11.39 4 8.5 4 6.5 5.5 5 7.5 5c1.54 0 3.04.99 3.57 2.36h1.87C13.46 5.99 14.96 5 16.5 5c2 0 3.5 1.5 3.5 3.5 0 2.89-3.14 5.74-7.9 10.05z"
id="path4" />
<path
fill="currentColor"
d="m 4.4092095,12.147587 3.5617512,0 2.0837433,-5.0286723 1.932068,5.4893343 1.823648,-2.726785 h 6.48905 l -0.488067,1.38717 -5.262995,0 -2.938855,4.509156 -1.60202,-4.54569 -0.9505981,2.403348 -3.5064865,-4.01e-4 z"
id="path2361"
sodipodi:nodetypes="ccccccccccccc"
style="stroke-width:1"
inkscape:transform-center-x="1.0957646"
inkscape:transform-center-y="-1.4792654" />
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
height="20.465673"
viewBox="0 0 12.461885 20.465673"
width="12.461885"
version="1.1"
id="svg6"
sodipodi:docname="ic_intensity2.svg"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<g
id="g852"
transform="matrix(1.2633472,0,0,1.2633472,-7.0842384,-0.67734805)"
style="stroke-width:0.791548">
<path
fill="currentColor"
d="M 5.607515,11.686925 H 8.1252115 L 9.465446,6.8235586 10.96387,12.147587 12.471175,3.5240992 15.471696,11.686925 15.378398,14.515508 12.813693,7.3561261 11.311914,16.735717 9.4890299,10.441173 8.8928532,13.174786 6.4142216,13.174385 Z"
id="path2361"
sodipodi:nodetypes="ccccccccccccc"
style="stroke-width:0.791543"
inkscape:transform-center-x="0.77456355"
inkscape:transform-center-y="-1.4792654" />
<path
d="M 9.1454321,2.0091519 C 9.5324975,1.9556103 9.8035425,1.5818286 9.7477555,1.1785257 9.6919675,0.77522288 9.3296334,0.48905438 8.942568,0.54259588 8.5555035,0.59613728 8.2844579,0.96991921 8.3402456,1.3732221 8.3960336,1.776525 8.7583676,2.0626932 9.1454321,2.0091519 Z M 8.7316404,7.2832811 8.8161724,5.6413122 10.05796,5.9645671 10.13763,7.1563547 10.958841,7.1254797 10.779661,5.3602183 9.3400311,4.9714687 9.3990101,3.8423473 c 0.5335143,0.4866822 1.2879859,0.755973 2.0621139,0.64889 L 11.359694,3.7579588 C 10.691129,3.8504397 10.077409,3.5616792 9.7249045,3.087324 L 9.291882,2.5493755 C 9.1207016,2.3488618 8.8892885,2.2314106 8.6429739,2.2654826 8.537411,2.2800847 8.4721073,2.3264834 8.3665441,2.3410857 L 6.6483576,3.4007966 6.8867229,5.124 7.5904774,5.0266518 7.4180429,3.7800791 8.0159208,3.4358185 7.869675,6.8781207 6.7695976,8.2657413 7.1039661,9.0398223 Z"
id="path4"
style="stroke-width:0.791547"
sodipodi:nodetypes="sssssccccccccccccscccccccccc" />
</g>
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1042"
id="namedview8"
showgrid="false"
showguides="true"
inkscape:guide-bbox="true"
inkscape:zoom="22.185475"
inkscape:cx="7.4963196"
inkscape:cy="6.3791957"
inkscape:window-x="0"
inkscape:window-y="38"
inkscape:window-maximized="1"
inkscape:current-layer="svg6"
inkscape:document-rotation="0"
inkscape:snap-others="false">
<sodipodi:guide
position="13.511677,10.539359"
orientation="0,-1"
id="guide843" />
<sodipodi:guide
position="6.4686785,10.964203"
orientation="0,-1"
id="guide845" />
<sodipodi:guide
position="5.0713686,10.08525"
orientation="0,-1"
id="guide847" />
<sodipodi:guide
position="4.8009216,8.5977896"
orientation="0,-1"
id="guide849" />
</sodipodi:namedview>
<path
d="M -5.7690573,-1.7671634 H 18.230943 V 22.232837 H -5.7690573 Z"
fill="none"
id="path2" />
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
enable-background="new 0 0 24 24"
height="24"
viewBox="0 0 24 24"
width="24"
version="1.1"
id="svg1685"
sodipodi:docname="ic_shoe.svg"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<metadata
id="metadata1691">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs1689" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1042"
id="namedview1687"
showgrid="false"
showguides="true"
inkscape:guide-bbox="true"
inkscape:zoom="22.185475"
inkscape:cx="24.454686"
inkscape:cy="8.5363032"
inkscape:window-x="0"
inkscape:window-y="38"
inkscape:window-maximized="1"
inkscape:current-layer="g1683">
<sodipodi:guide
position="6.9189413,13.542708"
orientation="1,0"
id="guide2254" />
<sodipodi:guide
position="7.4147612,14.252632"
orientation="0,-1"
id="guide2256" />
</sodipodi:namedview>
<g
id="g1683">
<rect
fill="none"
height="24"
width="24"
id="rect1679" />
<path
d="M 18.51,15.68 17.1,14.27 21.58,9.79 23,11.2 Z M 14.185167,13.058927 18.05,9.08 13.8,4.82 9.9251666,8.7989268 l -1.42,-1.42 L 13.8,2 v 0 l 7.08,7.08 -5.274833,5.388927 z M 15.24,15.26 13.83,16.67 v 0 L 10.5,20 H 1 V 17.37 C 1,16.53 1.52,15.8 2.3,15.49 2.88,15.26 3.58,14.93 4.27,14.47 l 1.38,1.38 C 5.74,15.95 5.87,16 6,16 c 0.13,0 0.26,-0.05 0.36,-0.15 0.2,-0.2 0.2,-0.51 0,-0.71 L 5.08,13.86 c 0.27,-0.24 0.53,-0.51 0.77,-0.8 l 1.27,1.27 c 0.09,0.1 0.23,0.15 0.35,0.15 0.12,0 0.25,-0.05 0.35,-0.15 0.2,-0.2 0.2,-0.51 0,-0.71 l -1.4,-1.4 C 6.61,11.88 6.76,11.5 6.87,11.1 l 1.71,1.72 c 0.09,0.1 0.23,0.15 0.35,0.15 0.12,0 0.25,-0.05 0.35,-0.15 0.19,-0.2 0.19,-0.5 0.01,-0.7 l -2.3710587,-2.372632 1.42,-1.41 z m -2.82,0 -1.67,-1.68 -3.33,3.32 C 6.64,17.68 5.37,17.68 4.59,16.89 L 4.4,16.72 3.93,16.96 C 3.64,17.1 3.34,17.23 3.04,17.35 L 3.03,18 h 6.64 z"
id="path1681"
sodipodi:nodetypes="ccccccccccccccccccccscccssscccssscccsccccccccccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -2,47 +2,73 @@ package nodomain.freeyourgadget.gadgetbridge.activities.charts;
import android.content.Context;
import java.text.DecimalFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.adapter.AbstractItemAdapter;
import nodomain.freeyourgadget.gadgetbridge.adapter.AbstractActivityListingAdapter;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
public class ActivityListingAdapter extends AbstractItemAdapter<StepAnalysis.StepSession> {
public class ActivityListingAdapter extends AbstractActivityListingAdapter<StepAnalysis.StepSession> {
public ActivityListingAdapter(Context context) {
super(context);
}
@Override
protected String getName(StepAnalysis.StepSession item) {
int activityKind = item.getActivityKind();
String activityKindLabel = ActivityKind.asString(activityKind, getContext());
Date startTime = item.getStepStart();
Date endTime = item.getStepEnd();
String fromTime = DateTimeUtils.formatTime(startTime.getHours(), startTime.getMinutes());
String toTime = DateTimeUtils.formatTime(endTime.getHours(), endTime.getMinutes());
String duration = DateTimeUtils.formatDurationHoursMinutes(endTime.getTime() - startTime.getTime(), TimeUnit.MILLISECONDS);
if (activityKind == ActivityKind.TYPE_UNKNOWN) {
return getContext().getString(R.string.chart_no_active_data);
}
return activityKindLabel + " " + duration + " (" + fromTime + " - " + toTime + ")";
protected String getTimeFrom(StepAnalysis.StepSession item) {
Date time = item.getStepStart();
return DateTimeUtils.formatTime(time.getHours(), time.getMinutes());
}
@Override
protected String getDetails(StepAnalysis.StepSession item) {
String heartRate = "";
if (item.getActivityKind() == ActivityKind.TYPE_UNKNOWN) {
return getContext().getString(R.string.chart_get_active_and_synchronize);
}
if (item.getHeartRateAverage() > 50) {
heartRate = " ❤️ " + item.getHeartRateAverage();
}
protected String getTimeTo(StepAnalysis.StepSession item) {
Date time = item.getStepEnd();
return DateTimeUtils.formatTime(time.getHours(), time.getMinutes());
}
return "👣 " + item.getSteps() + heartRate;
@Override
protected String getActivityName(StepAnalysis.StepSession item) {
return ActivityKind.asString(item.getActivityKind(), getContext());
}
@Override
protected String getStepLabel(StepAnalysis.StepSession item) {
return String.valueOf(item.getSteps());
}
@Override
protected String getDistanceLabel(StepAnalysis.StepSession item) {
DecimalFormat df = new DecimalFormat("###m");
//DecimalFormatSymbols symbols = df.getDecimalFormatSymbols();
//symbols.setGroupingSeparator(' ');
return df.format(item.getDistance());
}
@Override
protected String getHrLabel(StepAnalysis.StepSession item) {
return String.valueOf(item.getHeartRateAverage());
}
@Override
protected String getIntensityLabel(StepAnalysis.StepSession item) {
DecimalFormat df = new DecimalFormat("###.#");
return df.format(item.getIntensity());
}
@Override
protected String getDurationLabel(StepAnalysis.StepSession item) {
long duration = item.getStepEnd().getTime() - item.getStepStart().getTime();
return DateTimeUtils.formatDurationHoursMinutes(duration, TimeUnit.MILLISECONDS);
}
@Override
public Boolean hasHR(StepAnalysis.StepSession item) {
if (item.getHeartRateAverage() > 0) {
return true;
} else {
return false;
}
}
@Override

View File

@ -46,7 +46,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
public class ActivityListingChartFragment extends AbstractChartFragment {
protected static final Logger LOG = LoggerFactory.getLogger(ActivityListingChartFragment.class);
int tsDataFrom;
int tsDateFrom;
private View rootView;
private List<? extends ActivitySample> activitySamples;
private ActivityListingAdapter stepListAdapter;
@ -61,7 +61,8 @@ public class ActivityListingChartFragment extends AbstractChartFragment {
stepListAdapter = new ActivityListingAdapter(getContext());
stepsList.setAdapter(stepListAdapter);
stepsDateView = rootView.findViewById(R.id.stepsDateView);
//refresh();
refresh();
return rootView;
}
@ -76,7 +77,7 @@ public class ActivityListingChartFragment extends AbstractChartFragment {
String action = intent.getAction();
if (action.equals(ChartsHost.REFRESH)) {
// TODO: use LimitLines to visualize smart alarms?
//refresh();
refresh();
} else {
super.onReceive(context, intent);
}
@ -94,13 +95,17 @@ public class ActivityListingChartFragment extends AbstractChartFragment {
@Override
protected void updateChartsnUIThread(ChartsData chartsData) {
//top displays selected date
stepsDateView.setText(DateTimeUtils.formatDate(new Date(tsDataFrom * 1000L)));
stepsDateView.setText(DateTimeUtils.formatDate(new Date(tsDateFrom * 1000L)));
//calculate active sessions
StepAnalysis stepAnalysis = new StepAnalysis();
if (activitySamples != null) {
List<StepAnalysis.StepSession> 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
// this still provides one pull to refresh on the start, in case it is needed
}
//push to the adapter
stepListAdapter.setItems(stepSessions, true);
@ -109,12 +114,10 @@ public class ActivityListingChartFragment extends AbstractChartFragment {
@Override
protected void renderCharts() {
}
@Override
protected void setupLegend(Chart chart) {
}
@Override
@ -124,20 +127,17 @@ public class ActivityListingChartFragment extends AbstractChartFragment {
day.set(Calendar.HOUR_OF_DAY, 0); //and we set time for the start and end of the same day
day.set(Calendar.MINUTE, 0);
day.set(Calendar.SECOND, 0);
tsFrom = (int) (day.getTimeInMillis() / 1000);
tsTo = tsFrom + 24 * 60 * 60 - 1;
tsDataFrom = tsFrom;
tsDateFrom = tsFrom;
return getAllSamples(db, device, tsFrom, tsTo);
}
private List<StepAnalysis.StepSession> create_empty_record() {
//have an "Unknown Activity" in the list in case there are no active sessions
List<StepAnalysis.StepSession> result = new ArrayList<>();
int tsTo = tsDataFrom + 24 * 60 * 60 - 1;
result.add(new StepAnalysis.StepSession(new Date(tsDataFrom * 1000L), new Date(tsTo * 1000L), 0, 0, ActivityKind.TYPE_UNKNOWN));
int tsTo = tsDateFrom + 24 * 60 * 60 - 1;
result.add(new StepAnalysis.StepSession(new Date(tsDateFrom * 1000L), new Date(tsTo * 1000L), 0, 0, 0, 0, ActivityKind.TYPE_UNKNOWN));
return result;
}
}

View File

@ -308,7 +308,8 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts
return super.onOptionsItemSelected(item);
}
private void enableSwipeRefresh(boolean enable) {
@Override
public void enableSwipeRefresh(boolean enable) {
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(mGBDevice);
swipeLayout.setEnabled(enable && coordinator.allowFetchActivityData(mGBDevice));
}

View File

@ -46,4 +46,6 @@ public interface ChartsHost {
void setDateInfo(String dateInfo);
ViewGroup getDateBar();
void enableSwipeRefresh(boolean enable);
}

View File

@ -26,100 +26,137 @@ import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
public class StepAnalysis {
protected static final Logger LOG = LoggerFactory.getLogger(StepAnalysis.class);
private final int MIN_SESSION_STEPS = 100;
final double MULTIPLIER_FEMALE = 0.44; //constants to calculate steps from height
final double MULTIPLIER_OTHER = 0.45; //thes feel too small though
final double MULTIPLIER_MALE = 0.46;
private final double MIN_SESSION_INTENSITY = 0.4; //needs tuning
private double STEP_SIZE = 1;
public List<StepSession> calculateStepSessions(List<? extends ActivitySample> samples) {
List<StepSession> result = new ArrayList<>();
final int MIN_SESSION_LENGTH = 60 * GBApplication.getPrefs().getInt("chart_list_min_session_length", 5);
final int MAX_IDLE_PHASE_LENGTH = 60 * GBApplication.getPrefs().getInt("chart_list_max_idle_phase_length", 5);
final int MIN_STEPS_PER_MINUTE = GBApplication.getPrefs().getInt("chart_list_min_steps_per_minute", 40);
final int GENDER = GBApplication.getPrefs().getInt("activity_user_gender", 2);
final int HEIGHT = GBApplication.getPrefs().getInt("activity_user_height_cm", 170);
STEP_SIZE = calculate_step_size(GENDER, HEIGHT);
ActivitySample previousSample = null;
Date stepStart = null;
Date stepEnd = null;
int activeSteps = 0;
int heartRateForAverage = 0;
int heartRateToAdd = 0;
int activeSamplesForAverage = 0;
int activeSamplesToAdd = 0;
int stepsBetweenActivities = 0;
int heartRateBetweenActivities = 0;
Date sessionStart = null;
Date sessionEnd = null;
int activeSteps = 0; //steps that we count
int stepsBetweenActivePeriods = 0; //steps during time when we maybe take a rest but then restart
int durationSinceLastActiveStep = 0;
int activityKind;
for (ActivitySample sample : samples) {
if (isStep(sample)) { //TODO we could improve/extend this to other activities as well, if in database
int heartRateForAverage = 0;
int heartRateToAdd = 0;
int heartRateBetweenActivePeriods = 0;
int activeHrSamplesForAverage = 0;
int activeHrSamplesToAdd = 0;
float activeIntensity = 0;
float intensityBetweenActivePeriods = 0;
for (ActivitySample sample : samples) {
if (sample.getKind() != ActivityKind.TYPE_SLEEP) { //anything but sleep counts
if (sample.getHeartRate() != 255 && sample.getHeartRate() != -1) {
heartRateToAdd = sample.getHeartRate();
activeSamplesToAdd = 1;
activeHrSamplesToAdd = 1;
} else {
heartRateToAdd = 0;
activeSamplesToAdd = 0;
activeHrSamplesToAdd = 0;
}
if (stepStart == null) {
stepStart = getDateFromSample(sample);
if (sessionStart == null) {
sessionStart = getDateFromSample(sample);
activeSteps = sample.getSteps();
activeIntensity = (int) sample.getIntensity();
heartRateForAverage = heartRateToAdd;
activeSamplesForAverage = activeSamplesToAdd;
activeHrSamplesForAverage = activeHrSamplesToAdd;
durationSinceLastActiveStep = 0;
stepsBetweenActivities = 0;
heartRateBetweenActivities = 0;
stepsBetweenActivePeriods = 0;
heartRateBetweenActivePeriods = 0;
previousSample = null;
}
if (previousSample != null) {
int durationSinceLastSample = sample.getTimestamp() - previousSample.getTimestamp();
activeSamplesForAverage += activeSamplesToAdd;
if (sample.getSteps() > MIN_STEPS_PER_MINUTE) {
activeSteps += sample.getSteps() + stepsBetweenActivities;
heartRateForAverage += heartRateToAdd + heartRateBetweenActivities;
stepsBetweenActivities = 0;
heartRateBetweenActivities = 0;
activeHrSamplesForAverage += activeHrSamplesToAdd;
if (sample.getSteps() > MIN_STEPS_PER_MINUTE || //either some steps
(sample.getIntensity() > MIN_SESSION_INTENSITY && sample.getSteps() > 0)) { //or some intensity plus at least one step
activeSteps += sample.getSteps() + stepsBetweenActivePeriods;
activeIntensity += sample.getIntensity() + intensityBetweenActivePeriods;
heartRateForAverage += heartRateToAdd + heartRateBetweenActivePeriods;
stepsBetweenActivePeriods = 0;
heartRateBetweenActivePeriods = 0;
intensityBetweenActivePeriods = 0;
durationSinceLastActiveStep = 0;
} else {
stepsBetweenActivities += sample.getSteps();
heartRateBetweenActivities += heartRateToAdd;
} else { //short break data to remember, we will add it to the rest later, if break not too long
stepsBetweenActivePeriods += sample.getSteps();
heartRateBetweenActivePeriods += heartRateToAdd;
durationSinceLastActiveStep += durationSinceLastSample;
intensityBetweenActivePeriods += sample.getIntensity();
}
if (durationSinceLastActiveStep >= MAX_IDLE_PHASE_LENGTH) {
if (durationSinceLastActiveStep >= MAX_IDLE_PHASE_LENGTH) { //break too long, we split here
int current = sample.getTimestamp();
int starting = (int) (stepStart.getTime() / 1000);
int starting = (int) (sessionStart.getTime() / 1000);
int session_length = current - starting - durationSinceLastActiveStep;
int heartRateAverage = activeSamplesForAverage > 0 ? heartRateForAverage / activeSamplesForAverage : 0;
if (session_length >= MIN_SESSION_LENGTH) {
stepEnd = new Date((sample.getTimestamp() - durationSinceLastActiveStep) * 1000L);
activityKind = detect_activity(session_length, activeSteps, heartRateAverage);
result.add(new StepSession(stepStart, stepEnd, activeSteps, heartRateAverage, activityKind));
if (session_length >= MIN_SESSION_LENGTH) { //valid activity session
int heartRateAverage = activeHrSamplesForAverage > 0 ? heartRateForAverage / activeHrSamplesForAverage : 0;
float distance = (float) (activeSteps * STEP_SIZE);
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));
}
stepStart = null;
sessionStart = null;
}
}
previousSample = sample;
}
}
//make sure we show the last portion of the data as well in case no further activity is recorded yet
if (stepStart != null && previousSample != null) {
if (sessionStart != null && previousSample != null) {
int current = previousSample.getTimestamp();
int starting = (int) (stepStart.getTime() / 1000);
int starting = (int) (sessionStart.getTime() / 1000);
int session_length = current - starting - durationSinceLastActiveStep;
int heartRateAverage = activeSamplesForAverage > 0 ? heartRateForAverage / activeSamplesForAverage : 0;
if (session_length > MIN_SESSION_LENGTH && activeSteps > MIN_SESSION_STEPS) {
stepEnd = getDateFromSample(previousSample);
activityKind = detect_activity(session_length, activeSteps, heartRateAverage);
result.add(new StepSession(stepStart, stepEnd, activeSteps, heartRateAverage, activityKind));
if (session_length >= MIN_SESSION_LENGTH) {
int heartRateAverage = activeHrSamplesForAverage > 0 ? heartRateForAverage / activeHrSamplesForAverage : 0;
float distance = (float) (activeSteps * STEP_SIZE);
sessionEnd = getDateFromSample(previousSample);
activityKind = detect_activity_kind(session_length, activeSteps, heartRateAverage, activeIntensity);
result.add(new StepSession(sessionStart, sessionEnd, activeSteps, heartRateAverage, activeIntensity, distance, activityKind));
}
}
return result;
}
private int detect_activity(int session_length, int activeSteps, int heartRateAverage) {
private double calculate_step_size(int gender, int height) {
double multiplier = 0;
switch (gender) {
case ActivityUser.GENDER_MALE:
multiplier = MULTIPLIER_MALE;
break;
case ActivityUser.GENDER_FEMALE:
multiplier = MULTIPLIER_FEMALE;
break;
case ActivityUser.GENDER_OTHER:
multiplier = MULTIPLIER_OTHER;
break;
}
return height * multiplier / 100;
}
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));
if (spm > MIN_STEPS_PER_MINUTE_FOR_RUN) {
@ -128,7 +165,7 @@ public class StepAnalysis {
if (activeSteps > 200) {
return ActivityKind.TYPE_WALKING;
}
if (heartRateAverage > 90) {
if (heartRateAverage > 90 && intensity > 30) { //needs tuning
return ActivityKind.TYPE_EXERCISE;
}
return ActivityKind.TYPE_ACTIVITY;
@ -147,15 +184,19 @@ public class StepAnalysis {
private final Date stepEnd;
private final int steps;
private final int heartRateAverage;
private final float intensity;
private final float distance;
private final int activityKind;
StepSession(Date stepStart,
Date stepEnd,
int steps, int heartRateAverage, int activityKind) {
int steps, int heartRateAverage, float intensity, float distance, int activityKind) {
this.stepStart = stepStart;
this.stepEnd = stepEnd;
this.steps = steps;
this.heartRateAverage = heartRateAverage;
this.intensity = intensity;
this.distance = distance;
this.activityKind = activityKind;
}
@ -179,5 +220,12 @@ public class StepAnalysis {
return activityKind;
}
public float getIntensity() {
return intensity;
}
public float getDistance() {
return distance;
}
}
}

View File

@ -0,0 +1,154 @@
/* Copyright (C) 2015-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.adapter;
import android.content.Context;
import android.content.res.Resources;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.DrawableRes;
import java.util.ArrayList;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.R;
import static nodomain.freeyourgadget.gadgetbridge.activities.charts.ActivityAnalysis.LOG;
/**
* Adapter for displaying generic ItemWithDetails instances.
*/
public abstract class AbstractActivityListingAdapter<T> extends ArrayAdapter<T> {
private final Context context;
private final List<T> items;
private int backgroundColor = 0;
private int alternateColor = 0;
public AbstractActivityListingAdapter(Context context) {
this(context, new ArrayList<T>());
}
public AbstractActivityListingAdapter(Context context, List<T> items) {
super(context, 0, items);
this.context = context;
this.items = items;
alternateColor = getAlternateColor(context);
}
public static int getAlternateColor(Context context) {
TypedValue typedValue = new TypedValue();
Resources.Theme theme = context.getTheme();
theme.resolveAttribute(R.attr.alternate_row_background, typedValue, true);
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);
}
TextView timeFrom = view.findViewById(R.id.line_layout_timeFrom);
TextView timeTo = view.findViewById(R.id.line_layout_timeTo);
TextView activityName = view.findViewById(R.id.line_layout_activityName);
TextView stepLabel = view.findViewById(R.id.line_layout_step_label);
TextView distanceLabel = view.findViewById(R.id.line_layout_distance_label);
TextView hrLabel = view.findViewById(R.id.line_layout_hr_label);
TextView intensityLabel = view.findViewById(R.id.line_layout_intensity_label);
TextView durationLabel = view.findViewById(R.id.line_layout_duration_labe);
LinearLayout hrLayout = view.findViewById(R.id.line_layout_hr);
LinearLayout stepLayout = view.findViewById(R.id.line_layout_step);
LinearLayout intensityLayout = view.findViewById(R.id.line_layout_intensity);
RelativeLayout parentLayout = view.findViewById(R.id.list_item_parent_layout);
ImageView activityIcon = view.findViewById(R.id.line_layout_activityIcon);
timeFrom.setText(getTimeFrom(item));
timeTo.setText(getTimeTo(item));
activityName.setText(getActivityName(item));
stepLabel.setText(getStepLabel(item));
distanceLabel.setText(getDistanceLabel(item));
hrLabel.setText(getHrLabel(item));
intensityLabel.setText(getIntensityLabel(item));
durationLabel.setText(getDurationLabel(item));
if (!hasHR(item)) {
hrLayout.setVisibility(View.GONE);
} else {
hrLayout.setVisibility(View.VISIBLE);
}
activityIcon.setImageResource(getIcon(item));
if (position % 2 == 0) {parentLayout.setBackgroundColor(alternateColor);}
return view;
}
protected abstract String getTimeFrom(T item);
protected abstract String getTimeTo(T item);
protected abstract String getActivityName(T item);
protected abstract String getStepLabel(T item);
protected abstract String getDistanceLabel(T item);
protected abstract String getHrLabel(T item);
protected abstract String getIntensityLabel(T item);
protected abstract String getDurationLabel(T item);
protected abstract Boolean hasHR(T item);
@DrawableRes
protected abstract int getIcon(T item);
public List<T> getItems() {
return items;
}
public void loadItems() {
}
public void setItems(List<T> items, boolean notify) {
this.items.clear();
this.items.addAll(items);
if (notify) {
notifyDataSetChanged();
}
}
}

View File

@ -207,7 +207,7 @@ public class ActivityKind {
case TYPE_ACTIVITY: // fall through
case TYPE_UNKNOWN: // fall through
default:
return R.drawable.ic_activity_unknown;
return R.drawable.ic_activity_unknown_small;
}
}
}

View File

@ -4,7 +4,7 @@
android:tint="#7E7E7E"
android:viewportWidth="25"
android:viewportHeight="25">
<path
android:fillColor="#000000"
android:pathData="M12 18a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-2.69L23.31 12 20 8.69V4h-4.69L12 0.69 8.69 4H4v4.69L0.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69z" />
</vector>
<path
android:fillColor="#000000"
android:pathData="M12 18a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-2.69L23.31 12 20 8.69V4h-4.69L12 0.69 8.69 4H4v4.69L0.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69z" />
</vector>

View File

@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="22.62dp"
android:height="22.62dp"
android:tint="#7E7E7E"
android:viewportWidth="22.62"
android:viewportHeight="22.62">
<path
android:pathData="m11.31,14.9746a3.6646,3.6646 0,0 1,-3.6646 -3.6646,3.6646 3.6646,0 0,1 3.6646,-3.6646 3.6646,3.6646 0,0 1,3.6646 3.6646,3.6646 3.6646,0 0,1 -3.6646,3.6646M16.1961,13.3316 L18.2178,11.31 16.1961,9.2884L16.1961,6.4239L13.3316,6.4239L11.31,4.4022 9.2884,6.4239L6.4239,6.4239L6.4239,9.2884L4.4022,11.31 6.4239,13.3316L6.4239,16.1961L9.2884,16.1961L11.31,18.2178 13.3316,16.1961L16.1961,16.1961Z"
android:strokeWidth="2.82858"
android:fillColor="#000000"/>
<path
android:pathData="m6.7147,11.5474l2.1754,0l1.2727,-3.0714 1.18,3.3527 1.1138,-1.6654l3.9633,0l-0.2981,0.8472L12.9074,11.0106l-1.795,2.754 -0.9785,-2.7764 -0.5806,1.4679 -2.1417,-0.0002z"
android:strokeWidth="1.63728"
android:fillColor="#000000"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:tint="#7E7E7E"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M16.5,3c-1.74,0 -3.41,0.81 -4.5,2.09C10.91,3.81 9.24,3 7.5,3 4.42,3 2,5.42 2,8.5c0,3.78 3.4,6.86 8.55,11.54L12,21.35l1.45,-1.32C18.6,15.36 22,12.28 22,8.5 22,5.42 19.58,3 16.5,3zM12.1,18.55l-0.1,0.1 -0.1,-0.1C7.14,14.24 4,11.39 4,8.5 4,6.5 5.5,5 7.5,5c1.54,0 3.04,0.99 3.57,2.36h1.87C13.46,5.99 14.96,5 16.5,5c2,0 3.5,1.5 3.5,3.5 0,2.89 -3.14,5.74 -7.9,10.05z"/>
</vector>

View File

@ -0,0 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:tint="#7E7E7E"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M16.5,3c-1.74,0 -3.41,0.81 -4.5,2.09C10.91,3.81 9.24,3 7.5,3 4.42,3 2,5.42 2,8.5c0,3.78 3.4,6.86 8.55,11.54L12,21.35l1.45,-1.32C18.6,15.36 22,12.28 22,8.5 22,5.42 19.58,3 16.5,3zM12.1,18.55l-0.1,0.1 -0.1,-0.1C7.14,14.24 4,11.39 4,8.5 4,6.5 5.5,5 7.5,5c1.54,0 3.04,0.99 3.57,2.36h1.87C13.46,5.99 14.96,5 16.5,5c2,0 3.5,1.5 3.5,3.5 0,2.89 -3.14,5.74 -7.9,10.05z"/>
<path
android:pathData="m4.4092,12.1476 l3.5618,0 2.0837,-5.0287 1.9321,5.4893 1.8236,-2.7268h6.489l-0.4881,1.3872 -5.263,0 -2.9389,4.5092 -1.602,-4.5457 -0.9506,2.4033 -3.5065,-0.0004z"
android:strokeWidth="1"
android:fillColor="#FF000000"/>
</vector>

View File

@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="12.461885dp"
android:height="20.465673dp"
android:tint="#7E7E7E"
android:viewportWidth="12.461885"
android:viewportHeight="20.465673">
<path
android:pathData="M-0,14.0873L3.1807,14.0873L4.8739,7.9432 6.7669,14.6693 8.6712,3.7748 12.4619,14.0873 12.344,17.6608 9.1039,8.616 7.2066,20.4657 4.9037,12.5135 4.1505,15.967 1.0192,15.9665Z"
android:strokeWidth="0.791543"
android:fillColor="#FF000000"/>
<path
android:fillColor="#FF000000"
android:pathData="M4.4696,1.8609C4.9586,1.7933 5.301,1.3211 5.2306,0.8115 5.1601,0.302 4.7023,-0.0595 4.2133,0.0081 3.7243,0.0758 3.3819,0.548 3.4524,1.0575 3.5229,1.567 3.9806,1.9285 4.4696,1.8609ZM3.9469,8.524 L4.0536,6.4496 5.6225,6.858 5.7231,8.3636 6.7606,8.3246 6.5342,6.0945 4.7155,5.6033 4.79,4.1769c0.674,0.6148 1.6272,0.9551 2.6052,0.8198L7.267,4.0703C6.4224,4.1871 5.647,3.8223 5.2017,3.223L4.6546,2.5434C4.4384,2.2901 4.146,2.1417 3.8348,2.1847 3.7015,2.2032 3.619,2.2618 3.4856,2.2803L1.3149,3.619 1.6161,5.796 2.5052,5.6731 2.2873,4.0982 3.0427,3.6633 2.8579,8.0121 1.4681,9.7652 1.8905,10.7431Z"
android:strokeWidth="0.791547"/>
</vector>

View File

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#7E7E7E"
>
<path
android:fillColor="@android:color/white"
android:pathData="M12,12c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM18,10.2C18,6.57 15.35,4 12,4s-6,2.57 -6,6.2c0,2.34 1.95,5.44 6,9.14 4.05,-3.7 6,-6.8 6,-9.14zM12,2c4.2,0 8,3.22 8,8.2 0,3.32 -2.67,7.25 -8,11.8 -5.33,-4.55 -8,-8.48 -8,-11.8C4,5.22 7.8,2 12,2z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:tint="#7E7E7E"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M18.51,15.68 L17.1,14.27 21.58,9.79 23,11.2ZM14.1852,13.0589 L18.05,9.08 13.8,4.82 9.9252,8.7989l-1.42,-1.42L13.8,2v0l7.08,7.08 -5.2748,5.3889zM15.24,15.26 L13.83,16.67v0L10.5,20L1,20L1,17.37C1,16.53 1.52,15.8 2.3,15.49 2.88,15.26 3.58,14.93 4.27,14.47l1.38,1.38C5.74,15.95 5.87,16 6,16c0.13,0 0.26,-0.05 0.36,-0.15 0.2,-0.2 0.2,-0.51 0,-0.71L5.08,13.86c0.27,-0.24 0.53,-0.51 0.77,-0.8l1.27,1.27c0.09,0.1 0.23,0.15 0.35,0.15 0.12,0 0.25,-0.05 0.35,-0.15 0.2,-0.2 0.2,-0.51 0,-0.71l-1.4,-1.4C6.61,11.88 6.76,11.5 6.87,11.1l1.71,1.72c0.09,0.1 0.23,0.15 0.35,0.15 0.12,0 0.25,-0.05 0.35,-0.15 0.19,-0.2 0.19,-0.5 0.01,-0.7l-2.3711,-2.3726 1.42,-1.41zM12.42,15.26 L10.75,13.58 7.42,16.9C6.64,17.68 5.37,17.68 4.59,16.89L4.4,16.72 3.93,16.96C3.64,17.1 3.34,17.23 3.04,17.35L3.03,18h6.64z"/>
</vector>

View File

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal"
>
<path
android:fillColor="@android:color/white"
android:pathData="M3.5,18.49l6,-6.01 4,4L22,6.92l-1.41,-1.41 -7.09,7.97 -4,-4L2,16.99l1.5,1.5z"/>
</vector>

View File

@ -0,0 +1,231 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/list_item_parent_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/activatedBackgroundIndicator">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="5dp"
android:layout_marginBottom="5dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/line_layout_timeFrom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:maxLines="1"
android:scrollHorizontally="false"
android:text="14:30" />
<TextView
android:id="@+id/line_layout_timeTo"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="end"
android:layout_weight="1"
android:gravity="bottom"
android:maxLines="1"
android:scrollHorizontally="false"
android:text="16:30" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/line_layout_activityIcon"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:contentDescription="@string/candidate_item_device_image"
android:padding="8dp"
app:srcCompat="@drawable/ic_activity_running" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/line_layout_activityName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:scrollHorizontally="false"
android:text="Running"
android:textStyle="bold" />
<TextView
android:id="@+id/line_layout_duration_labe"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:layout_weight="1"
android:gravity="end"
android:maxLines="1"
android:scrollHorizontally="false"
android:text="25min" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/line_layout_step"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:gravity="start"
android:orientation="horizontal">
<ImageView
android:id="@+id/line_layout_step_icon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="start"
android:contentDescription="@string/candidate_item_device_image"
app:srcCompat="@drawable/ic_shoe" />
<TextView
android:id="@+id/line_layout_step_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:layout_weight="1"
android:gravity="start"
android:maxLines="1"
android:scrollHorizontally="false"
android:text="2000" />
</LinearLayout>
<LinearLayout
android:id="@+id/line_layout_intensity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:gravity="start"
android:orientation="horizontal">
<ImageView
android:id="@+id/line_layout_intensity_icon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="start"
android:contentDescription="@string/candidate_item_device_image"
app:srcCompat="@drawable/ic_intensity" />
<TextView
android:id="@+id/line_layout_intensity_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:layout_weight="1"
android:gravity="start"
android:maxLines="1"
android:scrollHorizontally="false"
android:text="200" />
</LinearLayout>
<LinearLayout
android:id="@+id/line_layout_distance"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:gravity="start"
android:orientation="horizontal">
<ImageView
android:id="@+id/line_layout_distance_icon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="start"
android:contentDescription="@string/candidate_item_device_image"
app:srcCompat="@drawable/ic_place" />
<TextView
android:id="@+id/line_layout_distance_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:layout_weight="1"
android:gravity="start"
android:maxLines="1"
android:scrollHorizontally="false"
android:text="2.5km" />
</LinearLayout>
<LinearLayout
android:id="@+id/line_layout_hr"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:gravity="start"
android:orientation="horizontal">
<ImageView
android:id="@+id/line_layout_hr_icon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="start"
android:contentDescription="@string/candidate_item_device_image"
app:srcCompat="@drawable/ic_heartrate" />
<TextView
android:id="@+id/line_layout_hr_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:layout_weight="1"
android:gravity="start"
android:maxLines="1"
android:scrollHorizontally="false"
android:text="2.5km" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</RelativeLayout>

View File

@ -73,7 +73,7 @@
android:title="@string/activity_prefs_chart_max_idle_phase_length" />
<EditTextPreference
android:defaultValue="40"
android:defaultValue="20"
android:inputType="number"
android:key="chart_list_min_steps_per_minute"
android:maxLength="3"