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:
commit
7ecb5543f7
83
app/src/main/assets/activity.svg
Normal file
83
app/src/main/assets/activity.svg
Normal 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 |
85
app/src/main/assets/ic_heartrate.svg
Normal file
85
app/src/main/assets/ic_heartrate.svg
Normal 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 |
94
app/src/main/assets/ic_intensity.svg
Normal file
94
app/src/main/assets/ic_intensity.svg
Normal 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 |
74
app/src/main/assets/ic_shoe.svg
Normal file
74
app/src/main/assets/ic_shoe.svg
Normal 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 |
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -46,4 +46,6 @@ public interface ChartsHost {
|
||||
void setDateInfo(String dateInfo);
|
||||
|
||||
ViewGroup getDateBar();
|
||||
|
||||
void enableSwipeRefresh(boolean enable);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
15
app/src/main/res/drawable/ic_activity_unknown_small.xml
Normal file
15
app/src/main/res/drawable/ic_activity_unknown_small.xml
Normal 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>
|
10
app/src/main/res/drawable/ic_heart.xml
Normal file
10
app/src/main/res/drawable/ic_heart.xml
Normal 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>
|
14
app/src/main/res/drawable/ic_heartrate.xml
Normal file
14
app/src/main/res/drawable/ic_heartrate.xml
Normal 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>
|
15
app/src/main/res/drawable/ic_intensity.xml
Normal file
15
app/src/main/res/drawable/ic_intensity.xml
Normal 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>
|
11
app/src/main/res/drawable/ic_place.xml
Normal file
11
app/src/main/res/drawable/ic_place.xml
Normal 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>
|
10
app/src/main/res/drawable/ic_shoe.xml
Normal file
10
app/src/main/res/drawable/ic_shoe.xml
Normal 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>
|
11
app/src/main/res/drawable/ic_show_chart.xml
Normal file
11
app/src/main/res/drawable/ic_show_chart.xml
Normal 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>
|
231
app/src/main/res/layout/activity_list_item.xml
Normal file
231
app/src/main/res/layout/activity_list_item.xml
Normal 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>
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user