From fa714a97158886d3d15021af37cf318339fd660a Mon Sep 17 00:00:00 2001 From: "Chris J. Karr" Date: Thu, 11 May 2017 18:06:42 -0500 Subject: [PATCH] Implemented ambient light sensor. * Implemented accelerometer sensor. * Added lock functionality to Data Stream view. * Updated Foreground App generator to only count when screen is active. --- res/drawable-hdpi/ic_ambient_light.png | Bin 0 -> 1424 bytes res/drawable-hdpi/ic_pdk_accelerometer.png | Bin 0 -> 1721 bytes res/drawable-hdpi/ic_pdk_action_lock.png | Bin 0 -> 1338 bytes res/drawable-hdpi/ic_pdk_action_unlock.png | Bin 0 -> 1350 bytes res/drawable-mdpi/ic_ambient_light.png | Bin 0 -> 1271 bytes res/drawable-mdpi/ic_pdk_accelerometer.png | Bin 0 -> 1404 bytes res/drawable-mdpi/ic_pdk_action_lock.png | Bin 0 -> 1244 bytes res/drawable-mdpi/ic_pdk_action_unlock.png | Bin 0 -> 1248 bytes res/drawable-xhdpi/ic_ambient_light.png | Bin 0 -> 1569 bytes res/drawable-xhdpi/ic_pdk_accelerometer.png | Bin 0 -> 1997 bytes res/drawable-xhdpi/ic_pdk_action_lock.png | Bin 0 -> 1461 bytes res/drawable-xhdpi/ic_pdk_action_unlock.png | Bin 0 -> 1468 bytes res/drawable-xxhdpi/ic_ambient_light.png | Bin 0 -> 1880 bytes res/drawable-xxhdpi/ic_pdk_accelerometer.png | Bin 0 -> 2681 bytes res/drawable-xxhdpi/ic_pdk_action_lock.png | Bin 0 -> 1754 bytes res/drawable-xxhdpi/ic_pdk_action_unlock.png | Bin 0 -> 1784 bytes res/drawable-xxxhdpi/ic_ambient_light.png | Bin 0 -> 2016 bytes res/drawable-xxxhdpi/ic_pdk_accelerometer.png | Bin 0 -> 3185 bytes res/drawable-xxxhdpi/ic_pdk_action_lock.png | Bin 0 -> 1950 bytes res/drawable-xxxhdpi/ic_pdk_action_unlock.png | Bin 0 -> 1945 bytes .../card_generator_sensors_accelerometer.xml | 63 ++ .../card_generator_sensors_ambient_light.xml | 63 ++ res/menu/activity_data_stream.xml | 8 + res/values/databases.xml | 8 +- res/values/generators.xml | 21 +- res/values/strings.xml | 2 + .../activities/DataStreamActivity.java | 52 ++ .../generators/DataPointsAdapter.java | 92 ++- .../generators/device/Battery.java | 5 - .../device/ForegroundApplication.java | 34 +- .../generators/sensors/Accelerometer.java | 652 +++++++++++++++++- .../generators/sensors/AmbientLight.java | 279 ++++++-- .../generators/sensors/SensorGenerator.java | 74 ++ 33 files changed, 1244 insertions(+), 109 deletions(-) create mode 100755 res/drawable-hdpi/ic_ambient_light.png create mode 100755 res/drawable-hdpi/ic_pdk_accelerometer.png create mode 100755 res/drawable-hdpi/ic_pdk_action_lock.png create mode 100755 res/drawable-hdpi/ic_pdk_action_unlock.png create mode 100755 res/drawable-mdpi/ic_ambient_light.png create mode 100755 res/drawable-mdpi/ic_pdk_accelerometer.png create mode 100755 res/drawable-mdpi/ic_pdk_action_lock.png create mode 100755 res/drawable-mdpi/ic_pdk_action_unlock.png create mode 100755 res/drawable-xhdpi/ic_ambient_light.png create mode 100755 res/drawable-xhdpi/ic_pdk_accelerometer.png create mode 100755 res/drawable-xhdpi/ic_pdk_action_lock.png create mode 100755 res/drawable-xhdpi/ic_pdk_action_unlock.png create mode 100755 res/drawable-xxhdpi/ic_ambient_light.png create mode 100755 res/drawable-xxhdpi/ic_pdk_accelerometer.png create mode 100755 res/drawable-xxhdpi/ic_pdk_action_lock.png create mode 100755 res/drawable-xxhdpi/ic_pdk_action_unlock.png create mode 100755 res/drawable-xxxhdpi/ic_ambient_light.png create mode 100755 res/drawable-xxxhdpi/ic_pdk_accelerometer.png create mode 100755 res/drawable-xxxhdpi/ic_pdk_action_lock.png create mode 100755 res/drawable-xxxhdpi/ic_pdk_action_unlock.png create mode 100755 res/layout/card_generator_sensors_accelerometer.xml create mode 100755 res/layout/card_generator_sensors_ambient_light.xml create mode 100755 res/menu/activity_data_stream.xml create mode 100755 src/com/audacious_software/passive_data_kit/generators/sensors/SensorGenerator.java diff --git a/res/drawable-hdpi/ic_ambient_light.png b/res/drawable-hdpi/ic_ambient_light.png new file mode 100755 index 0000000000000000000000000000000000000000..01cd42fb19e2ed29ccf679acc743bedd90f76e74 GIT binary patch literal 1424 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 Mr>mdKI;Vst0E!ypSO5S3 literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_ambient_light.png b/res/drawable-mdpi/ic_ambient_light.png new file mode 100755 index 0000000000000000000000000000000000000000..183e05399ae0057f754d05a38211d8a298bd1c35 GIT binary patch literal 1271 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gjk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+m^Cs(B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxOgGuk*h0bFQqR!T z(!$6@N5ROz&`jUJQs2--*TB%qz|zXVPyq^*fVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8E%gnI^o@*kfhu&1EAvVcD|GXUm0>2hq!uR^WfqiV=I1GZOiWD5 zFD$Tv3bSNU;+l1ennz|zM-B0$V)JVzP|XC=H|jx7ncO3BHWAB;NpiyW)Z+ZoqGVvir744~DzI`cN=+=uFAB-e&w+(vKt_H^esM;Afr7I$DAddqG<*}2 zGxI=#nqXbNzE+-j#U+V($*G<$wn{*A^fEJ3tXvG74NT2V9gSQqO$-fP%^eNRoL!t< z4Gm1pO$>lMm|mCsATTySbngC`LrJCZ4FdpC0*k zmCW(PZMo^?6U04)Sq|P`mt4SFd~))<=c#edk0KAYB^sQopY{8J(5nJQ;Z=$k)wh0% zu753{=6ElR_1uExCk`kvUf#gfZ@~OqWZT7c4YI%POseMJeWAry;sWoA3v1>-n03z9 znR!*hYqpvTIqZopZ|!?lFOM{TAndn5{#^opULsfCgN30E;f=NyO^FeUzou_-IW4d~ zdGh`QS+SQ9``C|f;AOwCx|-ejOVZg(A8mA0Z}~smmvg{w%?07bT9(OcRUd8930)Sr z_S=KGnPTbxW9ByaF}&6@m^b~`s*cAI4Sz(X&r1H6at+)Z(vUgpQ0ZOPKgKJb_SA3I gd9AAdAi9B(!M(M}Xwu9i3s6zy>FVdQ&MBb@08q@o0RR91 literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_pdk_accelerometer.png b/res/drawable-mdpi/ic_pdk_accelerometer.png new file mode 100755 index 0000000000000000000000000000000000000000..557004955426c3704b6ae075ec1046c91b11782a GIT binary patch literal 1404 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gjk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+m^Cs(B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxOgGuk*h0bFQqR!T z(!$6@N5ROz&`jUJQs2--*TB%qz|zXVPyq^*fVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8E%gnI^o@*kfhu&1EAvVcD|GXUm0>2hq!uR^WfqiV=I1GZOiWD5 zFD$Tv3bSNU;+l1ennz|zM-B0$V)JVzP|XC=H|jx7ncO3BHWAB;NpiyW)Z+ZoqGVvir744~DzI`cN=+=uFAB-e&w+(vKt_H^esM;Afr7I$DAddqG<*}2 zGxI=#nqXbNzE+-j#U+V($*G<$wn{*A^fEJ3tV}E&P0gL1U5#9wTn!Cf%^eNRoL!t< z4Gm1pO$>lMm|mCsATTyX)sZwmMII{`Tz)4F|k26za$1}*x- z$|df#Z?f-C4c8RWAdgcYeQb@apUv64w(M2-^747#lhf`Q_Z~LrS>e(5qW+h)gO7@E zNQ3kP_P7UJ>W)kqEK_Igv^HQYbI`xR@J^xCf|)z$;Ixm{w{*80eIR<~RN3kWFBAG3 z%^e%=%Zl0EeZYI074vk#{a=x%kM zw3l(t#(=ky6)(21P0E=xr78PWt=IFJWq~#ki!S$RnPr~Ue&t>wHFxsl-}=YwPHgZD zV$oaD+{^XUUcmF&ixQSiIg_3=tG_z_V!f||TJ+Di48|o^j620Rk9@1~(v4c}ec{Z> zbiLol?OcCrYKJ!aOVzt?owdMAa89#{<_U|pi&(jri27wWeSdv!LFZbvbxpAbXI49Y z?~nOpKjRY1`>oa~f;C6e)*tMye}V9o4{4#L zaqFD!{Lc;3U1PlKpiM=3shse0r^)NM4kdnE<-xd8fkEz2ov6#No1b(KFnqP2{qPvm RUr=$%;OXk;vd$@?2>@#*{>}gZ literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_pdk_action_lock.png b/res/drawable-mdpi/ic_pdk_action_lock.png new file mode 100755 index 0000000000000000000000000000000000000000..53377c1e3f1a432613480fdc96fbc0bd4c5e49c7 GIT binary patch literal 1244 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gjk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+m^Cs(B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxOgGuk*h0bFQqR!T z(!$6@N5ROz&`jUJQs2--*TB%qz|zXVPyq^*fVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8E%gnI^o@*kfhu&1EAvVcD|GXUm0>2hq!uR^WfqiV=I1GZOiWD5 zFD$Tv3bSNU;+l1ennz|zM-B0$V)JVzP|XC=H|jx7ncO3BHWAB;NpiyW)Z+ZoqGVvir744~DzI`cN=+=uFAB-e&w+(vKt_H^esM;Afr7I$DAddqG<*}2 zGxI=#nqXbNzE+-j#U+V($*G<$wn{*A^fEJ3tPBl}EsfmFOpIKejSLN4%`MCz*Q}aq-dQ%X3O>yc4C5YStpv^9+MVV!(DQ-pixe8#9TV>*Q zixE!qpn6kqyTur%UVWfr^g+>!6x}c(U>X83;fWW>fhYgeJYbqH0w(Ur4S(wy7#O`g zT^vIyZf%*eU!TQMMoDqo*+N0;ZA)E1YNlM`1v zN>>@Yz3jovYoK%feBU4IJty`><%rL;|EAhH&D43%>JX& z_xy?t;@SMu5;@*IJp8dUO{JbWx?tJf4Dn?(Hjc5EUCVN{lH7WoVt-HGuf$VyeZPjW zuY4k})P+?^G4l^=#vG~>W$V6Vs=Q&9?kO*cGlxV=5;ny%-=E#fxo?(>a!P)j`J(Qs z56et%w3RLV>J;%%w&Fq`m$b0KI-A<-s&Ym@rvDI2VDOxEV!zs>0vAwuvd$@? F2>_BEt!n@P literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_pdk_action_unlock.png b/res/drawable-mdpi/ic_pdk_action_unlock.png new file mode 100755 index 0000000000000000000000000000000000000000..542703db3ba8f0e33df2ca671afc21406d6cd0b0 GIT binary patch literal 1248 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gjk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+m^Cs(B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxOgGuk*h0bFQqR!T z(!$6@N5ROz&`jUJQs2--*TB%qz|zXVPyq^*fVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8E%gnI^o@*kfhu&1EAvVcD|GXUm0>2hq!uR^WfqiV=I1GZOiWD5 zFD$Tv3bSNU;+l1ennz|zM-B0$V)JVzP|XC=H|jx7ncO3BHWAB;NpiyW)Z+ZoqGVvir744~DzI`cN=+=uFAB-e&w+(vKt_H^esM;Afr7I$DAddqG<*}2 zGxI=#nqXbNzE+-j#U+V($*G<$wn{*A^fEJ3tW3;|oGe|<&5T@~jSLN4%`MCz*Q}aq-dQ%X3&2j1lC5YStpv^9+MVV!(DQ-pixe8#9TV>*Q ziwREipn6kqyTufzUVWfr^g+>!6x}c(U>X83;fWW>fhYgeJYbqH0w(TM*Gvlr21Y+m z7srr_TU)NJ_hxYvIq>mz>QrGtk-ch*4lX*n=;5rB3QY=btN*tqTuKpa4xH_|A@Ypc zQ-?L_1{_WMF9?g~AAf&SZH#VUOdu~m&tS(}Sbzi>BJJNQ59yzO@r=g?KY z6IiR%^lzWsw(;8r4f}(#`?!VnB#Nisa@%e2^f-6cEUit~)s)U{O}w%t{bIgL)19yJ zOH`R`l6gx_cGb?hRoE9CleAx)^O<3*;+hQcR8=9L$6UHS<+BcK-z@krdU6KS&2ztd zRxC2J+tRzOi+#(~H^TY1MOVLAXx|q2bo+MeeebVDyRQ7Get=;Uk8no0VE$Q9nd9l| K=d#Wzp$Py}U$t5Q literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_ambient_light.png b/res/drawable-xhdpi/ic_ambient_light.png new file mode 100755 index 0000000000000000000000000000000000000000..64185f1622836de342e8f05709e7a8d9351b4772 GIT binary patch literal 1569 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 Q2&!N`UHx3vIVCg!0Q~D0ZU6uP literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_pdk_action_lock.png b/res/drawable-xhdpi/ic_pdk_action_lock.png new file mode 100755 index 0000000000000000000000000000000000000000..093cf20ca3defd36c82a9e9d664c5f2b72dec283 GIT binary patch literal 1461 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWuD@%o>>?5hW46K32*3xq68pHF_1f1wh>l3^w)^1&PVosU-?Y zsp*+{wo31J?^jaDOtDo8H}y5}EpSfF$n>ZxN)4{^3rViZPPR-@vbR&PsjvbXkegbP zs8ErclUHn2VXFi-*9yo63F|8hm3bwJ6}oxF$}kgLQj3#|G7CyF^YauyCMG83 zmzLNn0bL65LT&-v*t}wBFaZNhzap_f-%!s0#9CcUNA2%eZ%@_WRkj)vq@1SYLi` z?|t*{cZ$!~J`M=o>XA|`#+et?aQE4^2QzyUBzTzQKQx33FfkUK-eRuXAsNVR<{0z$ zK2yS+(EV%J?laH3^2V1@`amQH)2@Tvvu3}2c6-0_d7A*{JO}ntTP8gRftp4!1I}fQ z-Mh*qWp0TwN3-RB?KskW@vpFg*WLLnXUlvRvR&GFk8jh5&(c5E-c(FzlyZ>>NqxE7 z>t~yTr{3ce>Jxbb-WE?dbt$~Nf62PLUXNO`)s|VCOkNg~pA{U;Mx=6|mwPd;0(TOZD6DzR;P$oV*~aJa+Ytdp-J4eU7bVZY~^#twQx{k~9NZ zd?aJCnQ|9#T>?5hW46K32*3xq68pHF_1f1wh>l3^w)^1&PVosU-?Y zsp*+{wo31J?^jaDOtDo8H}y5}EpSfF$n>ZxN)4{^3rViZPPR-@vbR&PsjvbXkegbP zs8ErclUHn2VXFi-*9yo63F|8hm3bwJ6}oxF$}kgLQj3#|G7CyF^YauyCMG83 zmzLNn0bL65LT&-v*t}wBFaZNhzap_f-%!s0l?YJBS?bT^lA?@9oIGRe?im1xM+N1swkzN`f2<1k5iw{%3P66r3LA z;#v^=!C%12L-6QAg}$X4TXjPYK3dR}`G;*?K%Yit(atXl{C0=AciyR8|NY%vZ!OKP zAhk_Xr*r1@G3-8@d(imy0SyiY`40`|0<4JzhqsuUc1T`iDRT(=dH+ztq|p6q*z8&4 zMEXc&)yQMZ) zc|3A_{ZI3Q&)xrAR&OGl)+#vjsx4W3x4~_Uz7#i^zcj`F& ze6Fy1^I9qSnN0h;k8oc1@hOk+Y4U1}XpCo`Rmr_#*`I!;+x3BUOYf#Do_xZx!|SQe zlIvlP&W-Y-z7A84I?t&(AegY2Vdf|Mw*m=IOsx3D4zwK8`;=GYbS0&NY2F%(-c?7B zHzw`f%dlVc>j9Y^PqrB49pqT4I^*a8rQ=KAve&L@zH!>v%uzO*(ej*h&!MP>$tPTd z3;3rUw77BE;{WtFtB?M9aGa}aj`jzRzTSDiEB`b7mJ+=sDX4fv$m{RM?o?MngT;kw zWlOr15_s1%a6S5c;@9^sp(CLWL~eyzz$*Geyv@XrH hueM-1b8|fdGsBJx_Kqi7vcG`JQ%_evmvv4FO#pZ17hC`U literal 0 HcmV?d00001 diff --git a/res/drawable-xxhdpi/ic_ambient_light.png b/res/drawable-xxhdpi/ic_ambient_light.png new file mode 100755 index 0000000000000000000000000000000000000000..84d9cbbab2dfb900a7c2a82b94feb548ecf08b77 GIT binary patch literal 1880 zcmaJ?X;2eq7>*)`C?X(Q1#yi6f{Z#tua|h44YlDizEuA1Qjji zP}C8jo`^z6QHx?lJgb6(fI5nUqA-96h*+?VXYGcH_D6ANcE9ia=6#;`dcN7MlEtAm z)-$Xr6pD=~42mMxFw<*gL4KF^{~Agz(+Q!Jh(_ay6qy#L2o&fV7!V<{1UL$oDfCHo zFqc9xo2QDA5>jylM~)&inaPKyLogCep>X*+OeR;u1h589P-%G7f$ED?K&9YOm-~tt zVk{U|s>0T5;pp{?W8~}Aa<+oX4+OY64oQH(gbdIji5i@v<57osIpp4SOs4|FE`*v# z{WmG8SONs2S{U%9`FYDhp8&w0O#^+|0Zf(`z+`}abOxIavb-4}hr#ABK;Y9uC8KE- z@ti0qEVs*HH1v(t-XZHm?P5QgiIrcMG%il^3YT&1t%!PA7lwaAf}Mb0zsj_0Q3unf`uT1<bS4eESyL6`r0*WjA$Eb*IZvros~ zGV{Ali(OE6CO$R&ssG`g;?_dY*ic{iq$1GwXCLF^7YPj?@$U6k+D~j}WYO=rPqfG^ zudaT0uV@~Pwk&X8V_`w`_(iT_vH1AMn;8p%E~A}mS4+(*l$n)do-MFdnqRbMKj`>F zvBWW)`jq0i?(Raqj^dYr7B*Sv@1}lWV$)-O;7TaFGP3tGn#~`NEYO* z?xDP~*&n#v92rw>q26#=B`HuFJTI38RPbgLcl7RdFr=nlm9zBwek;n$dDA92Zq;Jh z%j$V@c@Z}G{r%2?&ZO=445gVho6kG6*%{Y*?@8qcSZsdirQ}z6oJ^Wl+}_ACJT`mm zR32Az%%H41A6fAC&g!8izG{}VR8yF-sLjgB-1nPOU-!#u@mp^OGvmwsSSK1U zxn2clyI2#d>mlk^FQLXN?(p;*O(n0qp3Q7KJFTtMEzM##k_}X}$M4NtkmQ{1_?KXe|9`XteNPO(Ykb646wW zGJW%ySode{uaAl5BS=`3J{2FWu${Mei7VJ9*b`2v zcSoaAw)Qf%EJG1A-wdP_)l}^Ok3qu>7UUs$ZaZBHg=HPE{%_fBe7{JZdl)yE8P zS%(s>TiPEu-`Kb#;M1& literal 0 HcmV?d00001 diff --git a/res/drawable-xxhdpi/ic_pdk_accelerometer.png b/res/drawable-xxhdpi/ic_pdk_accelerometer.png new file mode 100755 index 0000000000000000000000000000000000000000..d9970ce495ac8ec2c2a46db37b473e19fa136c42 GIT binary patch literal 2681 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 NHlD72F6*2UngFwcojU*k literal 0 HcmV?d00001 diff --git a/res/drawable-xxhdpi/ic_pdk_action_unlock.png b/res/drawable-xxhdpi/ic_pdk_action_unlock.png new file mode 100755 index 0000000000000000000000000000000000000000..0ffdc6048a3149133a4407456595ad1e3b14245c GIT binary patch literal 1784 zcmaJ?X;c$e7>%M>5EKy=K|EvV0Yu405|Y&D8Ik}ggoY55A_|javH;11WQ2fJkSJxb zxZ$ZHEx3RSqAbU~F0m}4fXZsI6txAhrhrSeXw^;-us=%YoSFCDcfNbS`|kVZyc}U@ zpuMe&Ee?mX=Lc~`*cfDaKe5KX5r5PKV#7j|7m0==ax@vz!8lNcNML}kh7_;}hGZ#; zjqnN_&dN(Ajzl8`AuK7PCP0=Pfh6+EDlvGHqY z0+K+>ft9MDR2>|i8Y-5i#!Hzp{Hm3}3IhuxP{Sw$7}NdCXg&!8c!4m{y$Z%p2*gt zBKV`;|0LFnQ?xKq1nZFvIw^K=a(7E9EsLXrAr#Sx5hP*UMWGTw5xo-80vwLTH9x>h z07+FEOK|BZN+4kIHF^}%NMSygjmLNhDwT}o%V6;6{sCMHk3%7od43E!lfm()lF6I^ zKYu2TI*#Qc(hX`@gN|cmAF;eiv6hBVYq82)Sf@&YWdS-w4UBG_rJ7ue|D=3lSlQ%S zIFn+Dm>8nvc>i_W;~}hvEYpeR#TFCo4{NZV*I~`QIBzf=`)A~QE+{s1n~TleOaG$}j_O4ze^*D!1%Dg<{bhu%&KK6W8m92@OLf@y6 zvImRWVlVV3uE{XQmNbK@IOVkPwxTZELesL_Hd&h~LeHUcmzc|4;*NRh;*xKk4Y(^2il4su)}?+BH#Dyy z>#k&n_r;EgoMi7!y|>D~Y@!kS=JC?S=CJPHdU7T7xPo%4W4Se8PeAR+oL`+SCR@Cw zKs$~*{UzTIvYrFSuk;&T9++d!tMar-VtwhBJr@g| zZfq}O9w9VYhanH-+b?F9y|IUi>-sb2mU#EwIRyXg8*As88!8Hl4}NjlvwI}Fv3Mpk z%-rc^(uCob_i4mF@18Ztbt=)40?tn{$8pF>Gl$I``mg&Z%kxK)p2N pe(lu2L+(y{iScg9{ojjmR=E0J2Km{NQU}Xj#OH-_Px{AZ{sSTSxKaQB literal 0 HcmV?d00001 diff --git a/res/drawable-xxxhdpi/ic_ambient_light.png b/res/drawable-xxxhdpi/ic_ambient_light.png new file mode 100755 index 0000000000000000000000000000000000000000..d5c326912d85a1f090ff6f800a8873e4182c7d07 GIT binary patch literal 2016 zcma)7dr(tn7XJc9L{#V$OOcM=NfZG~UIap}1dK_L02f1ufR(Al2U8`IiAXf8!SyV)-G^n?6BHVNkT5f$)}*5(v<*$g3?jy8V+#Yq zv?4}AAi_Zi37UpQ=uuJ)ne{q@2{wxuFL`0=Z8x(S z&`TFGUBvh!sYFBpNpK?y1+s$tHQaz;NFZc!1BJmn{<{#5!wq6{glsO~pTmVYLYTva zUVRKIno*kqE2ZJDVo{oikw%gP%w}h0WwEjXShz8j%@qoTYz~jjtB1DNG1AbzWBhHW43e~f$9MD39M`FeRN<@Nfb25RSx)N+IB83cfNF>98Wo|6 zeY>@*%(8G{i5C_ttw}ui?5wUtSs+e6^Z{OfHuK_{v{=P$Z)5kBP0#uoh`2*L2izj7 z{5xc5gPY%tMa;m@Mm3>o;$gLEMZkb*XnJO>X!rQvge|#Ulbsia-eXin`}u^_uk$E4 zdE`gkyCo5cz0T@=4h@bENTgId785)VEIew^k`TUim{Fy={r425}DdfIO7e_rDB4|jU+lH|qNyP7)D zX|8R%EvukL_`Pgo{Ptv|?HS!#MuU#VI18r=PFQ1U$-lT=yV~Yr^E-^(Dm|o~xbn{T zZ>*~#lZw5&*q$jJf!PBtZJsx217!E@$y09*9M19@LVI+sY^io0_q9Z^>L!jUi!#DGS_H@M`tFMKKpWdCt)O7l7T|uv!EftH)j*x0 zZ1S=n_%fty2s!^l_MYy~YDYGsH9B}HH!?K3x>Id!IocVEp3tqWuzlQjMCrP5=bm5E zD&VD{;hX(8>4H@`Pu{sF@6GwqrcLU*s<3YFfYsOCwt25|9qLC-E2K~9#m3EAVdivx z&x*Jt_ruKhT1e3w&&$tvAzlv)Gm6>|byY2gwnAZ*Av>gn=TENHO{imcMD>t`qZRGv zWud9L^V->Yzb@GC1>BNy+g&$3N{ifGWK0E+pl*EgX?U1zSPMc`jfkaes&L|Wz2hbE z<*(0iKfRxGNksDD^9pQ ziD`%huG(qy$6vYBx#}7mBZ>YZ7vKOuKJ}Y}C;{s{><1-ol+2@q_W!6ferosV;l0d! z^4vwc_lF&i6((u7=#Skgt2ODRd| z;!>>ADGs7ek%W`naw?ZD>d5&`=hXTAanA4adVRmo^L$>P_xtmHKF{Zm=gIQ%_JAwv zD+2%kr&7pt`53nRtz04hzbKBklMi|#w?L5}pCgKA#(;n;n;#A$s66IQkPb503DNDK z0{|$Xx&DEoK-y*^i_gO_mt!zuo~=gV#^67e-e z6yb>cQ&b?$2SMV;fCy`hjX4W%X@{^SVDQ!iI}0l_gar<7gT)cBcq?-po`@q5ad^aE zhLmTEVRMLdvio1Tyn3p~Mh+9=^)Ws(;RD`pC?cnofNOWy)% zwErK<<9&}7is<0qdjFAF=${|}v2;+#kBwo;4~}EBoJv3>#ehr^KgOTWkNj3epIv+r zU$~1eK#)kwRkKB)X-pP3YT0S}6-A>FsZl}^Gl~UL$&N@l4~ENS6D?ic$t0Y+wT0Uz zH$2|W))j9?28w3C-T<(2m`Zl_7Z3av%#93i(`u=qD`wx{%EspCxM#`3(Lq}I z8fN-1*eZ;g7CZ&v-l6(q<|?$Vueusz6FF_eaY&i2nl=gwODnp2c_rHG)I`OE@q@Xc zn>)w5I!gwU3b41P_tfwC*nJ|Y+x8K?%;&yRr}IVw6;~Izg+b(&0#lSyHU{ncJ!G9g z*tRzmwy3QZ*1Zn0TVw2Vw2Uu#ErCnSWU;{q&;k3L+xGv2C_Os{8CT#ac&~_%B%Fs8 zD=rw|)O|1rnc;awu|l184;mqJj!^Aan$)U+%rf@4WFY&_DdAAr8jb5OLdhsHJD2d>ByZoKJZj%Q#Xt#BQj_2O8nAj_#-mCC zFs}y`(dI!d@YLaz0(4uwgRI2qiu%rM+u#9f&ufvbXG%@pmHBNMvE8}r&GRu*(rnO3 zqwcWsq+T$_JH$sQbH8=VqglJ|=He}#KEOjE(#Kzsf4VtpI-<>;jQUV|(c9Jwr~w?L+_(fOt1MtXMcEkn3C!*~4k zmCW@CgBeq9n;Xn>%DbwI+kVaPo1~byIr(J-D9vG_N zpyOdkgf1ynTbi9O%nJ13%fNA<((6gDuArE4Z^rI(L0P^;Me|leOXrytKbV+zH|axH zTTCZf984@i>n<9boGw#1nz~mTNAWFr5B#7Ts{^=uKJnG7!ydeKZ+L4jO@`19>~^s8 zAz_$?g=zSL*emQ>t{Z&sBhPpz#DNfD_sGY`S0?*bEZ#=_n$S+%@1-`=1qP@|R48p>K{s${*c0Z5E_LLf$Q=RWfHz4~)wIlgigOwViOr>XLbZ5cO7*c_h zlHjS=Yt!LQdX5yY2RzKw6U&BLZDBJv%u=CXWiiOw#KEOk&QA`%u!y7i^;|^ly6m

{ZEsH~qnY}4U7iY^{pxZCh}s-pP?z(t>`Bza2(O^*8!xAh018wZ zFSV>TRuyqH8avdNeyN&#l-OjS!eVB9;#PIH=D|`!v!u`4JAV%vM%;L;yXqW?acRfr z>zOjsyy_ocO^p6hh|cdeJEp1g`Mxwk0Y_{o-qf4<0EXP3#C7gda_<*s`H9aKaL;@C zg=M$#@yBh*AJ^npBdQO$HnKJwOsRPpn_r4d_r{)W%~{KmgfZGBYpa?PihuO&%Tt#2 zFq&3UKFmx#c<6XqW1h5G!+lqg^^z8yl_RQwC`mhn&!!JkKB883N-`iH3F-Gz8ud;- zn}>{W;lgBI$G}y8{~?Y+gv=pjeOODIruev|ZIO|_#p!xTZHW$?CeZFVZBqXpO?3%( z+InlGPj%KN9(}kxN27;`YQ?*b?>My`Dx6)gTXCxSCbccZ&tR1&ikDaO`be(U6SW!l zkRpHE3G}+ftaGW+x<=d2l`_3x?~4vCiEZi+wnN8kSjl@c`*Vo5dq=fweB!)(0%UO< zgNv3O5fHoDy3ht4aFTMrBJCHxPZ6ylvyIIWx0S|dt1ekKRb;W&eoW0^Uz;5L=yRcm z@76bmGOXT(=VAj&;<65wG446AM}s|n%SFCPmfS1pKLhG%PK0E}Tqr6Z&QD90qGl~r z7`wNWf!c9hzZ%A0ERm%8)>;ctt2Y+ZwPvL~K32cSP5W`GsWUU(VGlpo^lXM}7Qy5{ zMQfz>5$C=3OZcoETWo9Q4Np&3=Zc@ryu1)_jo0IH#KOMgeMbA3CHSk~C8x&?L%S=R zH80P<%)rl;P41CeQlN<@cOZtJUPgbKGmuaa7xr2mpDt|MeG=+Cyzpj8ob2$h(&%V` ztDT?5e4uH-sQzQI{f%Ch2em*I)mfywxVylIcsENT@WehKiR|G$GQmBLvdWU4GGhS= z^(%wl^}Ew&Ka^4brm6=>zW;?9dW3nWb^Dpm;?!*rKZmvO!D$Czrk)R_q$^JrMt8Q7 ztR0yRpGLi8mB{x@Iw$*FFebSb!o1Vfe~Mtc#6=&hf4#TZ}d*6F}U!-4iLy@jkR;B7AiZAC(g(9JT{?ttxm*ZlLu_^sWf4T=X-T;uNJ|9R86r_a zsZ-;m8qVBQYWa!$FbAR<$s2sYI?+ zYi-J9uTT;RPplw`AP%UZ4s{Y1urp4L@LW5JDBT=0?7ga^;Fb()Bah`hmSdeM?rm(8%V_{E= zg{WX4TYLY}Zo7(_A=~lI@KTpIxQQh_6^4f}W^T;04it>SB^RqKG&1b$!C#t0vfHAr6I=}%0>=K{< z{e}dQIxv@h-T1rlqH)&HGsqffO|iyVgC|xVezf4?Vm@6N;1}@3Bk{w@VP|WOb+I+w zh5lBYF}*m(SnB@pD06FoIbi1=AeVH9JX7Ae^St8fhL(?nP2&bmY|F{i?E!fM{#!xI zG3QCs5-WVFK6>c4KwsvIB4Eg3Fc~VlhQ4!gF20gonSwE4&Ud64QXMhRaQQwL6bbR5wZgg+B zllSb5O6SffqNgqS>;lI<*}wR+=65DMSefm21zyefk&AbAH$CheEF%kyndi@=3i|Ne z)a~Xnv$4q>9FR9Vr7tA5f{nP;-}UUYELX4c2y-t8UtV9HRCgjUyS(Ou*24KQb2A#f zKhM5)Y^?NFPS>F?H{HA4I_qBD7HB5!Lv+;LT3D^!>he?C>78-Qcok(WRsBEAa2=`sy_9i(N zx`Kcs@GrqjO23?>uZXzF^#ON?%R|?#eea=EzbQl8!?_?CMXJtx92!5+bLTSet95ye zD?(_amK!eFje%yXYj#_a%VyTnk%MDC`6YeHv{K)8lh^+0s}E#EOiJ;fc*J3cFXKj! z;Bj?_(}Twb%iYNV&XxlYPJhlhH$jLPg369T- zv2d`HvB$EB#NJ3mnNU^>tso{49uu0JPPBUD^dN++d(|zLF8&kFQ zs;zP;V4Felh*dOd@#5;C9F|e**wz+7YlB!t+iFWtp&KN$f7I^G+wXgC-tYI$nQv1{ zQUcXK#GgbWQDqAx3gT3`#di$xzrD-ECyrn|UWun7S-44Ugh?VTk_iJcy*e9Kz-p~I zzZH%qk-WooX-ZrvPlPmxo}qU8Fbef3fhLinV+v8VCKtwmOgLL-5Yir=Yoq}>t&o-; zC1=S|F`T1YP-KKti;~hbMY$S*mKHM~h%ST(0zHhYfkJ(r0fP#Kv|(O|xVz0v8ZhjF z=L%^rl2Xc305M{OfhYzyLIXzf0iJ*XMhWL3l)9W&^G+j4)s`Jq!{U z(-?Iqu0srfn^B#K6yQP{k?D&P^ynz90UK!((J*GA8fAhEmb)a6qg?*~u6q5bH-;Aq7SaF}SRm?ocQsHo|HgF{U9%o~Mf`IS7tmIS2}f#qO^0fN;55 zqcgaTbB0lJIV3Y+xZ0qBWfCEc;9=-=S}2mwkLSio1?+e+8wBHde6D~mj^lu!SjvkN zL~%S=38E>`!v@@g)&7GOy%g(S2t7(fmcT~cYFH~ZB6?uBa7gzbxqx7#fGy>6U&=Rv z)xJCz{!6h;A{nN8y#E@v$3*Oq`#QS3gfhDRuz}clBeC4F5VVCvqKuVEL}`UL?(b~k53$GDlN%rUy?H+VEYBH6>H{k z&cLF5PrkR!tu1@JvMZwlzqki0>i7xE>ies*cw)Y@V|Qd1mgVGOPEI<#G^q3w=W+iW z64mArdadh~&hR5P>Pbt1N}$}geTX!pIrNd&R!fg<*OP#DI65%b>!N6ia>8$Q51XUV z;NoiXm6Mbz?*!3`OXv4DFGi;q*O0TOQJ&ktTg9_Q<4=E+)Zb+%*LRm~XiJBZTc2i@ z4K#cfO@5*ZykC3S)%V_)-Lt8_eb3%}^|NP7JAKABC7$AmTtW1VUd(y;xgVcmkWO88 zla$;v_mR)Fd%IM#zW0He?)jQm-)#t+wuwCFitqgLFLYk`x{5&8!iuElm8=TNTi@E> z=TJ`vIjnu_zQ$S_MMK=J16S?upR52jt?Ihx+*_qp7SS)1SRdz4Y4Q`FjZqyj+_qOw z>hoLV5_fAaxvHu=G%X(;T~dCC4ynPlQ#s|bKgfArT@#^f@9@{0JMUB-rVH1VUzl`x z)xqK`yD4)sde3)+T;=@gFn3W-1%H^lK8NkB4%=g@a+uYzTW3>U%l;zw9789`nYnUUO9uJ!Z|~W7=NHq>{f5{$AQpc7ATM<-CG_ z4?U53RB*%pf@6?^uTi=dW~9~y$S+Udp0I6GM&-6QS{~or-oEfzeOTz2{=o@nCEt|< z`(bqJm=um>L;#%idgy7BUG-G=-k_=0!CI~p9z^E_?nD|Ncr%Bv;!6JppKEs(0?r*| zqv>IIs_Vp#;PwM;H<#?SZ(Bb$F0Em~{x52ejbE_xyNDWiMxdf#0<={2CVaSqKQ8L< z^-}9uZPTB=I^MUII|Z-D-AMYA+?u;=yf}f{`?)21>c$(r>u20my;bV>)yBmeVt6U? zk00g5UO!-E?rpq2R44MuP{!=PUz^Zl@3dS)mc1etP9%Cd+Do7{wl#gNso7$h9?H69 zC65 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/card_generator_sensors_ambient_light.xml b/res/layout/card_generator_sensors_ambient_light.xml new file mode 100755 index 0000000..62dd685 --- /dev/null +++ b/res/layout/card_generator_sensors_ambient_light.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/menu/activity_data_stream.xml b/res/menu/activity_data_stream.xml new file mode 100755 index 0000000..d6a6bbf --- /dev/null +++ b/res/menu/activity_data_stream.xml @@ -0,0 +1,8 @@ + +

+ + diff --git a/res/values/databases.xml b/res/values/databases.xml index 9e41810..0b74d73 100755 --- a/res/values/databases.xml +++ b/res/values/databases.xml @@ -27,8 +27,14 @@ CREATE TABLE history(_id INTEGER PRIMARY KEY AUTOINCREMENT, fetched INTEGER, transmitted INTEGER, observed INTEGER, health TEXT, level INTERGER, plugged TEXT, present INTEGER, scale INTEGER, temperature INTEGER, voltage INTEGER, technology TEXT, status TEXT); - + CREATE TABLE history(_id INTEGER PRIMARY KEY AUTOINCREMENT, fetched INTEGER, transmitted INTEGER, observed INTEGER, application TEXT); ALTER TABLE history ADD duration REAL; + ALTER TABLE history ADD screen_active INTEGER; + + CREATE TABLE history(_id INTEGER PRIMARY KEY AUTOINCREMENT, fetched INTEGER, transmitted INTEGER, observed INTEGER, light_level REAL, raw_timestamp BIGINT, accuracy INTEGER); + + + CREATE TABLE history(_id INTEGER PRIMARY KEY AUTOINCREMENT, fetched INTEGER, transmitted INTEGER, observed INTEGER, x REAL, y REAL, z REAL, raw_timestamp BIGINT, accuracy INTEGER); diff --git a/res/values/generators.xml b/res/values/generators.xml index b4037cd..0150e72 100755 --- a/res/values/generators.xml +++ b/res/values/generators.xml @@ -158,4 +158,23 @@ Most Used (Last 24 Hours) Recently Used %1$s: %2$.0fm - \ No newline at end of file + + + Ambient Light + No ambient light levels have been recorded yet. + #FFD600 + #80FFEB3B + #ffFFEB3B + + + Accelerometer + No accelerometer readings have been recorded yet. + #3F51B5 + #80F44336 + #ffF44336 + #804CAF50 + #ff4CAF50 + #802196F3 + #ff2196F3 + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 73b220f..3c77eb0 100755 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -6,6 +6,8 @@ Data Stream Continue + Toggle Sort Lock + %d generator %d generators diff --git a/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java b/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java index 3e80bb0..c3a33f4 100755 --- a/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java +++ b/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java @@ -1,11 +1,16 @@ package com.audacious_software.passive_data_kit.activities; +import android.content.Intent; +import android.content.SharedPreferences; import android.os.Bundle; import android.os.Handler; import android.os.Looper; +import android.preference.PreferenceManager; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.view.Menu; +import android.view.MenuItem; import com.audacious_software.passive_data_kit.activities.generators.DataPointsAdapter; import com.audacious_software.passive_data_kit.generators.Generators; @@ -15,6 +20,7 @@ public class DataStreamActivity extends AppCompatActivity implements Generators.GeneratorUpdatedListener { private DataPointsAdapter mAdapter = null; + private Menu mMenu = null; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -70,4 +76,50 @@ public void run() { } }); } + + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + this.getMenuInflater().inflate(R.menu.activity_data_stream, menu); + + this.mMenu = menu; + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_pdk_toggle_sort_lock) { + this.toggleSortLock(); + + return true; + } + + return true; + } + + private void toggleSortLock() { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + + boolean locked = prefs.getBoolean(DataPointsAdapter.SORT_BY_UPDATED, DataPointsAdapter.SORT_BY_UPDATED_DEFAULT); + + MenuItem lockedItem = this.mMenu.findItem(R.id.action_pdk_toggle_sort_lock); + + if (locked) { + lockedItem.setIcon(R.drawable.ic_pdk_action_unlock); + } else { + lockedItem.setIcon(R.drawable.ic_pdk_action_lock); + } + + SharedPreferences.Editor e = prefs.edit(); + e.putBoolean(DataPointsAdapter.SORT_BY_UPDATED, (locked == false)); + e.apply(); + + this.mAdapter.notifyDataSetChanged(); + } } diff --git a/src/com/audacious_software/passive_data_kit/activities/generators/DataPointsAdapter.java b/src/com/audacious_software/passive_data_kit/activities/generators/DataPointsAdapter.java index b934071..38cffc3 100755 --- a/src/com/audacious_software/passive_data_kit/activities/generators/DataPointsAdapter.java +++ b/src/com/audacious_software/passive_data_kit/activities/generators/DataPointsAdapter.java @@ -1,6 +1,8 @@ package com.audacious_software.passive_data_kit.activities.generators; import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.View; @@ -18,6 +20,9 @@ import java.util.List; public class DataPointsAdapter extends RecyclerView.Adapter { + public static final String SORT_BY_UPDATED = "com.audacious_software.passive_data_kit.activities.generators.DataPointsAdapter"; + public static final boolean SORT_BY_UPDATED_DEFAULT = true; + private Context mContext = null; @Override @@ -85,46 +90,57 @@ public int getItemCount() { } private void sortGenerators(final Context context, List> generators) { - Collections.sort(generators, new Comparator>() { - @Override - public int compare(Class one, Class two) { - long oneUpdated = 0; - - try { - Method oneGenerated = one.getDeclaredMethod("latestPointGenerated", Context.class); - - oneUpdated = (long) oneGenerated.invoke(null, context); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + + if (prefs.getBoolean(DataPointsAdapter.SORT_BY_UPDATED, DataPointsAdapter.SORT_BY_UPDATED_DEFAULT)) { + Collections.sort(generators, new Comparator>() { + @Override + public int compare(Class one, Class two) { + long oneUpdated = 0; + + try { + Method oneGenerated = one.getDeclaredMethod("latestPointGenerated", Context.class); + + oneUpdated = (long) oneGenerated.invoke(null, context); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + long twoUpdated = 0; + + try { + Method twoGenerated = two.getDeclaredMethod("latestPointGenerated", Context.class); + + twoUpdated = (long) twoGenerated.invoke(null, context); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + if (oneUpdated < twoUpdated) { + return 1; + } else if (oneUpdated > twoUpdated) { + return -1; + } + + return 0; } - - long twoUpdated = 0; - - try { - Method twoGenerated = two.getDeclaredMethod("latestPointGenerated", Context.class); - - twoUpdated = (long) twoGenerated.invoke(null, context); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); + }); + } else { + Collections.sort(generators, new Comparator>() { + @Override + public int compare(Class one, Class two) { + return one.getCanonicalName().compareTo(two.getCanonicalName()); } - - if (oneUpdated < twoUpdated) { - return 1; - } else if (oneUpdated > twoUpdated) { - return -1; - } - - return 0; - } - }); + }); + } } public int getItemViewType (int position) { diff --git a/src/com/audacious_software/passive_data_kit/generators/device/Battery.java b/src/com/audacious_software/passive_data_kit/generators/device/Battery.java index f940c35..6fead94 100755 --- a/src/com/audacious_software/passive_data_kit/generators/device/Battery.java +++ b/src/com/audacious_software/passive_data_kit/generators/device/Battery.java @@ -11,7 +11,6 @@ import android.os.BatteryManager; import android.os.Bundle; import android.support.v4.content.ContextCompat; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -342,13 +341,9 @@ public String getFormattedValue(float value, AxisBase axis) { if (level != lastLevel) { values.add(0, new Entry(when, level)); lastLevel = level; - - Log.e("SLEEP-SIGHT", "VALUE: " + level + " -- " + (when - start)); } } - Log.e("SLEEP-SIGHT", "BATT VALUES COUNT 2: " + values.size()); - LineDataSet set = new LineDataSet(values, "Battery"); set.setAxisDependency(YAxis.AxisDependency.LEFT); set.setLineWidth(2.0f); diff --git a/src/com/audacious_software/passive_data_kit/generators/device/ForegroundApplication.java b/src/com/audacious_software/passive_data_kit/generators/device/ForegroundApplication.java index 4eb5410..90f4620 100755 --- a/src/com/audacious_software/passive_data_kit/generators/device/ForegroundApplication.java +++ b/src/com/audacious_software/passive_data_kit/generators/device/ForegroundApplication.java @@ -13,9 +13,11 @@ import android.os.Bundle; import android.provider.Settings; import android.util.Log; +import android.view.Display; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.WindowManager; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; @@ -46,12 +48,13 @@ public class ForegroundApplication extends Generator{ private static final String ENABLED = "com.audacious_software.passive_data_kit.generators.device.ForegroundApplication.ENABLED"; private static final boolean ENABLED_DEFAULT = true; - private static int DATABASE_VERSION = 2; + private static int DATABASE_VERSION = 3; private static final String TABLE_HISTORY = "history"; private static final String HISTORY_OBSERVED = "observed"; private static final String HISTORY_APPLICATION = "application"; private static final String HISTORY_DURATION = "duration"; + private static final String HISTORY_SCREEN_ACTIVE = "screen_active"; private static ForegroundApplication sInstance = null; @@ -94,14 +97,26 @@ private void startGenerator() { this.mAppChecker.other(new AppChecker.Listener() { @Override public void onForeground(String process) { - Log.e("PDK", "PROCESS: " + process); - long now = System.currentTimeMillis(); + WindowManager window = (WindowManager) me.mContext.getSystemService(Context.WINDOW_SERVICE); + Display display = window.getDefaultDisplay(); + + boolean screenActive = true; + + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { + if (display.getState() != Display.STATE_ON) { + screenActive = false; + } + } + + Log.e("PDK", "PROCESS: " + process + " -- " + screenActive); + ContentValues values = new ContentValues(); values.put(ForegroundApplication.HISTORY_OBSERVED, now); values.put(ForegroundApplication.HISTORY_APPLICATION, process); values.put(ForegroundApplication.HISTORY_DURATION, me.mSampleInterval); + values.put(ForegroundApplication.HISTORY_SCREEN_ACTIVE, screenActive); me.mDatabase.insert(ForegroundApplication.TABLE_HISTORY, null, values); @@ -130,6 +145,8 @@ public void onForeground(String process) { this.mDatabase.execSQL(this.mContext.getString(R.string.pdk_generator_foreground_applications_create_history_table)); case 1: this.mDatabase.execSQL(this.mContext.getString(R.string.pdk_generator_foreground_applications_history_table_add_duration)); + case 2: + this.mDatabase.execSQL(this.mContext.getString(R.string.pdk_generator_foreground_applications_history_table_add_screen_active)); } this.setDatabaseVersion(this.mDatabase, ForegroundApplication.DATABASE_VERSION); @@ -200,8 +217,8 @@ public static void bindViewHolder(DataPointViewHolder holder) { ArrayList latest = new ArrayList<>(); - String where = ForegroundApplication.HISTORY_OBSERVED + " > ?"; - String[] args = { "" + yesterday }; + String where = ForegroundApplication.HISTORY_OBSERVED + " > ? AND " + ForegroundApplication.HISTORY_SCREEN_ACTIVE + " = ?"; + String[] args = { "" + yesterday, "1" }; c = generator.mDatabase.query(ForegroundApplication.TABLE_HISTORY, null, where, args, null, null, ForegroundApplication.HISTORY_OBSERVED); @@ -298,16 +315,19 @@ public int compare(HashMap mapOne, HashMap mapTw for (String key : appDef.keySet()) { double duration = appDef.get(key); + double minutes = duration / (1000 * 60); + try { String name = packageManager.getApplicationLabel(packageManager.getApplicationInfo(key, PackageManager.GET_META_DATA)).toString(); - double minutes = duration / (1000 * 60); - appName.setText(context.getString(R.string.generator_foreground_application_app_name_duration, name, minutes)); Drawable icon = packageManager.getApplicationIcon(key); appIcon.setImageDrawable(icon); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); + + appName.setText(context.getString(R.string.generator_foreground_application_app_name_duration, key, minutes)); + appIcon.setImageDrawable(null); } double remainder = largestUsage - duration; diff --git a/src/com/audacious_software/passive_data_kit/generators/sensors/Accelerometer.java b/src/com/audacious_software/passive_data_kit/generators/sensors/Accelerometer.java index 3828e56..93e805d 100755 --- a/src/com/audacious_software/passive_data_kit/generators/sensors/Accelerometer.java +++ b/src/com/audacious_software/passive_data_kit/generators/sensors/Accelerometer.java @@ -1,8 +1,658 @@ package com.audacious_software.passive_data_kit.generators.sensors; +import android.app.Activity; +import android.content.ContentValues; +import android.content.Context; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.support.v4.content.ContextCompat; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.audacious_software.passive_data_kit.PassiveDataKit; +import com.audacious_software.passive_data_kit.activities.generators.DataPointViewHolder; +import com.audacious_software.passive_data_kit.diagnostics.DiagnosticAction; +import com.audacious_software.passive_data_kit.generators.Generator; +import com.audacious_software.passive_data_kit.generators.Generators; +import com.audacious_software.pdk.passivedatakit.R; +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; + +import java.io.File; +import java.text.DateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + /** * Created by cjkarr on 4/17/2017. */ -public class Accelerometer { +public class Accelerometer extends SensorGenerator implements SensorEventListener { + private static final String GENERATOR_IDENTIFIER = "pdk-sensor-accelerometer"; + + private static final String ENABLED = "com.audacious_software.passive_data_kit.generators.device.Accelerometer.ENABLED"; + private static final boolean ENABLED_DEFAULT = true; + + private static final String DATABASE_PATH = "pdk-sensor-accelerometer.sqlite"; + private static final int DATABASE_VERSION = 1; + + public static final String TABLE_HISTORY = "history"; + + public static final String HISTORY_OBSERVED = "observed"; + private static final String HISTORY_RAW_TIMESTAMP = "raw_timestamp"; + private static final String HISTORY_ACCURACY = "accuracy"; + public static final String HISTORY_X = "x"; + public static final String HISTORY_Y = "y"; + public static final String HISTORY_Z = "z"; + + private static Accelerometer sInstance = null; + private static Handler sHandler = null; + + private static boolean sIsDrawing = false; + private static long sLastDrawStart = 0; + + private SQLiteDatabase mDatabase = null; + + private Sensor mSensor = null; + + private static int NUM_BUFFERS = 3; + private static int BUFFER_SIZE = 1024; + + private long mLastCleanup = 0; + private long mCleanupInterval = 15 * 60 * 1000; + + private int mActiveBuffersIndex = 0; + private int mCurrentBufferIndex = 0; + + private float[][] mXValueBuffers = null; + private float[][] mYValueBuffers = null; + private float[][] mZValueBuffers = null; + private int[][] mAccuracyBuffers = null; + private long[][] mRawTimestampBuffers = null; + private long[][] mTimestampBuffers = null; + + long mBaseTimestamp = 0; + + private long mLatestTimestamp = 0; + + public static Accelerometer getInstance(Context context) { + if (Accelerometer.sInstance == null) { + Accelerometer.sInstance = new Accelerometer(context.getApplicationContext()); + } + + return Accelerometer.sInstance; + } + + public Accelerometer(Context context) { + super(context); + } + + public static void start(final Context context) { + Accelerometer.getInstance(context).startGenerator(); + } + + private void startGenerator() { + final SensorManager sensors = (SensorManager) this.mContext.getSystemService(Context.SENSOR_SERVICE); + + final Accelerometer me = this; + + Generators.getInstance(this.mContext).registerCustomViewClass(Accelerometer.GENERATOR_IDENTIFIER, Accelerometer.class); + + File path = PassiveDataKit.getGeneratorsStorage(this.mContext); + + path = new File(path, Accelerometer.DATABASE_PATH); + + this.mDatabase = SQLiteDatabase.openOrCreateDatabase(path, null); + + int version = this.getDatabaseVersion(this.mDatabase); + + switch (version) { + case 0: + this.mDatabase.execSQL(this.mContext.getString(R.string.pdk_generator_accelerometer_create_history_table)); + } + + this.setDatabaseVersion(this.mDatabase, Accelerometer.DATABASE_VERSION); + + if (Accelerometer.isEnabled(this.mContext)) { + this.mSensor = sensors.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + + Runnable r = new Runnable() + { + public void run() + { + Looper.prepare(); + + me.mXValueBuffers = new float[Accelerometer.NUM_BUFFERS][Accelerometer.BUFFER_SIZE]; + me.mYValueBuffers = new float[Accelerometer.NUM_BUFFERS][Accelerometer.BUFFER_SIZE]; + me.mZValueBuffers = new float[Accelerometer.NUM_BUFFERS][Accelerometer.BUFFER_SIZE]; + me.mAccuracyBuffers = new int[Accelerometer.NUM_BUFFERS][Accelerometer.BUFFER_SIZE]; + me.mRawTimestampBuffers = new long[Accelerometer.NUM_BUFFERS][Accelerometer.BUFFER_SIZE]; + me.mTimestampBuffers = new long[Accelerometer.NUM_BUFFERS][Accelerometer.BUFFER_SIZE]; + + me.mActiveBuffersIndex = 0; + me.mCurrentBufferIndex = 0; + + Accelerometer.sHandler = new Handler(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) + sensors.registerListener(me, me.mSensor, SensorManager.SENSOR_DELAY_NORMAL, 0, Accelerometer.sHandler); + else + sensors.registerListener(me, me.mSensor, SensorManager.SENSOR_DELAY_NORMAL, Accelerometer.sHandler); + + Looper.loop(); + } + }; + + Thread t = new Thread(r, "accelerometer"); + t.start(); + } else { + if (this.mSensor != null) { + sensors.unregisterListener(this, this.mSensor); + + if (Accelerometer.sHandler != null) { + Looper loop = Accelerometer.sHandler.getLooper(); + loop.quit(); + + Accelerometer.sHandler = null; + } + + me.mXValueBuffers = null; + me.mYValueBuffers = null; + me.mZValueBuffers = null; + me.mAccuracyBuffers = null; + me.mRawTimestampBuffers = null; + me.mTimestampBuffers = null; + + me.mActiveBuffersIndex = 0; + me.mCurrentBufferIndex = 0; + + this.mSensor = null; + } + } + } + + public static boolean isEnabled(Context context) { + SharedPreferences prefs = Generators.getInstance(context).getSharedPreferences(context); + + return prefs.getBoolean(Accelerometer.ENABLED, Accelerometer.ENABLED_DEFAULT); + } + + public static boolean isRunning(Context context) { + if (Accelerometer.sInstance == null) { + return false; + } + + return Accelerometer.sInstance.mSensor != null; + } + + public static ArrayList diagnostics(Context context) { + return new ArrayList<>(); + } + + public static void bindViewHolder(final DataPointViewHolder holder) { + if (Accelerometer.sIsDrawing) { + Log.e("PDK", "IS DRAWING"); + return; + } + + final long drawStart = System.currentTimeMillis(); + + if (drawStart - Accelerometer.sLastDrawStart < (30 * 1000)) { + Log.e("PDK", "TOO SOON"); + return; + } + + Accelerometer.sLastDrawStart = drawStart; + + Accelerometer.sIsDrawing = true; + + Log.e("PDK", "HOLDER " + holder.hashCode()); + + final Context context = holder.itemView.getContext(); + final View itemView = holder.itemView; + + final Accelerometer generator = Accelerometer.getInstance(context); + + final long now = System.currentTimeMillis() / (1000 * 60 * 5); + final long start = now - (24 * 12); // * 60); + + Log.e("PDK", "START QUERY: " + (System.currentTimeMillis() - drawStart)); + + Cursor c = generator.mDatabase.query(Accelerometer.TABLE_HISTORY, null, null, null, null, null, Accelerometer.HISTORY_OBSERVED + " DESC"); + + Log.e("PDK", "END QUERY: " + (System.currentTimeMillis() - drawStart)); + + View cardContent = itemView.findViewById(R.id.card_content); + View cardEmpty = itemView.findViewById(R.id.card_empty); + TextView dateLabel = (TextView) itemView.findViewById(R.id.generator_data_point_date); + + Log.e("PDK", "ACCEL PREP: " + (System.currentTimeMillis() - drawStart) + " -- COUNT: " + c.getCount()); + + if (c.moveToNext() && (context instanceof Activity)) { + cardContent.setVisibility(View.VISIBLE); + cardEmpty.setVisibility(View.GONE); + + long timestamp = c.getLong(c.getColumnIndex(Accelerometer.HISTORY_OBSERVED)) / (1000 * 1000 * 1000); + + dateLabel.setText(Generator.formatTimestamp(context, timestamp)); + + Runnable r = new Runnable() { + @Override + public void run() { + Log.e("PDK", "THREAD START: " + (System.currentTimeMillis() - drawStart)); + + final ArrayList xLowValues = new ArrayList<>(); + final ArrayList xHighValues = new ArrayList<>(); + + final ArrayList yLowValues = new ArrayList<>(); + final ArrayList yHighValues = new ArrayList<>(); + + final ArrayList zLowValues = new ArrayList<>(); + final ArrayList zHighValues = new ArrayList<>(); + + final String where = Accelerometer.HISTORY_OBSERVED + " >= ? AND _id % 1024 = 0"; + final String[] args = { "" + start }; + + Cursor c = generator.mDatabase.query(Accelerometer.TABLE_HISTORY, null, where, args, null, null, Accelerometer.HISTORY_OBSERVED + " DESC"); + + long lastTimestamp = -1; + + float maxValue = 0; + float minValue = 0; + + float lowX = -1; + float highX = -1; + + float lowY = -1; + float highY = -1; + + float lowZ = -1; + float highZ = -1; + + int whenIndex = c.getColumnIndex(Accelerometer.HISTORY_OBSERVED); + int xIndex = c.getColumnIndex(Accelerometer.HISTORY_X); + int yIndex = c.getColumnIndex(Accelerometer.HISTORY_Y); + int zIndex = c.getColumnIndex(Accelerometer.HISTORY_Z); + + Log.e("PDK", "COUNT: " + c.getCount()); + Log.e("PDK", "ACCEL START BUILD: " + (System.currentTimeMillis() - drawStart)); + + while (c.moveToNext()) { + long when = c.getLong(whenIndex); + + when = when / (1000 * 1000); + when = when / (1000 * 6 * 50); + + float x = c.getFloat(xIndex); + float y = c.getFloat(yIndex); + float z = c.getFloat(zIndex); + + if (lastTimestamp != when) { + if (lastTimestamp != -1) { + xLowValues.add(0, new Entry(lastTimestamp, lowX)); + xHighValues.add(0, new Entry(lastTimestamp, highX)); + + yLowValues.add(0, new Entry(lastTimestamp, lowY)); + yHighValues.add(0, new Entry(lastTimestamp, highY)); + + zLowValues.add(0, new Entry(lastTimestamp, lowZ)); + zHighValues.add(0, new Entry(lastTimestamp, highZ)); + } + + lastTimestamp = when; + + lowX = x; + highX = x; + + lowY = y; + highY = y; + + lowZ = z; + highZ = z; + } else { + if (x < lowX) { + lowX = x; + } + + if (x > highX) { + highX = x; + } + + if (y < lowY) { + lowY = y; + } + + if (y > highY) { + highY = y; + } + + if (z < lowZ) { + lowZ = z; + } + + if (z > highZ) { + highZ = z; + } + } + + if (x > maxValue) { + maxValue = x; + } + + if (x < minValue) { + minValue = x; + } + + if (y > maxValue) { + maxValue = y; + } + + if (y < minValue) { + minValue = y; + } + + if (z > maxValue) { + maxValue = z; + } + + if (z < minValue) { + minValue = z; + } + } + + Log.e("PDK", "ACCEL END BUILD BUILD: " + (System.currentTimeMillis() - drawStart)); + + Log.e("PDK", "DATA COUNT: " + xLowValues.size()); + + if (lastTimestamp != -1) { + xLowValues.add(0, new Entry(lastTimestamp, lowX)); + xHighValues.add(0, new Entry(lastTimestamp, highX)); + + yLowValues.add(0, new Entry(lastTimestamp, lowY)); + yHighValues.add(0, new Entry(lastTimestamp, highY)); + + zLowValues.add(0, new Entry(lastTimestamp, lowZ)); + zHighValues.add(0, new Entry(lastTimestamp, highZ)); + } + + c.close(); + + Activity activity = (Activity) context; + + final float finalMaxValue = maxValue; + final float finalMinValue = minValue; + + Log.e("PDK", "THREAD HANDOFF: " + (System.currentTimeMillis() - drawStart)); + + final List> data = new ArrayList<>(); + data.add(xLowValues); + data.add(xHighValues); + data.add(yLowValues); + data.add(yHighValues); + data.add(zLowValues); + data.add(zHighValues); + + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + Log.e("PDK", "UI START: " + (System.currentTimeMillis() - drawStart)); + + int[] colors = { + R.color.generator_accelerometer_x_low, + R.color.generator_accelerometer_x_high, + R.color.generator_accelerometer_y_low, + R.color.generator_accelerometer_y_high, + R.color.generator_accelerometer_z_low, + R.color.generator_accelerometer_z_high + }; + + LineData chartData = new LineData(); + + for (int i = 0; i < colors.length; i++) { + int color = colors[i]; + + ArrayList entries = data.get(i); + + LineDataSet set = new LineDataSet(entries, ""); + set.setAxisDependency(YAxis.AxisDependency.LEFT); + set.setLineWidth(1.0f); + set.setDrawCircles(false); + set.setFillAlpha(192); + set.setDrawFilled(false); + set.setDrawValues(true); + set.setColor(ContextCompat.getColor(context, color)); + set.setDrawCircleHole(false); + set.setDrawValues(false); + set.setMode(LineDataSet.Mode.LINEAR); + + chartData.addDataSet(set); + } + + Log.e("PDK", "ACCEL START GRAPH: " + (System.currentTimeMillis() - drawStart)); + + final LineChart chart = (LineChart) itemView.findViewById(R.id.accelerometer_chart); + + if (chart != null) { + chart.setViewPortOffsets(0, 0, 0, 0); + chart.setHighlightPerDragEnabled(false); + chart.setHighlightPerTapEnabled(false); + chart.setBackgroundColor(ContextCompat.getColor(context, android.R.color.black)); + chart.setPinchZoom(false); + + final DateFormat timeFormat = android.text.format.DateFormat.getTimeFormat(context); + + final XAxis xAxis = chart.getXAxis(); + xAxis.setPosition(XAxis.XAxisPosition.BOTTOM_INSIDE); + xAxis.setTextSize(10f); + xAxis.setDrawAxisLine(true); + xAxis.setDrawGridLines(true); + xAxis.setCenterAxisLabels(true); + xAxis.setDrawLabels(true); + xAxis.setTextColor(ContextCompat.getColor(context, android.R.color.white)); + xAxis.setGranularityEnabled(true); + xAxis.setGranularity(1); + xAxis.setAxisMinimum(start); + xAxis.setAxisMaximum(now); + xAxis.setValueFormatter(new IAxisValueFormatter() { + @Override + public String getFormattedValue(float value, AxisBase axis) { + Date date = new Date((long) value * 5 * 60 * 1000); + + return timeFormat.format(date); + } + }); + + YAxis leftAxis = chart.getAxisLeft(); + leftAxis.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART); + leftAxis.setDrawGridLines(true); + leftAxis.setDrawAxisLine(true); + leftAxis.setGranularityEnabled(true); + leftAxis.setTextColor(ContextCompat.getColor(context, android.R.color.white)); + + YAxis rightAxis = chart.getAxisRight(); + rightAxis.setEnabled(false); + + chart.getLegend().setEnabled(false); + chart.getDescription().setEnabled(false); + + chart.setVisibleYRange((float) Math.floor(finalMinValue) - 1, (float) Math.ceil(finalMaxValue) + 1, YAxis.AxisDependency.LEFT); + chart.setData(chartData); + } + + Log.e("PDK", "UI END: " + (System.currentTimeMillis() - drawStart)); + + Accelerometer.sIsDrawing = false; + } + }); + } + }; + + Thread t = new Thread(r, "render_accelerometer_graph"); + t.start(); + + c.close(); + } else { + cardContent.setVisibility(View.GONE); + cardEmpty.setVisibility(View.VISIBLE); + + dateLabel.setText(R.string.label_never_pdk); + } + + c.close(); + + Log.e("PDK", "MAIN DONE: " + (System.currentTimeMillis() - drawStart)); + } + + public static View fetchView(ViewGroup parent) + { + return LayoutInflater.from(parent.getContext()).inflate(R.layout.card_generator_sensors_accelerometer, parent, false); + } + + @Override + public List fetchPayloads() { + return new ArrayList<>(); + } + + public static long latestPointGenerated(Context context) { + Accelerometer me = Accelerometer.getInstance(context); + + if (me.mLatestTimestamp == 0) { + Cursor c = me.mDatabase.query(Accelerometer.TABLE_HISTORY, null, null, null, null, null, Accelerometer.HISTORY_OBSERVED + " DESC"); + + if (c.moveToNext()) { + me.mLatestTimestamp = c.getLong(c.getColumnIndex(Accelerometer.HISTORY_OBSERVED) / (1000 * 1000)); + } + + c.close(); + } + + return me.mLatestTimestamp; + } + + public Cursor queryHistory(String[] cols, String where, String[] args, String orderBy) { + return this.mDatabase.query(Accelerometer.TABLE_HISTORY, cols, where, args, null, null, orderBy); + } + + @Override + public void onSensorChanged(SensorEvent sensorEvent) { + long rawTimestamp = sensorEvent.timestamp; + + if (this.mBaseTimestamp == 0) { + this.mBaseTimestamp = (System.currentTimeMillis() * (1000 * 1000)) - rawTimestamp; + } + + int accuracy = sensorEvent.accuracy; + long normalizedTimestamp = this.mBaseTimestamp + rawTimestamp; + + if (this.mCurrentBufferIndex >= Accelerometer.BUFFER_SIZE) { + this.saveBuffer(this.mActiveBuffersIndex, this.mCurrentBufferIndex); + + this.mCurrentBufferIndex = 0; + this.mActiveBuffersIndex += 1; + + if (this.mActiveBuffersIndex >= Accelerometer.NUM_BUFFERS) { + this.mActiveBuffersIndex = 0; + } + } + +// Log.e("PDK", "ACCEL[" + this.mCurrentBufferIndex + "/" + this.mActiveBuffersIndex + "] = " + sensorEvent.values[0] + " -- " + sensorEvent.values[1] + " -- " + sensorEvent.values[2]); + + this.mXValueBuffers[this.mActiveBuffersIndex][this.mCurrentBufferIndex] = sensorEvent.values[0]; + this.mYValueBuffers[this.mActiveBuffersIndex][this.mCurrentBufferIndex] = sensorEvent.values[1]; + this.mZValueBuffers[this.mActiveBuffersIndex][this.mCurrentBufferIndex] = sensorEvent.values[2]; + this.mAccuracyBuffers[this.mActiveBuffersIndex][this.mCurrentBufferIndex] = accuracy; + this.mRawTimestampBuffers[this.mActiveBuffersIndex][this.mCurrentBufferIndex] = rawTimestamp; + this.mTimestampBuffers[this.mActiveBuffersIndex][this.mCurrentBufferIndex] = normalizedTimestamp; + + this.mCurrentBufferIndex += 1; + } + + private void saveBuffer(final int bufferIndex, final int bufferSize) { + final Accelerometer me = this; + + me.mLatestTimestamp = System.currentTimeMillis(); + + Runnable r = new Runnable() { + @Override + public void run() { + long now = System.currentTimeMillis(); + + try { + me.mDatabase.beginTransaction(); + + for (int i = 0; i < bufferSize; i++) { + ContentValues values = new ContentValues(); + + values.put(Accelerometer.HISTORY_X, me.mXValueBuffers[bufferIndex][i]); + values.put(Accelerometer.HISTORY_Y, me.mYValueBuffers[bufferIndex][i]); + values.put(Accelerometer.HISTORY_Z, me.mZValueBuffers[bufferIndex][i]); + values.put(Accelerometer.HISTORY_OBSERVED, me.mTimestampBuffers[bufferIndex][i]); + values.put(Accelerometer.HISTORY_RAW_TIMESTAMP, me.mRawTimestampBuffers[bufferIndex][i]); + values.put(Accelerometer.HISTORY_ACCURACY, me.mAccuracyBuffers[bufferIndex][i]); + + me.mDatabase.insert(Accelerometer.TABLE_HISTORY, null, values); + } + + me.mDatabase.setTransactionSuccessful(); + } finally { + me.mDatabase.endTransaction(); + } + + Bundle update = new Bundle(); + update.putLong(Accelerometer.HISTORY_OBSERVED, now); + + Bundle sensorReadings = new Bundle(); + + sensorReadings.putFloatArray(Accelerometer.HISTORY_X, me.mXValueBuffers[bufferIndex]); + sensorReadings.putFloatArray(Accelerometer.HISTORY_Y, me.mYValueBuffers[bufferIndex]); + sensorReadings.putFloatArray(Accelerometer.HISTORY_Z, me.mZValueBuffers[bufferIndex]); + sensorReadings.putLongArray(Accelerometer.HISTORY_RAW_TIMESTAMP, me.mRawTimestampBuffers[bufferIndex]); + sensorReadings.putLongArray(Accelerometer.HISTORY_OBSERVED, me.mTimestampBuffers[bufferIndex]); + sensorReadings.putIntArray(Accelerometer.HISTORY_ACCURACY, me.mAccuracyBuffers[bufferIndex]); + + update.putBundle(SensorGenerator.SENSOR_DATA, sensorReadings); + SensorGenerator.addSensorMetadata(update, me.mSensor); + + Generators.getInstance(me.mContext).notifyGeneratorUpdated(Accelerometer.GENERATOR_IDENTIFIER, update); + + if (now - me.mLastCleanup > me.mCleanupInterval) { + me.mLastCleanup = now; + + long start = (now - (2 * 24 * 60 * 60 * 1000)) * 1000 * 1000; + + String where = Accelerometer.HISTORY_OBSERVED + " < ?"; + String[] args = { "" + start }; + + me.mDatabase.delete(Accelerometer.TABLE_HISTORY, where, args); + } + } + }; + + Thread t = new Thread(r, "accelerometer-save-buffer"); + t.start(); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int newAccuracy) { + // Do nothing... + } } diff --git a/src/com/audacious_software/passive_data_kit/generators/sensors/AmbientLight.java b/src/com/audacious_software/passive_data_kit/generators/sensors/AmbientLight.java index 4dbedf8..cba3bc2 100755 --- a/src/com/audacious_software/passive_data_kit/generators/sensors/AmbientLight.java +++ b/src/com/audacious_software/passive_data_kit/generators/sensors/AmbientLight.java @@ -1,5 +1,6 @@ package com.audacious_software.passive_data_kit.generators.sensors; +import android.content.ContentValues; import android.content.Context; import android.content.SharedPreferences; import android.database.Cursor; @@ -13,7 +14,6 @@ import android.os.Handler; import android.os.Looper; import android.support.v4.content.ContextCompat; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -24,7 +24,6 @@ import com.audacious_software.passive_data_kit.diagnostics.DiagnosticAction; import com.audacious_software.passive_data_kit.generators.Generator; import com.audacious_software.passive_data_kit.generators.Generators; -import com.audacious_software.passive_data_kit.generators.device.Battery; import com.audacious_software.pdk.passivedatakit.R; import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.components.AxisBase; @@ -45,19 +44,21 @@ * Created by cjkarr on 4/17/2017. */ -public class AmbientLight extends Generator implements SensorEventListener { +public class AmbientLight extends SensorGenerator implements SensorEventListener { private static final String GENERATOR_IDENTIFIER = "pdk-sensor-light"; private static final String ENABLED = "com.audacious_software.passive_data_kit.generators.device.AmbientLight.ENABLED"; private static final boolean ENABLED_DEFAULT = true; - private static final String DATABASE_PATH = "pdk-sensor-light.sqlite"; + private static final String DATABASE_PATH = "pdk-sensor-ambient-light.sqlite"; private static final int DATABASE_VERSION = 1; public static final String TABLE_HISTORY = "history"; public static final String HISTORY_OBSERVED = "observed"; public static final String HISTORY_LEVEL = "light_level"; + private static final String HISTORY_RAW_TIMESTAMP = "raw_timestamp"; + private static final String HISTORY_ACCURACY = "accuracy"; private static AmbientLight sInstance = null; private static Handler sHandler = null; @@ -66,6 +67,23 @@ public class AmbientLight extends Generator implements SensorEventListener { private Sensor mSensor = null; + private static int NUM_BUFFERS = 3; + private static int BUFFER_SIZE = 32; + + private long mLastCleanup = 0; + private long mCleanupInterval = 15 * 60 * 1000; + + private int mActiveBuffersIndex = 0; + private int mCurrentBufferIndex = 0; + + private float[][] mValueBuffers = null; + private int[][] mAccuracyBuffers = null; + private long[][] mRawTimestampBuffers = null; + private long[][] mTimestampBuffers = null; + + long mBaseTimestamp = 0; + private long mLatestTimestamp = 0; + public static AmbientLight getInstance(Context context) { if (AmbientLight.sInstance == null) { AmbientLight.sInstance = new AmbientLight(context.getApplicationContext()); @@ -87,6 +105,23 @@ private void startGenerator() { final AmbientLight me = this; + Generators.getInstance(this.mContext).registerCustomViewClass(AmbientLight.GENERATOR_IDENTIFIER, AmbientLight.class); + + File path = PassiveDataKit.getGeneratorsStorage(this.mContext); + + path = new File(path, AmbientLight.DATABASE_PATH); + + this.mDatabase = SQLiteDatabase.openOrCreateDatabase(path, null); + + int version = this.getDatabaseVersion(this.mDatabase); + + switch (version) { + case 0: + this.mDatabase.execSQL(this.mContext.getString(R.string.pdk_generator_ambient_light_create_history_table)); + } + + this.setDatabaseVersion(this.mDatabase, AmbientLight.DATABASE_VERSION); + if (AmbientLight.isEnabled(this.mContext)) { this.mSensor = sensors.getDefaultSensor(Sensor.TYPE_LIGHT); @@ -96,12 +131,20 @@ public void run() { Looper.prepare(); + me.mValueBuffers = new float[AmbientLight.NUM_BUFFERS][AmbientLight.BUFFER_SIZE]; + me.mAccuracyBuffers = new int[AmbientLight.NUM_BUFFERS][AmbientLight.BUFFER_SIZE]; + me.mRawTimestampBuffers = new long[AmbientLight.NUM_BUFFERS][AmbientLight.BUFFER_SIZE]; + me.mTimestampBuffers = new long[AmbientLight.NUM_BUFFERS][AmbientLight.BUFFER_SIZE]; + + me.mActiveBuffersIndex = 0; + me.mCurrentBufferIndex = 0; + AmbientLight.sHandler = new Handler(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) - sensors.registerListener(me, me.mSensor, SensorManager.SENSOR_DELAY_NORMAL, 0, AmbientLight.sHandler); + sensors.registerListener(me, me.mSensor, SensorManager.SENSOR_DELAY_FASTEST, 0, AmbientLight.sHandler); else - sensors.registerListener(me, me.mSensor, SensorManager.SENSOR_DELAY_NORMAL, AmbientLight.sHandler); + sensors.registerListener(me, me.mSensor, SensorManager.SENSOR_DELAY_FASTEST, AmbientLight.sHandler); Looper.loop(); } @@ -111,6 +154,8 @@ public void run() t.start(); } else { if (this.mSensor != null) { + sensors.unregisterListener(this, this.mSensor); + if (AmbientLight.sHandler != null) { Looper loop = AmbientLight.sHandler.getLooper(); loop.quit(); @@ -118,30 +163,17 @@ public void run() AmbientLight.sHandler = null; } - sensors.unregisterListener(this, this.mSensor); + me.mValueBuffers = null; + me.mAccuracyBuffers = null; + me.mRawTimestampBuffers = null; + me.mTimestampBuffers = null; + + me.mActiveBuffersIndex = 0; + me.mCurrentBufferIndex = 0; this.mSensor = null; } } - - - Generators.getInstance(this.mContext).registerCustomViewClass(AmbientLight.GENERATOR_IDENTIFIER, Battery.class); - - File path = PassiveDataKit.getGeneratorsStorage(this.mContext); - - path = new File(path, AmbientLight.DATABASE_PATH); - - this.mDatabase = SQLiteDatabase.openOrCreateDatabase(path, null); - - int version = this.getDatabaseVersion(this.mDatabase); - - switch (version) { - case 0: -// -// this.mDatabase.execSQL(this.mContext.getString(R.string.pdk_generator_device_ambient_light_create_history_table)); - } - - this.setDatabaseVersion(this.mDatabase, AmbientLight.DATABASE_VERSION); } public static boolean isEnabled(Context context) { @@ -167,8 +199,8 @@ public static void bindViewHolder(DataPointViewHolder holder) { AmbientLight generator = AmbientLight.getInstance(context); - long now = System.currentTimeMillis(); - long start = now - (24 * 60 * 60 * 1000); + long now = System.currentTimeMillis() / (1000 * 60 * 5); + long start = now - (24 * 12); // * 60); String where = AmbientLight.HISTORY_OBSERVED + " >= ?"; String[] args = { "" + start }; @@ -183,13 +215,13 @@ public static void bindViewHolder(DataPointViewHolder holder) { cardContent.setVisibility(View.VISIBLE); cardEmpty.setVisibility(View.GONE); - long timestamp = c.getLong(c.getColumnIndex(AmbientLight.HISTORY_OBSERVED)) / 1000; + long timestamp = c.getLong(c.getColumnIndex(AmbientLight.HISTORY_OBSERVED)) / (1000 * 1000 * 1000); dateLabel.setText(Generator.formatTimestamp(context, timestamp)); c.moveToPrevious(); - final LineChart chart = (LineChart) holder.itemView.findViewById(R.id.battery_level_chart); + final LineChart chart = (LineChart) holder.itemView.findViewById(R.id.light_chart); chart.setViewPortOffsets(0,0,0,0); chart.setHighlightPerDragEnabled(false); chart.setHighlightPerTapEnabled(false); @@ -213,7 +245,7 @@ public static void bindViewHolder(DataPointViewHolder holder) { xAxis.setValueFormatter(new IAxisValueFormatter() { @Override public String getFormattedValue(float value, AxisBase axis) { - Date date = new Date((long) value); + Date date = new Date((long) value * 5 * 60 * 1000); return timeFormat.format(date); } @@ -224,8 +256,6 @@ public String getFormattedValue(float value, AxisBase axis) { leftAxis.setDrawGridLines(true); leftAxis.setDrawAxisLine(true); leftAxis.setGranularityEnabled(true); - leftAxis.setAxisMaximum(110); - leftAxis.setAxisMinimum(-10); leftAxis.setTextColor(ContextCompat.getColor(context, android.R.color.white)); YAxis rightAxis = chart.getAxisRight(); @@ -234,38 +264,88 @@ public String getFormattedValue(float value, AxisBase axis) { chart.getLegend().setEnabled(false); chart.getDescription().setEnabled(false); - ArrayList values = new ArrayList<>(); + ArrayList lowValues = new ArrayList<>(); + ArrayList highValues = new ArrayList<>(); - long lastLevel = -1; + long lastTimestamp = -1; + + float maxValue = 0; + float minValue = 0; + + float lowLevel = -1; + float highLevel = -1; while (c.moveToNext()) { long when = c.getLong(c.getColumnIndex(AmbientLight.HISTORY_OBSERVED)); - long level = c.getLong(c.getColumnIndex(AmbientLight.HISTORY_LEVEL)); - if (level != lastLevel) { - values.add(0, new Entry(when, level)); - lastLevel = level; + when = when / (1000 * 1000); + when = when / (1000 * 6 * 50); + + float level = c.getFloat(c.getColumnIndex(AmbientLight.HISTORY_LEVEL)); + + if (lastTimestamp != when) { + if (lastTimestamp != -1) { + lowValues.add(0, new Entry(lastTimestamp, lowLevel)); + highValues.add(0, new Entry(lastTimestamp, highLevel)); + } + + lastTimestamp = when; + lowLevel = level; + highLevel = level; + } else { + if (level < lowLevel) { + lowLevel = level; + } + + if (level > highLevel) { + highLevel = level; + } + } + + if (level > maxValue) { + maxValue = level; + } - Log.e("SLEEP-SIGHT", "VALUE: " + level + " -- " + (when - start)); + if (level < minValue) { + minValue = level; } } - Log.e("SLEEP-SIGHT", "LIGHT VALUES COUNT 2: " + values.size()); + if (lastTimestamp != -1) { + lowValues.add(0, new Entry(lastTimestamp, lowLevel)); + highValues.add(0, new Entry(lastTimestamp, highLevel)); + } - LineDataSet set = new LineDataSet(values, "Light Level"); + LineDataSet set = new LineDataSet(lowValues, "Low Light Levels"); set.setAxisDependency(YAxis.AxisDependency.LEFT); - set.setLineWidth(2.0f); + set.setLineWidth(1.0f); set.setDrawCircles(false); set.setFillAlpha(192); set.setDrawFilled(false); set.setDrawValues(true); - set.setColor(ContextCompat.getColor(context, R.color.generator_battery_plot)); + set.setColor(ContextCompat.getColor(context, R.color.generator_ambient_light_low)); set.setDrawCircleHole(false); set.setDrawValues(false); set.setMode(LineDataSet.Mode.LINEAR); - chart.setVisibleYRange(0, 120, YAxis.AxisDependency.LEFT); - chart.setData(new LineData(set)); + LineData chartData = new LineData(set); + + set = new LineDataSet(highValues, "High Light Levels"); + set.setAxisDependency(YAxis.AxisDependency.LEFT); + set.setLineWidth(1.0f); + set.setDrawCircles(false); + set.setFillAlpha(192); + set.setDrawFilled(false); + set.setDrawValues(true); + set.setColor(ContextCompat.getColor(context, R.color.generator_ambient_light_high)); + set.setDrawCircleHole(false); + set.setDrawValues(false); + set.setMode(LineDataSet.Mode.LINEAR); + + chartData.addDataSet(set); + + chart.setVisibleYRange((float) Math.floor(minValue) - 1, (float) Math.ceil(maxValue) + 1, YAxis.AxisDependency.LEFT); + chart.setData(chartData); } else { cardContent.setVisibility(View.GONE); cardEmpty.setVisibility(View.VISIBLE); @@ -278,7 +358,7 @@ public String getFormattedValue(float value, AxisBase axis) { public static View fetchView(ViewGroup parent) { - return LayoutInflater.from(parent.getContext()).inflate(R.layout.card_generator_device_battery, parent, false); + return LayoutInflater.from(parent.getContext()).inflate(R.layout.card_generator_sensors_ambient_light, parent, false); } @Override @@ -287,19 +367,19 @@ public List fetchPayloads() { } public static long latestPointGenerated(Context context) { - long timestamp = 0; - AmbientLight me = AmbientLight.getInstance(context); - Cursor c = me.mDatabase.query(AmbientLight.TABLE_HISTORY, null, null, null, null, null, AmbientLight.HISTORY_OBSERVED + " DESC"); + if (me.mLatestTimestamp == 0) { + Cursor c = me.mDatabase.query(AmbientLight.TABLE_HISTORY, null, null, null, null, null, Accelerometer.HISTORY_OBSERVED + " DESC"); - if (c.moveToNext()) { - timestamp = c.getLong(c.getColumnIndex(AmbientLight.HISTORY_OBSERVED)); - } + if (c.moveToNext()) { + me.mLatestTimestamp = c.getLong(c.getColumnIndex(Accelerometer.HISTORY_OBSERVED) / (1000 * 1000)); + } - c.close(); + c.close(); + } - return timestamp; + return me.mLatestTimestamp; } public Cursor queryHistory(String[] cols, String where, String[] args, String orderBy) { @@ -308,11 +388,98 @@ public Cursor queryHistory(String[] cols, String where, String[] args, String or @Override public void onSensorChanged(SensorEvent sensorEvent) { + long rawTimestamp = sensorEvent.timestamp; + + if (this.mBaseTimestamp == 0) { + this.mBaseTimestamp = (System.currentTimeMillis() * (1000 * 1000)) - rawTimestamp; + } + + int accuracy = sensorEvent.accuracy; + long normalizedTimestamp = this.mBaseTimestamp + rawTimestamp; + float value = sensorEvent.values[0]; + + if (this.mCurrentBufferIndex >= AmbientLight.BUFFER_SIZE) { + this.saveBuffer(this.mActiveBuffersIndex, this.mCurrentBufferIndex); + + this.mCurrentBufferIndex = 0; + this.mActiveBuffersIndex += 1; + + if (this.mActiveBuffersIndex >= AmbientLight.NUM_BUFFERS) { + this.mActiveBuffersIndex = 0; + } + } + this.mValueBuffers[this.mActiveBuffersIndex][this.mCurrentBufferIndex] = value; + this.mAccuracyBuffers[this.mActiveBuffersIndex][this.mCurrentBufferIndex] = accuracy; + this.mRawTimestampBuffers[this.mActiveBuffersIndex][this.mCurrentBufferIndex] = rawTimestamp; + this.mTimestampBuffers[this.mActiveBuffersIndex][this.mCurrentBufferIndex] = normalizedTimestamp; + + this.mCurrentBufferIndex += 1; + } + + private void saveBuffer(final int bufferIndex, final int bufferSize) { + final AmbientLight me = this; + + me.mLatestTimestamp = System.currentTimeMillis(); + + Runnable r = new Runnable() { + @Override + public void run() { + long now = System.currentTimeMillis(); + + try { + me.mDatabase.beginTransaction(); + + for (int i = 0; i < bufferSize; i++) { + ContentValues values = new ContentValues(); + + values.put(AmbientLight.HISTORY_LEVEL, me.mValueBuffers[bufferIndex][i]); + values.put(AmbientLight.HISTORY_OBSERVED, me.mTimestampBuffers[bufferIndex][i]); + values.put(AmbientLight.HISTORY_RAW_TIMESTAMP, me.mRawTimestampBuffers[bufferIndex][i]); + values.put(AmbientLight.HISTORY_ACCURACY, me.mAccuracyBuffers[bufferIndex][i]); + + me.mDatabase.insert(AmbientLight.TABLE_HISTORY, null, values); + } + + me.mDatabase.setTransactionSuccessful(); + } finally { + me.mDatabase.endTransaction(); + } + + Bundle update = new Bundle(); + update.putLong(AmbientLight.HISTORY_OBSERVED, now); + + Bundle sensorReadings = new Bundle(); + + sensorReadings.putFloatArray(AmbientLight.HISTORY_LEVEL, me.mValueBuffers[bufferIndex]); + sensorReadings.putLongArray(AmbientLight.HISTORY_RAW_TIMESTAMP, me.mRawTimestampBuffers[bufferIndex]); + sensorReadings.putLongArray(AmbientLight.HISTORY_OBSERVED, me.mTimestampBuffers[bufferIndex]); + sensorReadings.putIntArray(AmbientLight.HISTORY_ACCURACY, me.mAccuracyBuffers[bufferIndex]); + + update.putBundle(SensorGenerator.SENSOR_DATA, sensorReadings); + SensorGenerator.addSensorMetadata(update, me.mSensor); + + Generators.getInstance(me.mContext).notifyGeneratorUpdated(AmbientLight.GENERATOR_IDENTIFIER, update); + + if (now - me.mLastCleanup > me.mCleanupInterval) { + me.mLastCleanup = now; + + long start = (now - (2 * 24 * 60 * 60 * 1000)) * 1000 * 1000; + + String where = AmbientLight.HISTORY_OBSERVED + " < ?"; + String[] args = { "" + start }; + + me.mDatabase.delete(AmbientLight.TABLE_HISTORY, where, args); + } + } + }; + + Thread t = new Thread(r, "ambient-light-save-buffer"); + t.start(); } @Override public void onAccuracyChanged(Sensor sensor, int newAccuracy) { - + // Do nothing... } } diff --git a/src/com/audacious_software/passive_data_kit/generators/sensors/SensorGenerator.java b/src/com/audacious_software/passive_data_kit/generators/sensors/SensorGenerator.java new file mode 100755 index 0000000..947858b --- /dev/null +++ b/src/com/audacious_software/passive_data_kit/generators/sensors/SensorGenerator.java @@ -0,0 +1,74 @@ +package com.audacious_software.passive_data_kit.generators.sensors; + +import android.content.Context; +import android.hardware.Sensor; +import android.os.Build; +import android.os.Bundle; + +import com.audacious_software.passive_data_kit.generators.Generator; + +/** + * Created by cjkarr on 5/11/2017. + */ + +public abstract class SensorGenerator extends Generator { + public static final String SENSOR_DATA = "sensor_data"; + + private static final String SENSOR_REPORTING_MODE = "reporting_mode"; + private static final String SENSOR_NAME = "name"; + private static final String SENSOR_VENDOR = "vendor"; + private static final String SENSOR_MAX_RANGE = "max_range"; + private static final String SENSOR_POWER_USAGE = "power_usage"; + private static final String SENSOR_RESOLUTION = "resolution"; + private static final String SENSOR_MIN_DELAY = "min_delay"; + private static final String SENSOR_VERSION = "version"; + private static final String SENSOR_TYPE = "type"; + private static final String SENSOR_MAX_DELAY = "max_delay"; + private static final String SENSOR_IS_WAKEUP = "is_wakeup"; + + private static final String SENSOR_REPORTING_MODE_CONTINUOUS = "continuous"; + private static final String SENSOR_REPORTING_MODE_ON_CHANGE = "on_change"; + private static final String SENSOR_REPORTING_MODE_ONE_SHOT = "one_shot"; + private static final String SENSOR_REPORTING_MODE_SPECIAL_TRIGGER = "special_trigger"; + + public SensorGenerator(Context context) { + super(context); + } + + public static void addSensorMetadata(Bundle update, Sensor sensor) { + update.putString(SensorGenerator.SENSOR_NAME, sensor.getName()); + update.putString(SensorGenerator.SENSOR_VENDOR, sensor.getVendor()); + + update.putFloat(SensorGenerator.SENSOR_MAX_RANGE, sensor.getMaximumRange()); + update.putFloat(SensorGenerator.SENSOR_POWER_USAGE, sensor.getPower()); + update.putFloat(SensorGenerator.SENSOR_RESOLUTION, sensor.getResolution()); + + update.putInt(SensorGenerator.SENSOR_MIN_DELAY, sensor.getMinDelay()); + update.putInt(SensorGenerator.SENSOR_VERSION, sensor.getVersion()); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { + update.putString(SensorGenerator.SENSOR_TYPE, sensor.getStringType()); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + update.putInt(SensorGenerator.SENSOR_MAX_DELAY, sensor.getMaxDelay()); + + switch(sensor.getReportingMode()) { + case Sensor.REPORTING_MODE_CONTINUOUS: + update.putString(SensorGenerator.SENSOR_REPORTING_MODE, SensorGenerator.SENSOR_REPORTING_MODE_CONTINUOUS); + break; + case Sensor.REPORTING_MODE_ON_CHANGE: + update.putString(SensorGenerator.SENSOR_REPORTING_MODE, SensorGenerator.SENSOR_REPORTING_MODE_ON_CHANGE); + break; + case Sensor.REPORTING_MODE_ONE_SHOT: + update.putString(SensorGenerator.SENSOR_REPORTING_MODE, SensorGenerator.SENSOR_REPORTING_MODE_ONE_SHOT); + break; + case Sensor.REPORTING_MODE_SPECIAL_TRIGGER: + update.putString(SensorGenerator.SENSOR_REPORTING_MODE, SensorGenerator.SENSOR_REPORTING_MODE_SPECIAL_TRIGGER); + break; + } + + update.putBoolean(SensorGenerator.SENSOR_IS_WAKEUP, sensor.isWakeUpSensor()); + } + } + } +}