Compare commits
642 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fa5214c4bc | |||
| 84ad0d4072 | |||
| 9ed835007f | |||
| 67bc2884bb | |||
| 3b3280bdda | |||
| 4fcb4b8cb6 | |||
| 7d7a25f77c | |||
| 64f51aaa3c | |||
| f9b7b57e3a | |||
| 8155c96d61 | |||
| af6a62483a | |||
| e88ed5d551 | |||
| cfeb443255 | |||
| 9813ddc17e | |||
| fa37135471 | |||
| 90f4cc472a | |||
| c3254ecc46 | |||
| 68623b97c2 | |||
| ad707bf2a3 | |||
| f9e5a898a2 | |||
| eef997cc1b | |||
| 4fac6c1c08 | |||
| 781d95ab88 | |||
| 69c5c63e7b | |||
| f36a059306 | |||
| 2593b7ae99 | |||
| 512bed7505 | |||
| 91e8a9a370 | |||
| fd330506f2 | |||
| 600c8f0898 | |||
| 17219a81a2 | |||
| c7a0f893a4 | |||
| fbe97c7c53 | |||
| 1f9ea2351c | |||
| 99db33cbc4 | |||
| 1c52f9421b | |||
| 2ff60bfc91 | |||
| 784b9c266c | |||
| 87d9a720c8 | |||
| 07005babf4 | |||
| 6a877cf70a | |||
| 4358bd903f | |||
| f711545f8f | |||
| 591ee60003 | |||
| 34d2fec94e | |||
| 41e239fa2e | |||
| c05f998007 | |||
| 0559b095af | |||
| 8d95cf9c19 | |||
| 94333e65da | |||
| 6c9312ffd8 | |||
| 9e498684b2 | |||
| 43359003ca | |||
| 8c2b897a8e | |||
| 61a67a4e26 | |||
| 0f22145f37 | |||
| 1bd8764245 | |||
| a6aa2fbab7 | |||
| 43bd4897cd | |||
| ce52ed22cc | |||
| cac4c8bbeb | |||
| 54c3374f93 | |||
| 34d80c8842 | |||
| 25dec24ebf | |||
| 58b512172d | |||
| 3c49a02b1c | |||
| df31766f16 | |||
| bbfd809e84 | |||
| 2eee55747a | |||
| 06d1a23032 | |||
| e458ec5845 | |||
| 2bb1eed6f4 | |||
| f66aead4da | |||
| 614b1d7080 | |||
| 8649fa8223 | |||
| fb939304b9 | |||
| 3e23cd4b8b | |||
| f8632a371d | |||
| 097ff8dd63 | |||
| 22696a6d38 | |||
| 81acc7bbd9 | |||
| 98980500ec | |||
| 2ca06c44b2 | |||
| 06326d7ffe | |||
| bc7ea01397 | |||
| dbe906e7ec | |||
| bde087d3b3 | |||
| c3b353783f | |||
| 53bcccdc87 | |||
| 94b644ca6f | |||
| d25bdbe953 | |||
| 3af24d1828 | |||
| 117c84d06a | |||
| 180294ce71 | |||
| 6bc2d35a94 | |||
| 1fe84298a0 | |||
| 5e6442d3a0 | |||
| 8fbcb42170 | |||
| 63272ecbce | |||
| ea07a956b2 | |||
| be0df4d187 | |||
| 07f48c125b | |||
| b44cc8c072 | |||
| 5cd1562f6f | |||
| 2240780a5b | |||
| 01a2de8934 | |||
| fa966fa682 | |||
| 775e7f2520 | |||
| ff50ceb63e | |||
| b7b8b751f6 | |||
| fa5bbf3011 | |||
| 952fc8c7ff | |||
| e486bdef88 | |||
| 5047401252 | |||
| 26dd857f0a | |||
| 404e46f84b | |||
| 17679ff56e | |||
| b6b5f0accc | |||
| 6ce52bad33 | |||
| 8bf354e741 | |||
| e725a6514a | |||
| bd4bf8ec38 | |||
| 0736e68401 | |||
| f85ed88df3 | |||
| b6f4dbc345 | |||
| 853103ef1c | |||
| 622af54829 | |||
| 52f3d906a4 | |||
| 72217721cd | |||
| cde2e256f6 | |||
| 05355b639c | |||
| 0842a0abb1 | |||
| 6cdd9b81b6 | |||
| f3c0bce4ff | |||
| 54de137765 | |||
| b3bae6eebd | |||
| d2f6e1f0d0 | |||
| ecce58e5a1 | |||
| 40062a22a7 | |||
| 9d47a69ccd | |||
| 0ef6913118 | |||
| 66d978b37f | |||
| 324192edef | |||
| 1df428f956 | |||
| 6bcffa2368 | |||
| d9fe0c38bb | |||
| ec8a764c06 | |||
| 291fa77606 | |||
| 58b34e98a1 | |||
| b7b7cd26a4 | |||
| a6cbe2a5d0 | |||
| 0ffb0fd90b | |||
| db346ea955 | |||
| 1529cd3a88 | |||
| e8bd33b710 | |||
| 339541a0c8 | |||
| 20ba6f7bf0 | |||
| dcdc1e6686 | |||
| e09ae87e63 | |||
| 1958f073db | |||
| 1cae0db303 | |||
| 4cf953ec0f | |||
| 46f5cf6fd8 | |||
| 977dab78dc | |||
| 0855cabddf | |||
| 38326f25f4 | |||
| d97c70dee6 | |||
| 3545133d51 | |||
| f3f0d5540d | |||
| 46d1e57d81 | |||
| 49e9181001 | |||
| e22e1144fb | |||
| ddb3d459f5 | |||
| 3c75a76ae2 | |||
| 14743d1868 | |||
| 462cf41773 | |||
| 9d1918b361 | |||
| fac2badb47 | |||
| 9c17ac09ac | |||
| bfaac8ad2f | |||
| cb001e2f4e | |||
| c7a91a19a9 | |||
| 164a7a4415 | |||
| df71c79fe5 | |||
| 88a2b86d3f | |||
| 93b5df5541 | |||
| 492ce201f1 | |||
| d8c2bc8b55 | |||
| 8481b66de4 | |||
| 3462909cd7 | |||
| 16477efe6a | |||
| 4fa8de266c | |||
| 38b6310d10 | |||
| 7d833c8262 | |||
| 14aa32074e | |||
| 67fee928e0 | |||
| 94db7d392c | |||
| 38135994ab | |||
| 23246cbcbb | |||
| 267ff6e563 | |||
| d7fe76008a | |||
| 4a494bda70 | |||
| c50120d64f | |||
| d2cf8eba0a | |||
| e0ca725a42 | |||
| ce735a1835 | |||
| fb316bb744 | |||
| cc9f194a2d | |||
| c787d2a134 | |||
| 536d28f6d9 | |||
| f20d81ee3f | |||
| 0518fd777d | |||
| 9c1cf6c6bf | |||
| 51dc62a780 | |||
| fb5a7a3f48 | |||
| 3d91a933b9 | |||
| f1e75f2ce7 | |||
| 7dc37149f3 | |||
| c50e88b3c7 | |||
| 90a1b8407c | |||
| 46990ba807 | |||
| 6795808555 | |||
| 3b6262c4fe | |||
| 9659fbb530 | |||
| 3b45807b87 | |||
| 8ff919dc79 | |||
| df3883988b | |||
| 63ce90d4d3 | |||
| 74d403b52d | |||
| 3541f2b253 | |||
| a923c426ed | |||
| 2c2e5254bc | |||
| ce28538a36 | |||
| 1de5fcc6c2 | |||
| ee90b17351 | |||
| f8211adf83 | |||
| f5b440fd1f | |||
| 619e9434a9 | |||
| 2e01d033fb | |||
| dab4bcc047 | |||
| b68ad7b906 | |||
| b7a70253d6 | |||
| df04bf8235 | |||
| f56f6312e6 | |||
| 912631f4d8 | |||
| 61e8746cbd | |||
| 51d666f1e8 | |||
| 6e8006b094 | |||
| 5aabe018f0 | |||
| 62745570c5 | |||
| 9a49196cd6 | |||
| 9603fb8461 | |||
| 38cf7a4541 | |||
| 78cafb6a52 | |||
| 4725b15bcd | |||
| d929de736e | |||
| d4b73a8f42 | |||
| f0a9e7d443 | |||
| 55198e7dad | |||
| be0c887b85 | |||
| 9c1bbbbaac | |||
| d1126e228c | |||
| 623e33841f | |||
| 3dedf8c373 | |||
| c42f29fdad | |||
| 5d47c8e183 | |||
| a56b351593 | |||
| fd6e941159 | |||
| 115591c032 | |||
| 152557e5e4 | |||
| f16bba3076 | |||
| 21427d5795 | |||
| 972879a85e | |||
| ae4af75736 | |||
| b738688f00 | |||
| fc81624880 | |||
| a622726b1b | |||
| caec12f51c | |||
| d090892754 | |||
| e1f54278ca | |||
| 7a26e368e7 | |||
| ec63648083 | |||
| 806c7917d3 | |||
| 546b965c5b | |||
| efb78baf7d | |||
| 4cd9bd8122 | |||
| 37ca7a3704 | |||
| 236a4026ce | |||
| 3e8bf44aea | |||
| 978a585cde | |||
| aa5219cef6 | |||
| c20126a9ef | |||
| 466dbb98c5 | |||
| 9169f9730f | |||
| 94c902d721 | |||
| 29c2829df0 | |||
| 075dc1f7b6 | |||
| 8b4294f0dd | |||
| 27397735f4 | |||
| e3c596ddb1 | |||
| b948c08d8f | |||
| 5dd23fbb54 | |||
| 9ff7710b14 | |||
| 40e50fb0e6 | |||
| 5ba33f832d | |||
| c34852d677 | |||
| 46bd241ca3 | |||
| b45628d543 | |||
| 9ea4786e21 | |||
| 6b03670808 | |||
| d4d874e312 | |||
| 6abc57b566 | |||
| d5fd81fd50 | |||
| bfdebd914c | |||
| 19fce1ac39 | |||
| 117545160e | |||
| 703155eb72 | |||
| c86bc22f71 | |||
| 31b792a795 | |||
| 70c8ef2e30 | |||
| 833f81296a | |||
| bdf032553d | |||
| 03cb3606c5 | |||
| 4d5f3307e5 | |||
| 9498cda223 | |||
| b338926f56 | |||
| 75fdaf6b09 | |||
| 2959667c65 | |||
| db1f2a9ba5 | |||
| 1d4d307344 | |||
| ac399005ba | |||
| 0d45511b15 | |||
| 19b5b34cc4 | |||
| 2e7c58f48a | |||
| 2a99e7d54f | |||
| 378f5140de | |||
| 3027038744 | |||
| 09e8b1d494 | |||
| 4881a32004 | |||
| 9832cbc91f | |||
| 5bc04aca3f | |||
| 6030efaa96 | |||
| 0604f586df | |||
| e59576640a | |||
| 3d44ed80c6 | |||
| 3378ab16f2 | |||
| 8a5b6cc28d | |||
| cc959e266f | |||
| 5e222351dc | |||
| cf3ea70f77 | |||
| 71c97a72b6 | |||
| 4639a41eea | |||
| 965b39fe4a | |||
| 4a55ec228c | |||
| 14dd7a7c73 | |||
| 1b0d93e247 | |||
| 4047451e4d | |||
| f602e85b75 | |||
| 3ad592e27c | |||
| 7ab47f3a88 | |||
| fe82617dc5 | |||
| ed740fe7d3 | |||
| 621652b26f | |||
| a0adc6c557 | |||
| 227e5af903 | |||
| 01d2c72c2d | |||
| fcb9fb61b0 | |||
| 75c05d200b | |||
| 9e12bed600 | |||
| b3c64fc66b | |||
| edb78414ec | |||
| 1d89b53c78 | |||
| 706244f95d | |||
| 643be7ee8b | |||
| 0ba31ce525 | |||
| efc1394987 | |||
| 0997555a34 | |||
| b48ac27ec8 | |||
| dc35088d39 | |||
| 139a3f6d00 | |||
| 4c25cc50ee | |||
| a48796942c | |||
| 136471090e | |||
| a27ecaa20a | |||
| 31ea7908a2 | |||
| 2930653a98 | |||
| 9604154b17 | |||
| 709dec2841 | |||
| e3cca38a69 | |||
| 66f5452a35 | |||
| 9b4f0a404d | |||
| dbdabb49c2 | |||
| ac1cf8e16e | |||
| 226791c774 | |||
| 08a378b6b8 | |||
| 71008fcc05 | |||
| d50d647ffd | |||
| 1a8f9e81bd | |||
| a200c992ae | |||
| 91821ca5ae | |||
| a739df5f69 | |||
| 5cd75fc585 | |||
| 28a54aac7c | |||
| 35841e2ba8 | |||
| 77bb256cfa | |||
| 74ab1b3726 | |||
| 49256010d5 | |||
| e15f56ff6e | |||
| 3ed725399c | |||
| 3e48fc66a8 | |||
| ae6fdc65d2 | |||
| 74a3f4b589 | |||
| 2df6cc99e1 | |||
| 713b27060f | |||
| 8cb90d81c1 | |||
| 1f3ea67c73 | |||
| d18907ea56 | |||
| ef2eec6926 | |||
| 553c675eab | |||
| b8e24f456d | |||
| 4a518c8d9a | |||
| eb30a43ac6 | |||
| 47babbc318 | |||
| 711fed4ff5 | |||
| 0c2bdc488f | |||
| 4cbd40f0d7 | |||
| 0df2d9a98b | |||
| 6d1e80a583 | |||
| daa7f7cd52 | |||
| 4f792442b1 | |||
| 1e8002279d | |||
| 9eb1d445bb | |||
| 1b1954e9ca | |||
| 46236a4a08 | |||
| ee2127397b | |||
| ac1dd33c34 | |||
| 8586381fb6 | |||
| e79c7d25d4 | |||
| 21dba1f124 | |||
| 3251a5226c | |||
| ccf6b461bc | |||
| 45215ecb0e | |||
| 439df0718b | |||
| 3c3eb5cb02 | |||
| 6a895b4b5c | |||
| 720aed5388 | |||
| e2950decc2 | |||
| f9e119acad | |||
| bbda212a04 | |||
| 891c096bc1 | |||
| cec9c3acf6 | |||
| 2612aca236 | |||
| 2272d2e6db | |||
| ed8960f8b5 | |||
| 0f8a07fd8f | |||
| 93c9c8d85a | |||
| 25c543c468 | |||
| fb519862b8 | |||
| 681891b300 | |||
| 1c5b93cae6 | |||
| 8a7ea2d051 | |||
| 12148d0973 | |||
| acd1f4379c | |||
| 2a7d12381f | |||
| 2541fa2835 | |||
| ca43fbf341 | |||
| 745fb12b9b | |||
| edb33fa975 | |||
| 0984008494 | |||
| ca0b2b2a5c | |||
| a4fad3df74 | |||
| 0c6b420744 | |||
| 9aa69cd1ef | |||
| 6bf1e4aec5 | |||
| cfe35edfef | |||
| cdff825c7b | |||
| 881f01a2d5 | |||
| 793deeec7d | |||
| 3d5abb0b7a | |||
| ed88a71af0 | |||
| aed6935765 | |||
| 022fe854b0 | |||
| 4039555bbc | |||
| 0453c28748 | |||
| a8affe5e06 | |||
| e5e2f5810e | |||
| c9983cae47 | |||
| dcca7bf200 | |||
| a08f8ee42d | |||
| 2f81b9d31d | |||
| adfe0147ab | |||
| f750ccff78 | |||
| 95634467f6 | |||
| d9b0e5dfed | |||
| 59ac68442f | |||
| 483cf7cd11 | |||
| 3817139626 | |||
| 4ec792498e | |||
| ad87af34cf | |||
| 52550021a9 | |||
| 686fb2458a | |||
| 62cff57325 | |||
| 9877ec749e | |||
| 22d0de778b | |||
| 598b275a1a | |||
| f0675d2646 | |||
| 15be84da3b | |||
| 553b27735b | |||
| c7d27e2e0a | |||
| 83eb2a5766 | |||
| 5ecb0490a9 | |||
| ecac08f098 | |||
| 3db44a194d | |||
| fc0f58e664 | |||
| f9b46de2be | |||
| c6df59cfbb | |||
| 5f3c5ccd7d | |||
| f4c30882c7 | |||
| aa150c1fa7 | |||
| f4949f859d | |||
| 4fe283f294 | |||
| f78bbece03 | |||
| d77dc73201 | |||
| 3d39c73a37 | |||
| 7142e9e4eb | |||
| 94a47a4b3a | |||
| 18988376aa | |||
| 37673a096b | |||
| 44f6355977 | |||
| c0d84dc4a9 | |||
| d9739e0843 | |||
| 47261f181c | |||
| 447594d36c | |||
| 8c39fcf935 | |||
| fbbfac9a23 | |||
| 49e79ab9f4 | |||
| fc558417ac | |||
| d58785fcf7 | |||
| 4362e13ebc | |||
| 7d4853a197 | |||
| e0e216eecc | |||
| a2140aff06 | |||
| 99bc310088 | |||
| 5f52f13833 | |||
| 7b1725a955 | |||
| f349bd9b7f | |||
| b66fa14873 | |||
| 56bd6301b1 | |||
| 073d712324 | |||
| d3dba26b79 | |||
| 2fc27d7bda | |||
| a3d3fbb439 | |||
| f5e0594c75 | |||
| 96f83191dd | |||
| fe40138b12 | |||
| 4376582b4a | |||
| d313e3f7a5 | |||
| 6d39c5e744 | |||
| 96e22ba0c2 | |||
| 5a9bb2f8fd | |||
| 5de3d1ec8b | |||
| de7ec25b28 | |||
| 9849beded4 | |||
| 1503c05d6e | |||
| 6f517f89cf | |||
| d56fc3e1bd | |||
| e6b5a96c48 | |||
| 228da9c789 | |||
| 75ece11fc1 | |||
| 9044b794a3 | |||
| a2ff2bd393 | |||
| f2ada1a01c | |||
| 10ca52e63d | |||
| 627399a78c | |||
| 58b544d5df | |||
| 422d3afb41 | |||
| e5f5119bba | |||
| 44036cb73e | |||
| ac4f40a902 | |||
| 954a2a05a6 | |||
| 34b481f68c | |||
| 00aa3212b4 | |||
| b7824b1851 | |||
| 9d97a29104 | |||
| 4726d6fdd7 | |||
| bd1d1ca3e6 | |||
| 390c52e5e8 | |||
| 37b30537e9 | |||
| 4aac2f9010 | |||
| 2c29f029c4 | |||
| 175679468e | |||
| 5f288877d0 | |||
| 3faffe89ea | |||
| 648b382fe9 | |||
| 077e1bb94b | |||
| 17ae36d939 | |||
| ddcb2ce4f0 | |||
| 5953891a7c | |||
| fd2ab4b4a3 | |||
| b923899ced | |||
| 87fd1537d7 | |||
| 3cb3b72c2d | |||
| e270c9812d | |||
| 0b5f1d7082 | |||
| f7f7c73ccc | |||
| d4bd7b07d7 | |||
| bd89f14b7e | |||
| 9b5ee80420 | |||
| a2ab9874ab | |||
| c1593b312d | |||
| aeddc8fea9 | |||
| 9a03d12817 | |||
| 54e147c969 | |||
| 9f0bdfd2fd | |||
| 4ddf07f106 | |||
| eae5f56040 | |||
| 120d063bdf | |||
| 1d8182db32 | |||
| 946753ea91 | |||
| 4757edc130 | |||
| 19460998b6 | |||
| 1f0307aa63 | |||
| e477f4e38b | |||
| 06125bb24d | |||
| 4ff80ee4c2 | |||
| f61f4e66db | |||
| c11ae6d95e | |||
| 064d4d0a03 | |||
| fd84004c37 | |||
| 33880857a0 | |||
| 9b6decc643 | |||
| d81f303959 | |||
| e4f8af52eb | |||
| 208249a3b9 | |||
| 71373cc46f | |||
| aa8186e175 | |||
| cabe77ca67 | |||
| cdcda16714 | |||
| 9c75263ac5 | |||
| 3d08b355bd | |||
| c5b1640343 | |||
| f22c425b25 |
@@ -18,6 +18,4 @@ DerivedData
|
||||
*.xcuserstate
|
||||
|
||||
.DS_Store
|
||||
|
||||
StudyDemo/ResearchKit
|
||||
*.pyc
|
||||
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
Contributing to the ResearchKit Framework
|
||||
===========================
|
||||
|
||||
This page focuses on code contributions to the existing
|
||||
codebase. However, other types of contributions are welcome too, in
|
||||
keeping with the ResearchKit™ framework [best practices](../../wiki/best-practices). For example,
|
||||
contributions of original free-to-use survey content, back-end integrations,
|
||||
validation data, and analysis or processing tools are all welcome. Ask
|
||||
on [researchkit-dev](https://lists.apple.com/mailman/listinfo/researchkit-dev) or [contact us](https://developer.apple.com/contact/researchkit/) for guidance.
|
||||
|
||||
|
||||
Contributing software
|
||||
---------------------
|
||||
|
||||
This page assumes you already know how to check out and build the
|
||||
code. Contributions to the ResearchKit framework are expected to comply with the
|
||||
[ResearchKit Contribution Terms and License Policy](#contribution); please familiarize yourself
|
||||
with this policy prior to submitting a pull request. For any contribution, ensure that you own
|
||||
the rights or have permission from the copyright holder. (e.g. code, images, surveys, videos
|
||||
and other content you may include)
|
||||
|
||||
To contribute to ResearchKit:
|
||||
|
||||
1. [Choose or create an issue to work on.](#create)
|
||||
2. [Create a personal fork of the ResearchKit framework.](#fork)
|
||||
3. [Develop your changes in your fork.](#develop)
|
||||
4. [Run the tests.](#test)
|
||||
5. [Submit a pull request.](#request)
|
||||
6. Make any changes requested by the reviewer, and update your pull request as needed.
|
||||
7. Once accepted, your pull request will be merged into master.
|
||||
|
||||
Choosing an issue to work on<a name="create"></a>
|
||||
----------------------------
|
||||
|
||||
To find an issue to work on, either pick something that you need for
|
||||
your app, or select one of the issues from our [issue list](../../issues). Or,
|
||||
consider one of the areas where we'd like to extend ResearchKit:
|
||||
|
||||
* Faster 'get started' to a useful app
|
||||
* More active tasks
|
||||
* Data analysis for active tasks
|
||||
* More consent sections
|
||||
* Back end integrations
|
||||
|
||||
If in doubt, bring your idea up on [researchkit-dev](https://lists.apple.com/mailman/listinfo/researchkit-dev).
|
||||
|
||||
|
||||
Creating a personal fork<a name="fork"></a>
|
||||
------------------------
|
||||
|
||||
On GitHub, it's easy to create a personal fork. Just tap the "Fork"
|
||||
button on the top right, and clone your new repository.
|
||||
|
||||
|
||||
Develop your changes in your fork<a name="develop"></a>
|
||||
---------------------------------
|
||||
|
||||
Develop your changes using your normal development process. If you
|
||||
already have code from an existing project, you may need to adjust its
|
||||
style to more closely match that in the ResearchKit framework.
|
||||
|
||||
New components may need to expose new Public or Private
|
||||
headers. Public headers are for APIs that are likely to be a stable
|
||||
part of the interface of the ResearchKit framework. Private headers are for APIs that
|
||||
may need to be accessed from app-side unit tests, or that are more
|
||||
subject to change than the public interface. All other headers should
|
||||
be internal, "Project" headers.
|
||||
|
||||
Please review and ensure that any contributions you make comply with
|
||||
the [ResearchKit Contribution Terms and License Policy](#contribution).
|
||||
|
||||
Add automated tests for your feature, where it is possible to do
|
||||
so. For UI driven components where it is harder to write automated
|
||||
tests, add UI to at least one test application so that the new
|
||||
features can be reviewed and tested. Consider also whether to add new
|
||||
code to other existing demo apps to exercise your feature.
|
||||
|
||||
Keep changes that fix different issues separate. For bug fixes,
|
||||
separate bugs should be submitted as separate pull requests. A good
|
||||
way to do this is to create a new branch in your fork for each new
|
||||
bug work on.
|
||||
|
||||
Any new user-visible strings should be included in the English
|
||||
`Localizable.strings` table so that they can be picked up and
|
||||
localized in the next release cycle.
|
||||
|
||||
|
||||
Run the tests<a name="test"></a>
|
||||
-------------
|
||||
|
||||
All unit tests should pass, and there should be no warnings. Also
|
||||
verify that test apps run on both device and simulator.
|
||||
|
||||
Where your code affects UI presentation, also test:
|
||||
|
||||
* Multiple device form factors (for instance, iPhone 4S, iPhone 5, iPhone 6, iPhone 6 Plus).
|
||||
* Dynamic text, especially at the "Large" setting.
|
||||
* Rotation between portrait and landscape, where appropriate.
|
||||
|
||||
You can use the apps in the `Testing` and `samples` directories to
|
||||
test your changes.
|
||||
|
||||
Submit a pull request<a name="request"></a>
|
||||
---------------------
|
||||
|
||||
The reviewers may request changes. Make the changes, and update your
|
||||
pull request as needed. Reviews will focus on coding style,
|
||||
correctness, and design consistency.
|
||||
|
||||
This process does not take the place of an ethical review, for example
|
||||
by an institutional review board (IRB) or ethics committee.
|
||||
|
||||
After acceptance<a name="after"></a>
|
||||
----------------
|
||||
|
||||
Once your pull request has been accepted, your changes will be merged
|
||||
to master. You are still responsible for your change after it is
|
||||
accepted. Stay in contact, in case bugs are detected that may require
|
||||
your attention.
|
||||
|
||||
When the project is next branched for release, your changes will be
|
||||
incorporated. Queries may come back to you regarding localization,
|
||||
documentation, or other issues during this process.
|
||||
|
||||
|
||||
|
||||
|
||||
Release process
|
||||
-----------------
|
||||
|
||||
The `master` branch is used for work in progress. On `master`:
|
||||
|
||||
* All test apps should build and run error free.
|
||||
* Unit tests should all pass.
|
||||
* Everything should be continuously in working order in English (the
|
||||
base language).
|
||||
|
||||
The project will make periodic releases. When preparing a stable release, we
|
||||
will branch from `master` to a convergence branch. During this process,
|
||||
changes will be made first to the convergence branch, and then merged into
|
||||
`master`. On the convergence branch, changes will be made only to:
|
||||
|
||||
* Fix high priority issues.
|
||||
* Update documentation.
|
||||
* Bring localization up to date.
|
||||
* Ensure good behavior across all supported devices.
|
||||
|
||||
After the converging process is completed, we will merge everything to the
|
||||
`stable` branch and tag with a new release number. The most recent release
|
||||
will be highlighted in the [README](../..).
|
||||
|
||||
|
||||
ResearchKit Contribution Terms and License Policy<a name="contribution"></a>
|
||||
=======================================
|
||||
|
||||
Thank you for your interest in contributing to the ResearchKit
|
||||
community. In order to maintain consistency and license compatibility
|
||||
throughout the project, all contributions must comply with our
|
||||
licensing policy and terms for contributing code to the ResearchKit
|
||||
project:
|
||||
|
||||
1. If you are submitting a patch to the existing codebase, you
|
||||
represent that you have the right to license the patch to the
|
||||
community, and agree by submitting the patch that your changes are
|
||||
licensed under the existing license terms of the file you are
|
||||
modifying (i.e., [ResearchKit BSD license](LICENSE)).
|
||||
Please also add your copyright (name and year) to the relevant
|
||||
files for changes that are more than 10 lines of code.
|
||||
2. If you are submitting a new file for inclusion in the ResearchKit framework (no
|
||||
code copied from another source), please include your copyright (name
|
||||
and year) and a copy of the ResearchKit BSD license. By submitting
|
||||
your new file you represent that you have the right to license your
|
||||
file to the community, and agree that your file is licensed under the
|
||||
ResearchKit BSD license.
|
||||
3. If you aren't the author of the patch, you agree to include the
|
||||
original copyright notices and licensing terms with it, to the extent
|
||||
that they exist. If there wasn't a copyright notice or license,
|
||||
please make a note of it. Generally we can only take in patches that
|
||||
are BSD-licensed in order to maintain license compatibility within the
|
||||
project.
|
||||
@@ -5,10 +5,10 @@ The ResearchKit™ framework is an open source software framework that makes it
|
||||
create apps for medical research or for other research projects.
|
||||
|
||||
* Getting Started: [Getting Started](#gettingstarted)
|
||||
* Documentation: ([Programming Guide](http://researchkit.org/docs/docs/Overview/GuideOverview.html)) ([API](http://researchkit.org/docs/index.html))
|
||||
* Best practices: [Best Practices](../../wiki/best_practices)
|
||||
* Contributing to ResearchKit: [Contributing](../../wiki/contributing)
|
||||
* Website and blog: ([researchkit.org](http://researchkit.org/index.html)) ([Blog](http://researchkit.org/blog.html))
|
||||
* Documentation: ([Programming Guide](http://researchkit.github.io/docs/docs/Overview/GuideOverview.html)) ([API](http://researchkit.github.io/docs/index.html))
|
||||
* Best practices: [Best Practices](../../wiki/best-practices)
|
||||
* Contributing to ResearchKit: [Contributing](CONTRIBUTING.md)
|
||||
* Website and blog: ([researchkit.org](http://researchkit.github.io/index.html)) ([Blog](http://researchkit.github.io/blog.html))
|
||||
* ResearchKit BSD License: [License](#license)
|
||||
|
||||
Getting More Information
|
||||
@@ -18,7 +18,7 @@ Getting More Information
|
||||
* Join [researchkit-dev](https://lists.apple.com/mailman/listinfo/researchkit-dev) for discussing ongoing work to improve and expand the framework.
|
||||
* Or [contact us](https://developer.apple.com/contact/researchkit/)
|
||||
|
||||
Use cases
|
||||
Use Cases
|
||||
===========
|
||||
|
||||
A task in the ResearchKit framework contains a set of steps to present to a
|
||||
@@ -29,14 +29,14 @@ Surveys
|
||||
-------
|
||||
|
||||
The ResearchKit framework provides a pre-built user interface for surveys, which can be
|
||||
presented modally on an iPhone, iPod Touch, or iPad. [Surveys](http://researchkit.org/docs/docs/Survey/CreatingSurveys.html)
|
||||
presented modally on an iPhone, iPod Touch, or iPad. [Surveys](http://researchkit.github.io/docs/docs/Survey/CreatingSurveys.html)
|
||||
|
||||
|
||||
Consent
|
||||
----------------
|
||||
|
||||
The ResearchKit framework provides visual consent templates that you can customize to
|
||||
explain the details of your research study and obtain a signature if needed. [Consent](http://researchkit.org/docs/docs/InformedConsent/InformedConsent.html)
|
||||
explain the details of your research study and obtain a signature if needed. [Consent](http://researchkit.github.io/docs/docs/InformedConsent/InformedConsent.html)
|
||||
|
||||
|
||||
Active Tasks
|
||||
@@ -45,10 +45,10 @@ Active Tasks
|
||||
Some studies may need data beyond survey questions or the passive data collection
|
||||
capabilities available through use of the HealthKit and CoreMotion APIs if you are
|
||||
programming for iOS. ResearchKit's active tasks invite users to perform activities
|
||||
under semi-controlled conditions, while iPhone sensors actively collect data. [Active Tasks](http://researchkit.org/docs/docs/ActiveTasks/ActiveTasks.html)
|
||||
under semi-controlled conditions, while iPhone sensors actively collect data. [Active Tasks](http://researchkit.github.io/docs/docs/ActiveTasks/ActiveTasks.html)
|
||||
|
||||
|
||||
Getting started<a name="gettingstarted"></a>
|
||||
Getting Started<a name="gettingstarted"></a>
|
||||
===============
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ using the ResearchKit framework can run on devices with iOS 8.0 or newer.
|
||||
Installation
|
||||
------------
|
||||
|
||||
The lastest stable version of ResearchKit framework can be cloned with
|
||||
The latest stable version of ResearchKit framework can be cloned with
|
||||
|
||||
```
|
||||
git clone -b stable https://github.com/ResearchKit/ResearchKit.git
|
||||
@@ -110,6 +110,8 @@ target as shown in the figure below.
|
||||
</figure>
|
||||
</center>
|
||||
|
||||
Note: You can also import ResearchKit into your project using a [dependency manager](./dependency_management.md) such as CocoaPods or Carthage.
|
||||
|
||||
###2. Create a Step
|
||||
|
||||
In this walk-through, we will use the ResearchKit framework to modally present a
|
||||
@@ -120,24 +122,41 @@ Create a step for your task by adding some code, perhaps in
|
||||
simple, we'll use an instruction step (`ORKInstructionStep`) and name
|
||||
the step `myStep`.
|
||||
|
||||
*Objective-C*
|
||||
|
||||
```objc
|
||||
ORKInstructionStep *myStep =
|
||||
[[ORKInstructionStep alloc] initWithIdentifier:@"intro"];
|
||||
myStep.title = @"Welcome to ResearchKit";
|
||||
```
|
||||
|
||||
*Swift*
|
||||
|
||||
```swift
|
||||
let myStep = ORKInstructionStep(identifier: "intro")
|
||||
myStep.title = "Welcome to ResearchKit"
|
||||
```
|
||||
|
||||
###3. Create a Task
|
||||
|
||||
Use the ordered task class (`ORKOrderedTask`) to create a task that
|
||||
contains myStep. An ordered task is just a task where the order and
|
||||
contains `myStep`. An ordered task is just a task where the order and
|
||||
selection of later steps does not depend on the results of earlier
|
||||
ones. Name your task `task` and initialize it with `myStep`.
|
||||
|
||||
*Objective-C*
|
||||
|
||||
```objc
|
||||
ORKOrderedTask *task =
|
||||
[[ORKOrderedTask alloc] initWithIdentifier:@"task" steps:@[myStep]];
|
||||
```
|
||||
|
||||
*Swift*
|
||||
|
||||
```swift
|
||||
let task = ORKOrderedTask(identifier: "task", steps: [myStep])
|
||||
```
|
||||
|
||||
###4. Present the Task
|
||||
|
||||
Create a task view controller (`ORKTaskViewController`) and initialize
|
||||
@@ -145,6 +164,8 @@ it with your `task`. A task view controller manages a task and collects the
|
||||
results of each step. In this case, your task view
|
||||
controller simply displays your instruction step.
|
||||
|
||||
*Objective-C*
|
||||
|
||||
```objc
|
||||
ORKTaskViewController *taskViewController =
|
||||
[[ORKTaskViewController alloc] initWithTask:task taskRunUUID:nil];
|
||||
@@ -152,10 +173,20 @@ taskViewController.delegate = self;
|
||||
[self presentViewController:taskViewController animated:YES completion:nil];
|
||||
```
|
||||
|
||||
*Swift*
|
||||
|
||||
```swift
|
||||
let taskViewController = ORKTaskViewController(task: task, taskRunUUID: nil)
|
||||
taskViewController.delegate = self
|
||||
presentViewController(taskViewController, animated: true, completion: nil)
|
||||
```
|
||||
|
||||
The above snippet assumes that your class implements the
|
||||
`ORKTaskViewControllerDelegate` protocol. This has just one required method,
|
||||
which you must implement in order to handle the completion of the task:
|
||||
|
||||
*Objective-C*
|
||||
|
||||
```objc
|
||||
- (void)taskViewController:(ORKTaskViewController *)taskViewController
|
||||
didFinishWithReason:(ORKTaskViewControllerFinishReason)reason
|
||||
@@ -169,6 +200,20 @@ which you must implement in order to handle the completion of the task:
|
||||
}
|
||||
```
|
||||
|
||||
*Swift*
|
||||
|
||||
```swift
|
||||
func taskViewController(taskViewController: ORKTaskViewController,
|
||||
didFinishWithReason reason: ORKTaskViewControllerFinishReason,
|
||||
error: NSError?) {
|
||||
let taskResult = taskViewController.result
|
||||
// You could do something with the result here.
|
||||
|
||||
// Then, dismiss the task view controller.
|
||||
dismissViewControllerAnimated(true, completion: nil)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
If you now run your app, you should see your first ResearchKit framework
|
||||
instruction step:
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'ResearchKit'
|
||||
s.version = '1.1.2'
|
||||
s.summary = 'ResearchKit is an open source software framework that makes it easy to create apps for medical research or for other research projects.'
|
||||
s.homepage = 'https://www.github.com/ResearchKit/ResearchKit'
|
||||
s.documentation_url = 'http://researchkit.github.io/docs/'
|
||||
s.license = { :type => 'BSD', :file => 'LICENSE' }
|
||||
s.author = { 'researchkit.org' => 'http://researchkit.org' }
|
||||
s.source = { :git => 'https://github.com/ResearchKit/ResearchKit.git', :tag => s.version.to_s }
|
||||
s.public_header_files = `./scripts/find_headers.rb --public --private`.split("\n")
|
||||
s.source_files = 'ResearchKit/**/*.{h,m}'
|
||||
s.resources = 'ResearchKit/**/*.{fsh,vsh}', 'ResearchKit/Animations/**/*.m4v', 'ResearchKit/Artwork.xcassets', 'ResearchKit/Localized/*.lproj'
|
||||
s.platform = :ios, '8.0'
|
||||
s.requires_arc = true
|
||||
end
|
||||
@@ -22,6 +22,24 @@
|
||||
/* End PBXAggregateTarget section */
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
147503AF1AEE8071004B17F3 /* ORKAudioGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 147503AD1AEE8071004B17F3 /* ORKAudioGenerator.h */; };
|
||||
147503B01AEE8071004B17F3 /* ORKAudioGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 147503AE1AEE8071004B17F3 /* ORKAudioGenerator.m */; };
|
||||
147503B71AEE807C004B17F3 /* ORKToneAudiometryContentView.h in Headers */ = {isa = PBXBuildFile; fileRef = 147503B11AEE807C004B17F3 /* ORKToneAudiometryContentView.h */; };
|
||||
147503B81AEE807C004B17F3 /* ORKToneAudiometryContentView.m in Sources */ = {isa = PBXBuildFile; fileRef = 147503B21AEE807C004B17F3 /* ORKToneAudiometryContentView.m */; };
|
||||
147503B91AEE807C004B17F3 /* ORKToneAudiometryStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 147503B31AEE807C004B17F3 /* ORKToneAudiometryStep.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
147503BA1AEE807C004B17F3 /* ORKToneAudiometryStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 147503B41AEE807C004B17F3 /* ORKToneAudiometryStep.m */; };
|
||||
147503BB1AEE807C004B17F3 /* ORKToneAudiometryStepViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 147503B51AEE807C004B17F3 /* ORKToneAudiometryStepViewController.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
147503BC1AEE807C004B17F3 /* ORKToneAudiometryStepViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 147503B61AEE807C004B17F3 /* ORKToneAudiometryStepViewController.m */; };
|
||||
25ECC0951AFBD68300F3D63B /* ORKReactionTimeStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 25ECC0931AFBD68300F3D63B /* ORKReactionTimeStep.h */; };
|
||||
25ECC0961AFBD68300F3D63B /* ORKReactionTimeStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 25ECC0941AFBD68300F3D63B /* ORKReactionTimeStep.m */; };
|
||||
25ECC09B1AFBD8B300F3D63B /* ORKReactionTimeViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 25ECC0991AFBD8B300F3D63B /* ORKReactionTimeViewController.h */; };
|
||||
25ECC09C1AFBD8B300F3D63B /* ORKReactionTimeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 25ECC09A1AFBD8B300F3D63B /* ORKReactionTimeViewController.m */; };
|
||||
25ECC09F1AFBD92D00F3D63B /* ORKReactionTimeContentView.h in Headers */ = {isa = PBXBuildFile; fileRef = 25ECC09D1AFBD92D00F3D63B /* ORKReactionTimeContentView.h */; };
|
||||
25ECC0A01AFBD92D00F3D63B /* ORKReactionTimeContentView.m in Sources */ = {isa = PBXBuildFile; fileRef = 25ECC09E1AFBD92D00F3D63B /* ORKReactionTimeContentView.m */; };
|
||||
25ECC0A31AFBDD2700F3D63B /* ORKReactionTimeStimulusView.h in Headers */ = {isa = PBXBuildFile; fileRef = 25ECC0A11AFBDD2700F3D63B /* ORKReactionTimeStimulusView.h */; };
|
||||
25ECC0A41AFBDD2700F3D63B /* ORKReactionTimeStimulusView.m in Sources */ = {isa = PBXBuildFile; fileRef = 25ECC0A21AFBDD2700F3D63B /* ORKReactionTimeStimulusView.m */; };
|
||||
2EBFE11D1AE1B32D00CB8254 /* ORKUIViewAccessibilityTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2EBFE11C1AE1B32D00CB8254 /* ORKUIViewAccessibilityTests.m */; };
|
||||
2EBFE1201AE1B74100CB8254 /* ORKVoiceEngineTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2EBFE11F1AE1B74100CB8254 /* ORKVoiceEngineTests.m */; };
|
||||
618DA04E1A93D0D600E63AA8 /* ORKAccessibility.h in Headers */ = {isa = PBXBuildFile; fileRef = 618DA0481A93D0D600E63AA8 /* ORKAccessibility.h */; };
|
||||
618DA0501A93D0D600E63AA8 /* ORKAccessibilityFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = 618DA0491A93D0D600E63AA8 /* ORKAccessibilityFunctions.h */; };
|
||||
618DA0521A93D0D600E63AA8 /* ORKAccessibilityFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = 618DA04A1A93D0D600E63AA8 /* ORKAccessibilityFunctions.m */; };
|
||||
@@ -268,8 +286,6 @@
|
||||
86C40DDE1A8D7C5C00081FAC /* ORKVerticalContainerView.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40BE11A8D7C5C00081FAC /* ORKVerticalContainerView.h */; };
|
||||
86C40DE01A8D7C5C00081FAC /* ORKVerticalContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40BE21A8D7C5C00081FAC /* ORKVerticalContainerView.m */; };
|
||||
86C40DE21A8D7C5C00081FAC /* ORKVerticalContainerView_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40BE31A8D7C5C00081FAC /* ORKVerticalContainerView_Internal.h */; };
|
||||
86C40DE61A8D7C5C00081FAC /* UIApplication+ResearchKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40BE51A8D7C5C00081FAC /* UIApplication+ResearchKit.h */; };
|
||||
86C40DE81A8D7C5C00081FAC /* UIApplication+ResearchKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40BE61A8D7C5C00081FAC /* UIApplication+ResearchKit.m */; };
|
||||
86C40DEA1A8D7C5C00081FAC /* UIBarButtonItem+ORKBarButtonItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40BE71A8D7C5C00081FAC /* UIBarButtonItem+ORKBarButtonItem.h */; };
|
||||
86C40DEC1A8D7C5C00081FAC /* UIBarButtonItem+ORKBarButtonItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40BE81A8D7C5C00081FAC /* UIBarButtonItem+ORKBarButtonItem.m */; };
|
||||
86C40DEE1A8D7C5C00081FAC /* UIResponder+ResearchKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40BE91A8D7C5C00081FAC /* UIResponder+ResearchKit.h */; };
|
||||
@@ -291,8 +307,6 @@
|
||||
86C40E0E1A8D7C5C00081FAC /* ORKConsentReviewStepViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40BFA1A8D7C5C00081FAC /* ORKConsentReviewStepViewController.m */; };
|
||||
86C40E101A8D7C5C00081FAC /* ORKConsentSceneViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40BFB1A8D7C5C00081FAC /* ORKConsentSceneViewController.h */; };
|
||||
86C40E121A8D7C5C00081FAC /* ORKConsentSceneViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40BFC1A8D7C5C00081FAC /* ORKConsentSceneViewController.m */; };
|
||||
86C40E141A8D7C5C00081FAC /* ORKConsentSection+AssetLoading.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40BFD1A8D7C5C00081FAC /* ORKConsentSection+AssetLoading.h */; };
|
||||
86C40E161A8D7C5C00081FAC /* ORKConsentSection+AssetLoading.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40BFE1A8D7C5C00081FAC /* ORKConsentSection+AssetLoading.m */; };
|
||||
86C40E181A8D7C5C00081FAC /* ORKConsentSection.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40BFF1A8D7C5C00081FAC /* ORKConsentSection.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
86C40E1A1A8D7C5C00081FAC /* ORKConsentSection.m in Sources */ = {isa = PBXBuildFile; fileRef = 86C40C001A8D7C5C00081FAC /* ORKConsentSection.m */; };
|
||||
86C40E1C1A8D7C5C00081FAC /* ORKConsentSection_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C40C011A8D7C5C00081FAC /* ORKConsentSection_Internal.h */; };
|
||||
@@ -323,6 +337,10 @@
|
||||
B11C549B1A9EEF8800265E61 /* ORKConsentSharingStep.m in Sources */ = {isa = PBXBuildFile; fileRef = B11C54971A9EEF8800265E61 /* ORKConsentSharingStep.m */; };
|
||||
B11C549F1A9EF4A700265E61 /* ORKConsentSharingStepViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = B11C549C1A9EF4A700265E61 /* ORKConsentSharingStepViewController.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
B11C54A11A9EF4A700265E61 /* ORKConsentSharingStepViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B11C549D1A9EF4A700265E61 /* ORKConsentSharingStepViewController.m */; };
|
||||
B12EA0151B0D73A500F9F554 /* ORKToneAudiometryPracticeStep.h in Headers */ = {isa = PBXBuildFile; fileRef = B12EA0131B0D73A500F9F554 /* ORKToneAudiometryPracticeStep.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
B12EA0161B0D73A500F9F554 /* ORKToneAudiometryPracticeStep.m in Sources */ = {isa = PBXBuildFile; fileRef = B12EA0141B0D73A500F9F554 /* ORKToneAudiometryPracticeStep.m */; };
|
||||
B12EA0191B0D76AD00F9F554 /* ORKToneAudiometryPracticeStepViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = B12EA0171B0D76AD00F9F554 /* ORKToneAudiometryPracticeStepViewController.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
B12EA01A1B0D76AD00F9F554 /* ORKToneAudiometryPracticeStepViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B12EA0181B0D76AD00F9F554 /* ORKToneAudiometryPracticeStepViewController.m */; };
|
||||
B17FE7FD1A8DBE7C00BF9C28 /* Artwork.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 861610BF1A8D8EDD00245F7A /* Artwork.xcassets */; };
|
||||
B183A4A21A8535D100C76870 /* ResearchKit_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = B1B894391A00345200C5CF2D /* ResearchKit_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
B183A4DD1A8535D100C76870 /* ResearchKit.h in Headers */ = {isa = PBXBuildFile; fileRef = B1C1DE4F196F541F00F75544 /* ResearchKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
@@ -343,6 +361,37 @@
|
||||
B1A860F71A9693C400EA57B7 /* consent_07@3x.m4v in Resources */ = {isa = PBXBuildFile; fileRef = B1A860E91A9693C400EA57B7 /* consent_07@3x.m4v */; };
|
||||
B1C0F4E41A9BA65F0022C153 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = B1C0F4E11A9BA65F0022C153 /* Localizable.strings */; };
|
||||
B1C7955E1A9FBF04007279BA /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B1C7955D1A9FBF04007279BA /* HealthKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
|
||||
B8760F2B1AFBEFB0007FA16F /* ORKScaleRangeDescriptionLabel.h in Headers */ = {isa = PBXBuildFile; fileRef = B8760F291AFBEFB0007FA16F /* ORKScaleRangeDescriptionLabel.h */; };
|
||||
B8760F2C1AFBEFB0007FA16F /* ORKScaleRangeDescriptionLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = B8760F2A1AFBEFB0007FA16F /* ORKScaleRangeDescriptionLabel.m */; };
|
||||
BC01B0FB1B0EB99700863803 /* ORKTintedImageView_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = BC01B0FA1B0EB99700863803 /* ORKTintedImageView_Internal.h */; };
|
||||
BC13CE391B0660220044153C /* ORKNavigableOrderedTask.h in Headers */ = {isa = PBXBuildFile; fileRef = BC13CE371B0660220044153C /* ORKNavigableOrderedTask.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
BC13CE3A1B0660220044153C /* ORKNavigableOrderedTask.m in Sources */ = {isa = PBXBuildFile; fileRef = BC13CE381B0660220044153C /* ORKNavigableOrderedTask.m */; };
|
||||
BC13CE3C1B0662990044153C /* ORKStepNavigationRule_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = BC13CE3B1B0662990044153C /* ORKStepNavigationRule_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
BC13CE3E1B0662A80044153C /* ORKOrderedTask_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = BC13CE3D1B0662A80044153C /* ORKOrderedTask_Internal.h */; };
|
||||
BC13CE401B0666FD0044153C /* ORKResultPredicate.h in Headers */ = {isa = PBXBuildFile; fileRef = BC13CE3F1B0666FD0044153C /* ORKResultPredicate.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
BC13CE421B066A990044153C /* ORKStepNavigationRule_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = BC13CE411B066A990044153C /* ORKStepNavigationRule_Internal.h */; };
|
||||
BC4194291AE8453A00073D6B /* ORKObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = BC4194271AE8453A00073D6B /* ORKObserver.h */; };
|
||||
BC41942A1AE8453A00073D6B /* ORKObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = BC4194281AE8453A00073D6B /* ORKObserver.m */; };
|
||||
BCA5C0351AEC05F20092AC8D /* ORKStepNavigationRule.h in Headers */ = {isa = PBXBuildFile; fileRef = BCA5C0331AEC05F20092AC8D /* ORKStepNavigationRule.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
BCA5C0361AEC05F20092AC8D /* ORKStepNavigationRule.m in Sources */ = {isa = PBXBuildFile; fileRef = BCA5C0341AEC05F20092AC8D /* ORKStepNavigationRule.m */; };
|
||||
BCAD50E81B0201EE0034806A /* ORKTaskTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCAD50E71B0201EE0034806A /* ORKTaskTests.m */; };
|
||||
BCB96C131B19C0EC002A0B96 /* ORKStepTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCB96C121B19C0EC002A0B96 /* ORKStepTests.m */; };
|
||||
BCFF24BD1B0798D10044EC35 /* ORKResultPredicate.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFF24BC1B0798D10044EC35 /* ORKResultPredicate.m */; };
|
||||
D42FEFB81AF7557000A124F8 /* ORKImageCaptureView.h in Headers */ = {isa = PBXBuildFile; fileRef = D42FEFB61AF7557000A124F8 /* ORKImageCaptureView.h */; };
|
||||
D42FEFB91AF7557000A124F8 /* ORKImageCaptureView.m in Sources */ = {isa = PBXBuildFile; fileRef = D42FEFB71AF7557000A124F8 /* ORKImageCaptureView.m */; };
|
||||
D44239791AF17F5100559D96 /* ORKImageCaptureStep.h in Headers */ = {isa = PBXBuildFile; fileRef = D44239771AF17F5100559D96 /* ORKImageCaptureStep.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
D442397A1AF17F5100559D96 /* ORKImageCaptureStep.m in Sources */ = {isa = PBXBuildFile; fileRef = D44239781AF17F5100559D96 /* ORKImageCaptureStep.m */; };
|
||||
D442397D1AF17F7600559D96 /* ORKImageCaptureStepViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = D442397B1AF17F7600559D96 /* ORKImageCaptureStepViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
D442397E1AF17F7600559D96 /* ORKImageCaptureStepViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D442397C1AF17F7600559D96 /* ORKImageCaptureStepViewController.m */; };
|
||||
D458520A1AF6CCFA00A2DE13 /* ORKImageCaptureCameraPreviewView.h in Headers */ = {isa = PBXBuildFile; fileRef = D45852081AF6CCFA00A2DE13 /* ORKImageCaptureCameraPreviewView.h */; };
|
||||
D458520B1AF6CCFA00A2DE13 /* ORKImageCaptureCameraPreviewView.m in Sources */ = {isa = PBXBuildFile; fileRef = D45852091AF6CCFA00A2DE13 /* ORKImageCaptureCameraPreviewView.m */; };
|
||||
FA7A9D2B1B082688005A2BEA /* ORKConsentDocumentTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FA7A9D2A1B082688005A2BEA /* ORKConsentDocumentTests.m */; };
|
||||
FA7A9D2F1B083DD3005A2BEA /* ORKConsentSectionFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = FA7A9D2D1B083DD3005A2BEA /* ORKConsentSectionFormatter.h */; };
|
||||
FA7A9D301B083DD3005A2BEA /* ORKConsentSectionFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = FA7A9D2E1B083DD3005A2BEA /* ORKConsentSectionFormatter.m */; };
|
||||
FA7A9D331B0843A9005A2BEA /* ORKConsentSignatureFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = FA7A9D311B0843A9005A2BEA /* ORKConsentSignatureFormatter.h */; };
|
||||
FA7A9D341B0843A9005A2BEA /* ORKConsentSignatureFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = FA7A9D321B0843A9005A2BEA /* ORKConsentSignatureFormatter.m */; };
|
||||
FA7A9D371B09365F005A2BEA /* ORKConsentSectionFormatterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FA7A9D361B09365F005A2BEA /* ORKConsentSectionFormatterTests.m */; };
|
||||
FA7A9D391B0969A7005A2BEA /* ORKConsentSignatureFormatterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FA7A9D381B0969A7005A2BEA /* ORKConsentSignatureFormatterTests.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -363,6 +412,25 @@
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
147503AD1AEE8071004B17F3 /* ORKAudioGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKAudioGenerator.h; sourceTree = "<group>"; };
|
||||
147503AE1AEE8071004B17F3 /* ORKAudioGenerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKAudioGenerator.m; sourceTree = "<group>"; };
|
||||
147503B11AEE807C004B17F3 /* ORKToneAudiometryContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKToneAudiometryContentView.h; sourceTree = "<group>"; };
|
||||
147503B21AEE807C004B17F3 /* ORKToneAudiometryContentView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKToneAudiometryContentView.m; sourceTree = "<group>"; };
|
||||
147503B31AEE807C004B17F3 /* ORKToneAudiometryStep.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKToneAudiometryStep.h; sourceTree = "<group>"; };
|
||||
147503B41AEE807C004B17F3 /* ORKToneAudiometryStep.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKToneAudiometryStep.m; sourceTree = "<group>"; };
|
||||
147503B51AEE807C004B17F3 /* ORKToneAudiometryStepViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKToneAudiometryStepViewController.h; sourceTree = "<group>"; };
|
||||
147503B61AEE807C004B17F3 /* ORKToneAudiometryStepViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKToneAudiometryStepViewController.m; sourceTree = "<group>"; };
|
||||
25ECC0931AFBD68300F3D63B /* ORKReactionTimeStep.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKReactionTimeStep.h; sourceTree = "<group>"; };
|
||||
25ECC0941AFBD68300F3D63B /* ORKReactionTimeStep.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKReactionTimeStep.m; sourceTree = "<group>"; };
|
||||
25ECC0991AFBD8B300F3D63B /* ORKReactionTimeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKReactionTimeViewController.h; sourceTree = "<group>"; };
|
||||
25ECC09A1AFBD8B300F3D63B /* ORKReactionTimeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKReactionTimeViewController.m; sourceTree = "<group>"; };
|
||||
25ECC09D1AFBD92D00F3D63B /* ORKReactionTimeContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKReactionTimeContentView.h; sourceTree = "<group>"; };
|
||||
25ECC09E1AFBD92D00F3D63B /* ORKReactionTimeContentView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKReactionTimeContentView.m; sourceTree = "<group>"; };
|
||||
25ECC0A11AFBDD2700F3D63B /* ORKReactionTimeStimulusView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKReactionTimeStimulusView.h; sourceTree = "<group>"; };
|
||||
25ECC0A21AFBDD2700F3D63B /* ORKReactionTimeStimulusView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKReactionTimeStimulusView.m; sourceTree = "<group>"; };
|
||||
2EBFE11C1AE1B32D00CB8254 /* ORKUIViewAccessibilityTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKUIViewAccessibilityTests.m; sourceTree = "<group>"; };
|
||||
2EBFE11E1AE1B68800CB8254 /* ORKVoiceEngine_Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ORKVoiceEngine_Internal.h; sourceTree = "<group>"; };
|
||||
2EBFE11F1AE1B74100CB8254 /* ORKVoiceEngineTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKVoiceEngineTests.m; sourceTree = "<group>"; };
|
||||
618DA0481A93D0D600E63AA8 /* ORKAccessibility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKAccessibility.h; sourceTree = "<group>"; };
|
||||
618DA0491A93D0D600E63AA8 /* ORKAccessibilityFunctions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKAccessibilityFunctions.h; sourceTree = "<group>"; };
|
||||
618DA04A1A93D0D600E63AA8 /* ORKAccessibilityFunctions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKAccessibilityFunctions.m; sourceTree = "<group>"; };
|
||||
@@ -611,8 +679,6 @@
|
||||
86C40BE11A8D7C5C00081FAC /* ORKVerticalContainerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKVerticalContainerView.h; sourceTree = "<group>"; };
|
||||
86C40BE21A8D7C5C00081FAC /* ORKVerticalContainerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ORKVerticalContainerView.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
|
||||
86C40BE31A8D7C5C00081FAC /* ORKVerticalContainerView_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKVerticalContainerView_Internal.h; sourceTree = "<group>"; };
|
||||
86C40BE51A8D7C5C00081FAC /* UIApplication+ResearchKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "UIApplication+ResearchKit.h"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
|
||||
86C40BE61A8D7C5C00081FAC /* UIApplication+ResearchKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = "UIApplication+ResearchKit.m"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
|
||||
86C40BE71A8D7C5C00081FAC /* UIBarButtonItem+ORKBarButtonItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "UIBarButtonItem+ORKBarButtonItem.h"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
|
||||
86C40BE81A8D7C5C00081FAC /* UIBarButtonItem+ORKBarButtonItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = "UIBarButtonItem+ORKBarButtonItem.m"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
|
||||
86C40BE91A8D7C5C00081FAC /* UIResponder+ResearchKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "UIResponder+ResearchKit.h"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
|
||||
@@ -634,8 +700,6 @@
|
||||
86C40BFA1A8D7C5C00081FAC /* ORKConsentReviewStepViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ORKConsentReviewStepViewController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
|
||||
86C40BFB1A8D7C5C00081FAC /* ORKConsentSceneViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKConsentSceneViewController.h; sourceTree = "<group>"; };
|
||||
86C40BFC1A8D7C5C00081FAC /* ORKConsentSceneViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ORKConsentSceneViewController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
|
||||
86C40BFD1A8D7C5C00081FAC /* ORKConsentSection+AssetLoading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "ORKConsentSection+AssetLoading.h"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
|
||||
86C40BFE1A8D7C5C00081FAC /* ORKConsentSection+AssetLoading.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = "ORKConsentSection+AssetLoading.m"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
|
||||
86C40BFF1A8D7C5C00081FAC /* ORKConsentSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKConsentSection.h; sourceTree = "<group>"; };
|
||||
86C40C001A8D7C5C00081FAC /* ORKConsentSection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKConsentSection.m; sourceTree = "<group>"; };
|
||||
86C40C011A8D7C5C00081FAC /* ORKConsentSection_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKConsentSection_Internal.h; sourceTree = "<group>"; };
|
||||
@@ -669,8 +733,12 @@
|
||||
B11C549C1A9EF4A700265E61 /* ORKConsentSharingStepViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKConsentSharingStepViewController.h; sourceTree = "<group>"; };
|
||||
B11C549D1A9EF4A700265E61 /* ORKConsentSharingStepViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ORKConsentSharingStepViewController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
|
||||
B11DF3B31AA109C8009E76D2 /* AppledocSettings.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = AppledocSettings.plist; sourceTree = "<group>"; };
|
||||
B11DF4C21AA10D70009E76D2 /* tr */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B14660481AA10DD7002F95C2 /* zh_TW */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = zh_TW; path = zh_TW.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B11DF4C21AA10D70009E76D2 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B12EA0131B0D73A500F9F554 /* ORKToneAudiometryPracticeStep.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKToneAudiometryPracticeStep.h; sourceTree = "<group>"; };
|
||||
B12EA0141B0D73A500F9F554 /* ORKToneAudiometryPracticeStep.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKToneAudiometryPracticeStep.m; sourceTree = "<group>"; };
|
||||
B12EA0171B0D76AD00F9F554 /* ORKToneAudiometryPracticeStepViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKToneAudiometryPracticeStepViewController.h; sourceTree = "<group>"; };
|
||||
B12EA0181B0D76AD00F9F554 /* ORKToneAudiometryPracticeStepViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKToneAudiometryPracticeStepViewController.m; sourceTree = "<group>"; };
|
||||
B14660481AA10DD7002F95C2 /* zh_TW */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = zh_TW; path = zh_TW.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B183A5951A8535D100C76870 /* ResearchKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ResearchKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B18AABE01A9F08D9003871B5 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
|
||||
B1A860DB1A9693C400EA57B7 /* consent_01@2x.m4v */ = {isa = PBXFileReference; lastKnownFileType = file; path = "consent_01@2x.m4v"; sourceTree = "<group>"; };
|
||||
@@ -687,46 +755,78 @@
|
||||
B1A860E71A9693C400EA57B7 /* consent_05@3x.m4v */ = {isa = PBXFileReference; lastKnownFileType = file; path = "consent_05@3x.m4v"; sourceTree = "<group>"; };
|
||||
B1A860E81A9693C400EA57B7 /* consent_06@3x.m4v */ = {isa = PBXFileReference; lastKnownFileType = file; path = "consent_06@3x.m4v"; sourceTree = "<group>"; };
|
||||
B1A860E91A9693C400EA57B7 /* consent_07@3x.m4v */ = {isa = PBXFileReference; lastKnownFileType = file; path = "consent_07@3x.m4v"; sourceTree = "<group>"; };
|
||||
B1B349E41AA10DED005FAD66 /* zh_HK */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = zh_HK; path = zh_HK.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349E51AA10DF8005FAD66 /* zh_CN */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = zh_CN; path = zh_CN.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349E61AA10E02005FAD66 /* vi */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = vi; path = vi.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349E71AA10E0B005FAD66 /* uk */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = uk; path = uk.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349E81AA10E12005FAD66 /* th */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = th; path = th.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349E91AA10E27005FAD66 /* sv */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349EA1AA10E2E005FAD66 /* sk */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = sk; path = sk.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349EB1AA10E38005FAD66 /* ru */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349EC1AA10E40005FAD66 /* ro */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = ro; path = ro.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349ED1AA10E47005FAD66 /* pt */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = pt; path = pt.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349EE1AA10E4F005FAD66 /* pt_PT */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = pt_PT; path = pt_PT.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349EF1AA10E56005FAD66 /* pl */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349F01AA10E5E005FAD66 /* nl */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349F11AA10E65005FAD66 /* ms */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = ms; path = ms.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349F21AA10E6C005FAD66 /* ko */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = ko; path = ko.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349F31AA10E73005FAD66 /* ja */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349F41AA10E79005FAD66 /* it */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349F51AA10E80005FAD66 /* id */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = id; path = id.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349F61AA10E89005FAD66 /* hu */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = hu; path = hu.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349F71AA10E90005FAD66 /* hr */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = hr; path = hr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349F81AA10E96005FAD66 /* hi */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = hi; path = hi.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349F91AA10E9C005FAD66 /* he */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = he; path = he.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349FA1AA10EA2005FAD66 /* fr */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349FB1AA10EA8005FAD66 /* fr_CA */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = fr_CA; path = fr_CA.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349FC1AA10EAE005FAD66 /* fi */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349FD1AA10EB4005FAD66 /* es */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349FE1AA10EBA005FAD66 /* es_MX */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = es_MX; path = es_MX.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349FF1AA10EC1005FAD66 /* en_GB */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = en_GB; path = en_GB.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B34A001AA10EC6005FAD66 /* en_AU */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = en_AU; path = en_AU.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B34A011AA10ECB005FAD66 /* el */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = el; path = el.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B34A021AA10ED1005FAD66 /* de */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B34A031AA10ED5005FAD66 /* da */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = da; path = da.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B34A041AA10EDA005FAD66 /* cs */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = cs; path = cs.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B34A051AA10EDF005FAD66 /* ca */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = ca; path = ca.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B34A061AA10EE4005FAD66 /* ar */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = ar; path = ar.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B34A061AABBCCDDEEFFAAA /* no */ = {isa = PBXFileReference; explicitFileType = file.bplist; name = no; path = no.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349E41AA10DED005FAD66 /* zh_HK */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = zh_HK; path = zh_HK.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349E51AA10DF8005FAD66 /* zh_CN */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = zh_CN; path = zh_CN.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349E61AA10E02005FAD66 /* vi */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349E71AA10E0B005FAD66 /* uk */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349E81AA10E12005FAD66 /* th */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = th; path = th.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349E91AA10E27005FAD66 /* sv */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349EA1AA10E2E005FAD66 /* sk */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = sk; path = sk.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349EB1AA10E38005FAD66 /* ru */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349EC1AA10E40005FAD66 /* ro */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = ro; path = ro.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349ED1AA10E47005FAD66 /* pt */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = pt; path = pt.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349EE1AA10E4F005FAD66 /* pt_PT */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = pt_PT; path = pt_PT.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349EF1AA10E56005FAD66 /* pl */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349F01AA10E5E005FAD66 /* nl */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349F11AA10E65005FAD66 /* ms */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = ms; path = ms.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349F21AA10E6C005FAD66 /* ko */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349F31AA10E73005FAD66 /* ja */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349F41AA10E79005FAD66 /* it */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349F51AA10E80005FAD66 /* id */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = id; path = id.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349F61AA10E89005FAD66 /* hu */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349F71AA10E90005FAD66 /* hr */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = hr; path = hr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349F81AA10E96005FAD66 /* hi */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = hi; path = hi.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349F91AA10E9C005FAD66 /* he */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = he; path = he.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349FA1AA10EA2005FAD66 /* fr */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349FB1AA10EA8005FAD66 /* fr_CA */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = fr_CA; path = fr_CA.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349FC1AA10EAE005FAD66 /* fi */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349FD1AA10EB4005FAD66 /* es */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349FE1AA10EBA005FAD66 /* es_MX */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = es_MX; path = es_MX.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B349FF1AA10EC1005FAD66 /* en_GB */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = en_GB; path = en_GB.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B34A001AA10EC6005FAD66 /* en_AU */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = en_AU; path = en_AU.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B34A011AA10ECB005FAD66 /* el */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = el; path = el.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B34A021AA10ED1005FAD66 /* de */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B34A031AA10ED5005FAD66 /* da */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B34A041AA10EDA005FAD66 /* cs */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B34A051AA10EDF005FAD66 /* ca */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B34A061AA10EE4005FAD66 /* ar */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B34A061AABBCCDDEEFFAAA /* no */ = {isa = PBXFileReference; explicitFileType = text.plist.strings; name = no; path = no.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1B894391A00345200C5CF2D /* ResearchKit_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ResearchKit_Private.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
|
||||
B1C0F4E21A9BA65F0022C153 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
B1C1DE4F196F541F00F75544 /* ResearchKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResearchKit.h; sourceTree = "<group>"; };
|
||||
B1C7955D1A9FBF04007279BA /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = System/Library/Frameworks/HealthKit.framework; sourceTree = SDKROOT; };
|
||||
B8760F291AFBEFB0007FA16F /* ORKScaleRangeDescriptionLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKScaleRangeDescriptionLabel.h; sourceTree = "<group>"; };
|
||||
B8760F2A1AFBEFB0007FA16F /* ORKScaleRangeDescriptionLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKScaleRangeDescriptionLabel.m; sourceTree = "<group>"; };
|
||||
BC01B0FA1B0EB99700863803 /* ORKTintedImageView_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKTintedImageView_Internal.h; sourceTree = "<group>"; };
|
||||
BC13CE371B0660220044153C /* ORKNavigableOrderedTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKNavigableOrderedTask.h; sourceTree = "<group>"; };
|
||||
BC13CE381B0660220044153C /* ORKNavigableOrderedTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKNavigableOrderedTask.m; sourceTree = "<group>"; };
|
||||
BC13CE3B1B0662990044153C /* ORKStepNavigationRule_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKStepNavigationRule_Private.h; sourceTree = "<group>"; };
|
||||
BC13CE3D1B0662A80044153C /* ORKOrderedTask_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKOrderedTask_Internal.h; sourceTree = "<group>"; };
|
||||
BC13CE3F1B0666FD0044153C /* ORKResultPredicate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKResultPredicate.h; sourceTree = "<group>"; };
|
||||
BC13CE411B066A990044153C /* ORKStepNavigationRule_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKStepNavigationRule_Internal.h; sourceTree = "<group>"; };
|
||||
BC4194271AE8453A00073D6B /* ORKObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKObserver.h; sourceTree = "<group>"; };
|
||||
BC4194281AE8453A00073D6B /* ORKObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKObserver.m; sourceTree = "<group>"; };
|
||||
BCA5C0331AEC05F20092AC8D /* ORKStepNavigationRule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKStepNavigationRule.h; sourceTree = "<group>"; };
|
||||
BCA5C0341AEC05F20092AC8D /* ORKStepNavigationRule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKStepNavigationRule.m; sourceTree = "<group>"; };
|
||||
BCAD50E71B0201EE0034806A /* ORKTaskTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKTaskTests.m; sourceTree = "<group>"; };
|
||||
BCB96C121B19C0EC002A0B96 /* ORKStepTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKStepTests.m; sourceTree = "<group>"; };
|
||||
BCFB2EAF1AE70E4E0070B5D0 /* ORKConsentSceneViewController_Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ORKConsentSceneViewController_Internal.h; sourceTree = "<group>"; };
|
||||
BCFF24BC1B0798D10044EC35 /* ORKResultPredicate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKResultPredicate.m; sourceTree = "<group>"; };
|
||||
D42FEFB61AF7557000A124F8 /* ORKImageCaptureView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKImageCaptureView.h; sourceTree = "<group>"; };
|
||||
D42FEFB71AF7557000A124F8 /* ORKImageCaptureView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKImageCaptureView.m; sourceTree = "<group>"; };
|
||||
D44239771AF17F5100559D96 /* ORKImageCaptureStep.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKImageCaptureStep.h; sourceTree = "<group>"; };
|
||||
D44239781AF17F5100559D96 /* ORKImageCaptureStep.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKImageCaptureStep.m; sourceTree = "<group>"; };
|
||||
D442397B1AF17F7600559D96 /* ORKImageCaptureStepViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKImageCaptureStepViewController.h; sourceTree = "<group>"; };
|
||||
D442397C1AF17F7600559D96 /* ORKImageCaptureStepViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKImageCaptureStepViewController.m; sourceTree = "<group>"; };
|
||||
D45852081AF6CCFA00A2DE13 /* ORKImageCaptureCameraPreviewView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKImageCaptureCameraPreviewView.h; sourceTree = "<group>"; };
|
||||
D45852091AF6CCFA00A2DE13 /* ORKImageCaptureCameraPreviewView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKImageCaptureCameraPreviewView.m; sourceTree = "<group>"; };
|
||||
FA7A9D2A1B082688005A2BEA /* ORKConsentDocumentTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKConsentDocumentTests.m; sourceTree = "<group>"; };
|
||||
FA7A9D2D1B083DD3005A2BEA /* ORKConsentSectionFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKConsentSectionFormatter.h; sourceTree = "<group>"; };
|
||||
FA7A9D2E1B083DD3005A2BEA /* ORKConsentSectionFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKConsentSectionFormatter.m; sourceTree = "<group>"; };
|
||||
FA7A9D311B0843A9005A2BEA /* ORKConsentSignatureFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ORKConsentSignatureFormatter.h; sourceTree = "<group>"; };
|
||||
FA7A9D321B0843A9005A2BEA /* ORKConsentSignatureFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKConsentSignatureFormatter.m; sourceTree = "<group>"; };
|
||||
FA7A9D361B09365F005A2BEA /* ORKConsentSectionFormatterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKConsentSectionFormatterTests.m; sourceTree = "<group>"; };
|
||||
FA7A9D381B0969A7005A2BEA /* ORKConsentSignatureFormatterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ORKConsentSignatureFormatterTests.m; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -749,6 +849,40 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
147503AC1AEE8058004B17F3 /* Tone Audiometry */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
147503AD1AEE8071004B17F3 /* ORKAudioGenerator.h */,
|
||||
147503AE1AEE8071004B17F3 /* ORKAudioGenerator.m */,
|
||||
147503B11AEE807C004B17F3 /* ORKToneAudiometryContentView.h */,
|
||||
147503B21AEE807C004B17F3 /* ORKToneAudiometryContentView.m */,
|
||||
B12EA0131B0D73A500F9F554 /* ORKToneAudiometryPracticeStep.h */,
|
||||
B12EA0141B0D73A500F9F554 /* ORKToneAudiometryPracticeStep.m */,
|
||||
147503B31AEE807C004B17F3 /* ORKToneAudiometryStep.h */,
|
||||
147503B41AEE807C004B17F3 /* ORKToneAudiometryStep.m */,
|
||||
B12EA0171B0D76AD00F9F554 /* ORKToneAudiometryPracticeStepViewController.h */,
|
||||
B12EA0181B0D76AD00F9F554 /* ORKToneAudiometryPracticeStepViewController.m */,
|
||||
147503B51AEE807C004B17F3 /* ORKToneAudiometryStepViewController.h */,
|
||||
147503B61AEE807C004B17F3 /* ORKToneAudiometryStepViewController.m */,
|
||||
);
|
||||
name = "Tone Audiometry";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
25ECC0921AFBD64800F3D63B /* Reaction Time */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
25ECC0931AFBD68300F3D63B /* ORKReactionTimeStep.h */,
|
||||
25ECC0941AFBD68300F3D63B /* ORKReactionTimeStep.m */,
|
||||
25ECC0991AFBD8B300F3D63B /* ORKReactionTimeViewController.h */,
|
||||
25ECC09A1AFBD8B300F3D63B /* ORKReactionTimeViewController.m */,
|
||||
25ECC09D1AFBD92D00F3D63B /* ORKReactionTimeContentView.h */,
|
||||
25ECC09E1AFBD92D00F3D63B /* ORKReactionTimeContentView.m */,
|
||||
25ECC0A11AFBDD2700F3D63B /* ORKReactionTimeStimulusView.h */,
|
||||
25ECC0A21AFBDD2700F3D63B /* ORKReactionTimeStimulusView.m */,
|
||||
);
|
||||
name = "Reaction Time";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3FFF18341829DB1D00167070 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -815,8 +949,10 @@
|
||||
B12EFF4E1AB2161500A80147 /* Audio */,
|
||||
B12EFF5D1AB2177700A80147 /* Fitness */,
|
||||
B12EFF5E1AB2177D00A80147 /* Spatial Span Memory */,
|
||||
25ECC0921AFBD64800F3D63B /* Reaction Time */,
|
||||
B12EFF5F1AB2178500A80147 /* Tapping */,
|
||||
B12EFF601AB2178B00A80147 /* Walking */,
|
||||
147503AC1AEE8058004B17F3 /* Tone Audiometry */,
|
||||
);
|
||||
path = ActiveTasks;
|
||||
sourceTree = "<group>";
|
||||
@@ -833,6 +969,7 @@
|
||||
B12EFF3D1AB2121000A80147 /* Definitions */,
|
||||
B12EFF321AB2106F00A80147 /* UIKitCategories */,
|
||||
B12EFF311AB2100400A80147 /* Skin */,
|
||||
BC4194261AE8451F00073D6B /* Misc */,
|
||||
);
|
||||
path = Common;
|
||||
sourceTree = "<group>";
|
||||
@@ -840,6 +977,7 @@
|
||||
86C40BEB1A8D7C5C00081FAC /* Consent */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FA7A9D2C1B083D90005A2BEA /* Formatters */,
|
||||
B12EFF451AB214E000A80147 /* Model */,
|
||||
B12EFF461AB2150C00A80147 /* Visual */,
|
||||
B12EFF481AB2152D00A80147 /* Sharing */,
|
||||
@@ -851,16 +989,20 @@
|
||||
86CC8EA61AC09383001CCD89 /* ResearchKitTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
86D348001AC16175006DB02B /* ORKRecorderTests.m */,
|
||||
FA7A9D351B09362D005A2BEA /* Consent */,
|
||||
86CC8EA71AC09383001CCD89 /* Info.plist */,
|
||||
86CC8EA81AC09383001CCD89 /* ORKAccessibilityTests.m */,
|
||||
86CC8EA91AC09383001CCD89 /* ORKChoiceAnswerFormatHelperTests.m */,
|
||||
86CC8EAA1AC09383001CCD89 /* ORKConsentTests.m */,
|
||||
86CC8EAB1AC09383001CCD89 /* ORKDataLoggerManagerTests.m */,
|
||||
86CC8EAC1AC09383001CCD89 /* ORKDataLoggerTests.m */,
|
||||
86CC8EAD1AC09383001CCD89 /* ORKHKSampleTests.m */,
|
||||
86D348001AC16175006DB02B /* ORKRecorderTests.m */,
|
||||
86CC8EAF1AC09383001CCD89 /* ORKResultTests.m */,
|
||||
BCB96C121B19C0EC002A0B96 /* ORKStepTests.m */,
|
||||
BCAD50E71B0201EE0034806A /* ORKTaskTests.m */,
|
||||
86CC8EB01AC09383001CCD89 /* ORKTextChoiceCellGroupTests.m */,
|
||||
86CC8EA71AC09383001CCD89 /* Info.plist */,
|
||||
2EBFE11C1AE1B32D00CB8254 /* ORKUIViewAccessibilityTests.m */,
|
||||
2EBFE11F1AE1B74100CB8254 /* ORKVoiceEngineTests.m */,
|
||||
);
|
||||
path = ResearchKitTests;
|
||||
sourceTree = "<group>";
|
||||
@@ -887,6 +1029,7 @@
|
||||
B12EFF311AB2100400A80147 /* Skin */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BC01B0FA1B0EB99700863803 /* ORKTintedImageView_Internal.h */,
|
||||
86C40BB71A8D7C5C00081FAC /* ORKSkin.h */,
|
||||
86C40BB81A8D7C5C00081FAC /* ORKSkin.m */,
|
||||
86C40B7A1A8D7C5C00081FAC /* ORKDefaultFont.h */,
|
||||
@@ -918,6 +1061,8 @@
|
||||
86C40BB11A8D7C5C00081FAC /* ORKScaleValueLabel.m */,
|
||||
86C40BAC1A8D7C5C00081FAC /* ORKScaleRangeLabel.h */,
|
||||
86C40BAD1A8D7C5C00081FAC /* ORKScaleRangeLabel.m */,
|
||||
B8760F291AFBEFB0007FA16F /* ORKScaleRangeDescriptionLabel.h */,
|
||||
B8760F2A1AFBEFB0007FA16F /* ORKScaleRangeDescriptionLabel.m */,
|
||||
86C40BC01A8D7C5C00081FAC /* ORKSubheadlineLabel.h */,
|
||||
86C40BC11A8D7C5C00081FAC /* ORKSubheadlineLabel.m */,
|
||||
86C40BD31A8D7C5C00081FAC /* ORKTapCountLabel.h */,
|
||||
@@ -939,8 +1084,6 @@
|
||||
B12EFF321AB2106F00A80147 /* UIKitCategories */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
86C40BE51A8D7C5C00081FAC /* UIApplication+ResearchKit.h */,
|
||||
86C40BE61A8D7C5C00081FAC /* UIApplication+ResearchKit.m */,
|
||||
86C40BE71A8D7C5C00081FAC /* UIBarButtonItem+ORKBarButtonItem.h */,
|
||||
86C40BE81A8D7C5C00081FAC /* UIBarButtonItem+ORKBarButtonItem.m */,
|
||||
86C40BE91A8D7C5C00081FAC /* UIResponder+ResearchKit.h */,
|
||||
@@ -952,9 +1095,12 @@
|
||||
B12EFF331AB2110300A80147 /* Task */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BC13CE371B0660220044153C /* ORKNavigableOrderedTask.h */,
|
||||
BC13CE381B0660220044153C /* ORKNavigableOrderedTask.m */,
|
||||
86C40BD51A8D7C5C00081FAC /* ORKTask.h */,
|
||||
86C40B9D1A8D7C5C00081FAC /* ORKOrderedTask.h */,
|
||||
86C40B9E1A8D7C5C00081FAC /* ORKOrderedTask.m */,
|
||||
BC13CE3D1B0662A80044153C /* ORKOrderedTask_Internal.h */,
|
||||
86C40BD71A8D7C5C00081FAC /* ORKTaskViewController.h */,
|
||||
86C40BD81A8D7C5C00081FAC /* ORKTaskViewController.m */,
|
||||
86C40BD91A8D7C5C00081FAC /* ORKTaskViewController_Internal.h */,
|
||||
@@ -972,12 +1118,17 @@
|
||||
86C40BBC1A8D7C5C00081FAC /* ORKStepViewController.h */,
|
||||
86C40BBD1A8D7C5C00081FAC /* ORKStepViewController.m */,
|
||||
86C40BBE1A8D7C5C00081FAC /* ORKStepViewController_Internal.h */,
|
||||
BCA5C0331AEC05F20092AC8D /* ORKStepNavigationRule.h */,
|
||||
BCA5C0341AEC05F20092AC8D /* ORKStepNavigationRule.m */,
|
||||
BC13CE3B1B0662990044153C /* ORKStepNavigationRule_Private.h */,
|
||||
BC13CE411B066A990044153C /* ORKStepNavigationRule_Internal.h */,
|
||||
86C40B771A8D7C5C00081FAC /* ORKCustomStepView.h */,
|
||||
86C40B781A8D7C5C00081FAC /* ORKCustomStepView.m */,
|
||||
86C40B791A8D7C5C00081FAC /* ORKCustomStepView_Internal.h */,
|
||||
B12EFF351AB2116400A80147 /* Instruction Step */,
|
||||
B12EFF361AB2117B00A80147 /* Question Step */,
|
||||
B12EFF3C1AB211FB00A80147 /* Form Step */,
|
||||
D44239761AF17EE700559D96 /* Image Capture Step */,
|
||||
);
|
||||
name = Step;
|
||||
sourceTree = "<group>";
|
||||
@@ -1016,6 +1167,8 @@
|
||||
86C40BA71A8D7C5C00081FAC /* ORKResult.h */,
|
||||
86C40BA81A8D7C5C00081FAC /* ORKResult.m */,
|
||||
86C40BA91A8D7C5C00081FAC /* ORKResult_Private.h */,
|
||||
BC13CE3F1B0666FD0044153C /* ORKResultPredicate.h */,
|
||||
BCFF24BC1B0798D10044EC35 /* ORKResultPredicate.m */,
|
||||
);
|
||||
name = Result;
|
||||
sourceTree = "<group>";
|
||||
@@ -1178,8 +1331,6 @@
|
||||
86C40BFF1A8D7C5C00081FAC /* ORKConsentSection.h */,
|
||||
86C40C001A8D7C5C00081FAC /* ORKConsentSection.m */,
|
||||
86C40C011A8D7C5C00081FAC /* ORKConsentSection_Internal.h */,
|
||||
86C40BFD1A8D7C5C00081FAC /* ORKConsentSection+AssetLoading.h */,
|
||||
86C40BFE1A8D7C5C00081FAC /* ORKConsentSection+AssetLoading.m */,
|
||||
86C40C021A8D7C5C00081FAC /* ORKConsentSignature.h */,
|
||||
86C40C031A8D7C5C00081FAC /* ORKConsentSignature.m */,
|
||||
);
|
||||
@@ -1261,6 +1412,7 @@
|
||||
86C40C0E1A8D7C5C00081FAC /* ORKVisualConsentTransitionAnimator.h */,
|
||||
86C40C0F1A8D7C5C00081FAC /* ORKVisualConsentTransitionAnimator.m */,
|
||||
B12EFF441AB214B400A80147 /* Tinted Animations */,
|
||||
BCFB2EAF1AE70E4E0070B5D0 /* ORKConsentSceneViewController_Internal.h */,
|
||||
);
|
||||
name = "Consent Views";
|
||||
sourceTree = "<group>";
|
||||
@@ -1496,6 +1648,7 @@
|
||||
children = (
|
||||
86C40B4D1A8D7C5B00081FAC /* ORKVoiceEngine.h */,
|
||||
86C40B4E1A8D7C5B00081FAC /* ORKVoiceEngine.m */,
|
||||
2EBFE11E1AE1B68800CB8254 /* ORKVoiceEngine_Internal.h */,
|
||||
);
|
||||
name = "Speech Synthesis";
|
||||
sourceTree = "<group>";
|
||||
@@ -1545,6 +1698,52 @@
|
||||
path = Localized;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BC4194261AE8451F00073D6B /* Misc */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BC4194271AE8453A00073D6B /* ORKObserver.h */,
|
||||
BC4194281AE8453A00073D6B /* ORKObserver.m */,
|
||||
);
|
||||
name = Misc;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D44239761AF17EE700559D96 /* Image Capture Step */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D44239771AF17F5100559D96 /* ORKImageCaptureStep.h */,
|
||||
D44239781AF17F5100559D96 /* ORKImageCaptureStep.m */,
|
||||
D442397B1AF17F7600559D96 /* ORKImageCaptureStepViewController.h */,
|
||||
D442397C1AF17F7600559D96 /* ORKImageCaptureStepViewController.m */,
|
||||
D45852081AF6CCFA00A2DE13 /* ORKImageCaptureCameraPreviewView.h */,
|
||||
D45852091AF6CCFA00A2DE13 /* ORKImageCaptureCameraPreviewView.m */,
|
||||
D42FEFB61AF7557000A124F8 /* ORKImageCaptureView.h */,
|
||||
D42FEFB71AF7557000A124F8 /* ORKImageCaptureView.m */,
|
||||
);
|
||||
name = "Image Capture Step";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FA7A9D2C1B083D90005A2BEA /* Formatters */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FA7A9D2D1B083DD3005A2BEA /* ORKConsentSectionFormatter.h */,
|
||||
FA7A9D2E1B083DD3005A2BEA /* ORKConsentSectionFormatter.m */,
|
||||
FA7A9D311B0843A9005A2BEA /* ORKConsentSignatureFormatter.h */,
|
||||
FA7A9D321B0843A9005A2BEA /* ORKConsentSignatureFormatter.m */,
|
||||
);
|
||||
name = Formatters;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FA7A9D351B09362D005A2BEA /* Consent */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
86CC8EAA1AC09383001CCD89 /* ORKConsentTests.m */,
|
||||
FA7A9D2A1B082688005A2BEA /* ORKConsentDocumentTests.m */,
|
||||
FA7A9D361B09365F005A2BEA /* ORKConsentSectionFormatterTests.m */,
|
||||
FA7A9D381B0969A7005A2BEA /* ORKConsentSignatureFormatterTests.m */,
|
||||
);
|
||||
name = Consent;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
@@ -1552,35 +1751,43 @@
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
BCA5C0351AEC05F20092AC8D /* ORKStepNavigationRule.h in Headers */,
|
||||
BC13CE391B0660220044153C /* ORKNavigableOrderedTask.h in Headers */,
|
||||
86C40C921A8D7C5C00081FAC /* ORKAudioRecorder.h in Headers */,
|
||||
86C40D8E1A8D7C5C00081FAC /* ORKStep.h in Headers */,
|
||||
618DA04E1A93D0D600E63AA8 /* ORKAccessibility.h in Headers */,
|
||||
86B781BB1AA668ED00688151 /* ORKTimeIntervalPicker.h in Headers */,
|
||||
86C40C6A1A8D7C5C00081FAC /* CMDeviceMotion+ORKJSONDictionary.h in Headers */,
|
||||
86C40CE41A8D7C5C00081FAC /* ORKAnswerFormat.h in Headers */,
|
||||
25ECC0951AFBD68300F3D63B /* ORKReactionTimeStep.h in Headers */,
|
||||
86C40D0A1A8D7C5C00081FAC /* ORKCustomStepView.h in Headers */,
|
||||
86C40DCE1A8D7C5C00081FAC /* ORKTaskViewController_Internal.h in Headers */,
|
||||
25ECC09F1AFBD92D00F3D63B /* ORKReactionTimeContentView.h in Headers */,
|
||||
86C40C361A8D7C5C00081FAC /* ORKSpatialSpanGame.h in Headers */,
|
||||
86C40CAC1A8D7C5C00081FAC /* ORKRecorder.h in Headers */,
|
||||
865EA1681ABA1AA10037C68E /* ORKPicker.h in Headers */,
|
||||
86C40CC81A8D7C5C00081FAC /* ORKFormItemCell.h in Headers */,
|
||||
86C40DF21A8D7C5C00081FAC /* ORKConsentReviewController.h in Headers */,
|
||||
86C40C961A8D7C5C00081FAC /* ORKDataLogger.h in Headers */,
|
||||
BC13CE421B066A990044153C /* ORKStepNavigationRule_Internal.h in Headers */,
|
||||
86C40D781A8D7C5C00081FAC /* ORKScaleSlider.h in Headers */,
|
||||
86C40D241A8D7C5C00081FAC /* ORKFormStepViewController.h in Headers */,
|
||||
86C40D6A1A8D7C5C00081FAC /* ORKResult.h in Headers */,
|
||||
86C40DD61A8D7C5C00081FAC /* ORKUnitLabel.h in Headers */,
|
||||
86C40D9C1A8D7C5C00081FAC /* ORKSubheadlineLabel.h in Headers */,
|
||||
147503BB1AEE807C004B17F3 /* ORKToneAudiometryStepViewController.h in Headers */,
|
||||
86C40D441A8D7C5C00081FAC /* ORKInstructionStepViewController.h in Headers */,
|
||||
86C40D841A8D7C5C00081FAC /* ORKSelectionTitleLabel.h in Headers */,
|
||||
86C40E141A8D7C5C00081FAC /* ORKConsentSection+AssetLoading.h in Headers */,
|
||||
BC13CE3C1B0662990044153C /* ORKStepNavigationRule_Private.h in Headers */,
|
||||
86C40D3C1A8D7C5C00081FAC /* ORKImageChoiceLabel.h in Headers */,
|
||||
BC4194291AE8453A00073D6B /* ORKObserver.h in Headers */,
|
||||
86C40C661A8D7C5C00081FAC /* CMAccelerometerData+ORKJSONDictionary.h in Headers */,
|
||||
86C40DE61A8D7C5C00081FAC /* UIApplication+ResearchKit.h in Headers */,
|
||||
25ECC0A31AFBDD2700F3D63B /* ORKReactionTimeStimulusView.h in Headers */,
|
||||
86C40D701A8D7C5C00081FAC /* ORKRoundTappingButton.h in Headers */,
|
||||
865EA1621AB8DF750037C68E /* ORKDateTimePicker.h in Headers */,
|
||||
86C40DA01A8D7C5C00081FAC /* ORKSurveyAnswerCell.h in Headers */,
|
||||
86C40D941A8D7C5C00081FAC /* ORKStepViewController.h in Headers */,
|
||||
B8760F2B1AFBEFB0007FA16F /* ORKScaleRangeDescriptionLabel.h in Headers */,
|
||||
86C40C221A8D7C5C00081FAC /* ORKCountdownStep.h in Headers */,
|
||||
86C40DB61A8D7C5C00081FAC /* ORKSurveyAnswerCellForText.h in Headers */,
|
||||
86C40E281A8D7C5C00081FAC /* ORKSignatureView.h in Headers */,
|
||||
@@ -1593,14 +1800,17 @@
|
||||
86C40D201A8D7C5C00081FAC /* ORKFormStep.h in Headers */,
|
||||
86C40C761A8D7C5C00081FAC /* HKSample+ORKJSONDictionary.h in Headers */,
|
||||
86C40CEA1A8D7C5C00081FAC /* ORKAnswerTextField.h in Headers */,
|
||||
B12EA0191B0D76AD00F9F554 /* ORKToneAudiometryPracticeStepViewController.h in Headers */,
|
||||
B183A4A21A8535D100C76870 /* ResearchKit_Private.h in Headers */,
|
||||
86C40CE81A8D7C5C00081FAC /* ORKAnswerFormat_Internal.h in Headers */,
|
||||
86C40D021A8D7C5C00081FAC /* ORKContinueButton.h in Headers */,
|
||||
86C40D681A8D7C5C00081FAC /* ORKQuestionStepViewController_Private.h in Headers */,
|
||||
86C40D121A8D7C5C00081FAC /* ORKDefines.h in Headers */,
|
||||
86C40D401A8D7C5C00081FAC /* ORKInstructionStep.h in Headers */,
|
||||
147503B71AEE807C004B17F3 /* ORKToneAudiometryContentView.h in Headers */,
|
||||
86C40D981A8D7C5C00081FAC /* ORKStepViewController_Internal.h in Headers */,
|
||||
86B89ABB1AB3BECC001626A4 /* ORKStepHeaderView.h in Headers */,
|
||||
D42FEFB81AF7557000A124F8 /* ORKImageCaptureView.h in Headers */,
|
||||
86C40D1A1A8D7C5C00081FAC /* ORKFormItem_Internal.h in Headers */,
|
||||
86C40E1C1A8D7C5C00081FAC /* ORKConsentSection_Internal.h in Headers */,
|
||||
86C40C7A1A8D7C5C00081FAC /* ORKAccelerometerRecorder.h in Headers */,
|
||||
@@ -1631,10 +1841,12 @@
|
||||
86C40D7C1A8D7C5C00081FAC /* ORKScaleValueLabel.h in Headers */,
|
||||
86C40E2C1A8D7C5C00081FAC /* ORKVisualConsentStep.h in Headers */,
|
||||
86C40CB81A8D7C5C00081FAC /* ORKVoiceEngine.h in Headers */,
|
||||
FA7A9D331B0843A9005A2BEA /* ORKConsentSignatureFormatter.h in Headers */,
|
||||
86C40C5A1A8D7C5C00081FAC /* ORKWalkingTaskStep.h in Headers */,
|
||||
86C40E361A8D7C5C00081FAC /* ORKVisualConsentTransitionAnimator.h in Headers */,
|
||||
86AD910D1AB7AE4100361FEB /* ORKNavigationContainerView_Internal.h in Headers */,
|
||||
86C40CEE1A8D7C5C00081FAC /* ORKAnswerTextView.h in Headers */,
|
||||
B12EA0151B0D73A500F9F554 /* ORKToneAudiometryPracticeStep.h in Headers */,
|
||||
86C40C421A8D7C5C00081FAC /* ORKSpatialSpanMemoryStep.h in Headers */,
|
||||
86C40C6E1A8D7C5C00081FAC /* CMMotionActivity+ORKJSONDictionary.h in Headers */,
|
||||
B11C54991A9EEF8800265E61 /* ORKConsentSharingStep.h in Headers */,
|
||||
@@ -1643,11 +1855,15 @@
|
||||
86C40C841A8D7C5C00081FAC /* ORKActiveStepTimer.h in Headers */,
|
||||
86B781BD1AA668ED00688151 /* ORKValuePicker.h in Headers */,
|
||||
86C40DE21A8D7C5C00081FAC /* ORKVerticalContainerView_Internal.h in Headers */,
|
||||
BC01B0FB1B0EB99700863803 /* ORKTintedImageView_Internal.h in Headers */,
|
||||
86B89ABE1AB3BFDB001626A4 /* ORKStepHeaderView_Internal.h in Headers */,
|
||||
86C40C1E1A8D7C5C00081FAC /* ORKAudioStepViewController.h in Headers */,
|
||||
147503AF1AEE8071004B17F3 /* ORKAudioGenerator.h in Headers */,
|
||||
86AD91141AB7B97E00361FEB /* ORKQuestionStepView.h in Headers */,
|
||||
86C40C621A8D7C5C00081FAC /* CLLocation+ORKJSONDictionary.h in Headers */,
|
||||
86C40D801A8D7C5C00081FAC /* ORKSelectionSubTitleLabel.h in Headers */,
|
||||
D458520A1AF6CCFA00A2DE13 /* ORKImageCaptureCameraPreviewView.h in Headers */,
|
||||
D44239791AF17F5100559D96 /* ORKImageCaptureStep.h in Headers */,
|
||||
86C40CB21A8D7C5C00081FAC /* ORKRecorder_Private.h in Headers */,
|
||||
86C40C881A8D7C5C00081FAC /* ORKActiveStepTimerView.h in Headers */,
|
||||
86C40C161A8D7C5C00081FAC /* ORKAudioContentView.h in Headers */,
|
||||
@@ -1656,6 +1872,7 @@
|
||||
86C40CF61A8D7C5C00081FAC /* ORKBorderedButton.h in Headers */,
|
||||
86C40D4A1A8D7C5C00081FAC /* ORKLabel.h in Headers */,
|
||||
861D11A91AA691BB003C98A7 /* ORKScaleSliderView.h in Headers */,
|
||||
BC13CE3E1B0662A80044153C /* ORKOrderedTask_Internal.h in Headers */,
|
||||
86C40C3E1A8D7C5C00081FAC /* ORKSpatialSpanMemoryContentView.h in Headers */,
|
||||
86C40CC01A8D7C5C00081FAC /* ORKCompletionStep.h in Headers */,
|
||||
86C40DF61A8D7C5C00081FAC /* ORKConsentSignatureController.h in Headers */,
|
||||
@@ -1663,6 +1880,7 @@
|
||||
86C40D281A8D7C5C00081FAC /* ORKFormTextView.h in Headers */,
|
||||
86C40DEE1A8D7C5C00081FAC /* UIResponder+ResearchKit.h in Headers */,
|
||||
86C40E041A8D7C5C00081FAC /* ORKConsentLearnMoreViewController.h in Headers */,
|
||||
25ECC09B1AFBD8B300F3D63B /* ORKReactionTimeViewController.h in Headers */,
|
||||
86C40D061A8D7C5C00081FAC /* ORKCountdownLabel.h in Headers */,
|
||||
86C40C2A1A8D7C5C00081FAC /* ORKFitnessContentView.h in Headers */,
|
||||
86C40DC21A8D7C5C00081FAC /* ORKTapCountLabel.h in Headers */,
|
||||
@@ -1675,11 +1893,13 @@
|
||||
86C40C821A8D7C5C00081FAC /* ORKActiveStep_Internal.h in Headers */,
|
||||
86C40D481A8D7C5C00081FAC /* ORKInstructionStepViewController_Internal.h in Headers */,
|
||||
86C40D341A8D7C5C00081FAC /* ORKHelpers.h in Headers */,
|
||||
FA7A9D2F1B083DD3005A2BEA /* ORKConsentSectionFormatter.h in Headers */,
|
||||
86C40E341A8D7C5C00081FAC /* ORKVisualConsentStepViewController_Internal.h in Headers */,
|
||||
86C40D381A8D7C5C00081FAC /* ORKHTMLPDFWriter.h in Headers */,
|
||||
86C40D561A8D7C5C00081FAC /* ORKOrderedTask.h in Headers */,
|
||||
86C40C4E1A8D7C5C00081FAC /* ORKTappingContentView.h in Headers */,
|
||||
86C40CA01A8D7C5C00081FAC /* ORKHealthQuantityTypeRecorder.h in Headers */,
|
||||
BC13CE401B0666FD0044153C /* ORKResultPredicate.h in Headers */,
|
||||
86C40CFA1A8D7C5C00081FAC /* ORKCaption1Label.h in Headers */,
|
||||
86C40E081A8D7C5C00081FAC /* ORKConsentReviewStep.h in Headers */,
|
||||
86C40C901A8D7C5C00081FAC /* ORKActiveStepViewController_Internal.h in Headers */,
|
||||
@@ -1704,12 +1924,14 @@
|
||||
86C40D6E1A8D7C5C00081FAC /* ORKResult_Private.h in Headers */,
|
||||
86C40D161A8D7C5C00081FAC /* ORKErrors.h in Headers */,
|
||||
861D11B51AA7D073003C98A7 /* ORKTextChoiceCellGroup.h in Headers */,
|
||||
147503B91AEE807C004B17F3 /* ORKToneAudiometryStep.h in Headers */,
|
||||
86C40C9A1A8D7C5C00081FAC /* ORKDataLogger_Private.h in Headers */,
|
||||
861D11AD1AA7951F003C98A7 /* ORKChoiceAnswerFormatHelper.h in Headers */,
|
||||
86C40C461A8D7C5C00081FAC /* ORKSpatialSpanMemoryStepViewController.h in Headers */,
|
||||
86C40C521A8D7C5C00081FAC /* ORKTappingIntervalStep.h in Headers */,
|
||||
86C40D8A1A8D7C5C00081FAC /* ORKSkin.h in Headers */,
|
||||
86C40DFE1A8D7C5C00081FAC /* ORKConsentDocument.h in Headers */,
|
||||
D442397D1AF17F7600559D96 /* ORKImageCaptureStepViewController.h in Headers */,
|
||||
86C40C4A1A8D7C5C00081FAC /* ORKSpatialSpanTargetView.h in Headers */,
|
||||
86C40C1A1A8D7C5C00081FAC /* ORKAudioStep.h in Headers */,
|
||||
);
|
||||
@@ -1900,7 +2122,7 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "if [ ! -x /usr/local/bin/appledoc ]; then\n echo \"error: appledoc is required for building ResearchKit's documentation. See http://appledoc.gentlebytes.com\" 1>&2\n exit 1\nfi\n\necho \"warning: warnings are a result of https://github.com/tomaz/appledoc/issues/348\"\n\n/usr/local/bin/appledoc --print-settings --publish-docset --install-docset --output \"${BUILT_PRODUCTS_DIR}/docs/\" --include \"docs/ActiveTasks\" --include \"docs/InformedConsent\" --include \"docs/Overview\" --include \"docs/Survey\" --ignore \"docs/templates\" --templates \"docs/templates\" \"${BUILT_PRODUCTS_DIR}/ResearchKit.framework\" \"docs\"\n\necho \"warning: Opening documentation in browser...\"\nopen \"$HOME/Library/Developer/Shared/Documentation/DocSets/org.researchkit.ResearchKit.docset/Contents/Resources/Documents/index.html\"";
|
||||
shellScript = "if [ ! -x /usr/local/bin/appledoc ]; then\n echo \"error: appledoc is required for building ResearchKit's documentation. See http://appledoc.gentlebytes.com\" 1>&2\n exit 1\nfi\n\n/usr/local/bin/appledoc --print-settings --publish-docset --install-docset --output \"${BUILT_PRODUCTS_DIR}/docs/\" --include \"docs/ActiveTasks\" --include \"docs/InformedConsent\" --include \"docs/Overview\" --include \"docs/Survey\" --ignore \"docs/templates\" --templates \"docs/templates\" \"${BUILT_PRODUCTS_DIR}/ResearchKit.framework\" \"docs\"\n\necho \"note: Opening documentation in browser...\"\nopen \"$HOME/Library/Developer/Shared/Documentation/DocSets/org.researchkit.ResearchKit.docset/Contents/Resources/Documents/index.html\"";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
@@ -1912,10 +2134,17 @@
|
||||
files = (
|
||||
86CC8EB71AC09383001CCD89 /* ORKDataLoggerTests.m in Sources */,
|
||||
86CC8EBA1AC09383001CCD89 /* ORKResultTests.m in Sources */,
|
||||
FA7A9D391B0969A7005A2BEA /* ORKConsentSignatureFormatterTests.m in Sources */,
|
||||
86CC8EB81AC09383001CCD89 /* ORKHKSampleTests.m in Sources */,
|
||||
BCB96C131B19C0EC002A0B96 /* ORKStepTests.m in Sources */,
|
||||
86CC8EB51AC09383001CCD89 /* ORKConsentTests.m in Sources */,
|
||||
2EBFE11D1AE1B32D00CB8254 /* ORKUIViewAccessibilityTests.m in Sources */,
|
||||
86CC8EB41AC09383001CCD89 /* ORKChoiceAnswerFormatHelperTests.m in Sources */,
|
||||
2EBFE1201AE1B74100CB8254 /* ORKVoiceEngineTests.m in Sources */,
|
||||
BCAD50E81B0201EE0034806A /* ORKTaskTests.m in Sources */,
|
||||
86CC8EBB1AC09383001CCD89 /* ORKTextChoiceCellGroupTests.m in Sources */,
|
||||
FA7A9D2B1B082688005A2BEA /* ORKConsentDocumentTests.m in Sources */,
|
||||
FA7A9D371B09365F005A2BEA /* ORKConsentSectionFormatterTests.m in Sources */,
|
||||
86D348021AC161B0006DB02B /* ORKRecorderTests.m in Sources */,
|
||||
86CC8EB61AC09383001CCD89 /* ORKDataLoggerManagerTests.m in Sources */,
|
||||
86CC8EB31AC09383001CCD89 /* ORKAccessibilityTests.m in Sources */,
|
||||
@@ -1930,7 +2159,9 @@
|
||||
86C40E2E1A8D7C5C00081FAC /* ORKVisualConsentStep.m in Sources */,
|
||||
86C40CA61A8D7C5C00081FAC /* ORKLocationRecorder.m in Sources */,
|
||||
86C40CB61A8D7C5C00081FAC /* ORKTouchRecorder.m in Sources */,
|
||||
B12EA0161B0D73A500F9F554 /* ORKToneAudiometryPracticeStep.m in Sources */,
|
||||
86C40DC41A8D7C5C00081FAC /* ORKTapCountLabel.m in Sources */,
|
||||
25ECC0A01AFBD92D00F3D63B /* ORKReactionTimeContentView.m in Sources */,
|
||||
86C40D4C1A8D7C5C00081FAC /* ORKLabel.m in Sources */,
|
||||
86C40C9E1A8D7C5C00081FAC /* ORKDeviceMotionRecorder.m in Sources */,
|
||||
86C40D961A8D7C5C00081FAC /* ORKStepViewController.m in Sources */,
|
||||
@@ -1942,6 +2173,7 @@
|
||||
86C40C381A8D7C5C00081FAC /* ORKSpatialSpanGame.m in Sources */,
|
||||
86C40C481A8D7C5C00081FAC /* ORKSpatialSpanMemoryStepViewController.m in Sources */,
|
||||
86C40CDA1A8D7C5C00081FAC /* ORKTextFieldView.m in Sources */,
|
||||
B8760F2C1AFBEFB0007FA16F /* ORKScaleRangeDescriptionLabel.m in Sources */,
|
||||
86C40D821A8D7C5C00081FAC /* ORKSelectionSubTitleLabel.m in Sources */,
|
||||
86C40DCC1A8D7C5C00081FAC /* ORKTaskViewController.m in Sources */,
|
||||
86C40E061A8D7C5C00081FAC /* ORKConsentLearnMoreViewController.m in Sources */,
|
||||
@@ -1949,9 +2181,12 @@
|
||||
86C40C801A8D7C5C00081FAC /* ORKActiveStep.m in Sources */,
|
||||
86C40D721A8D7C5C00081FAC /* ORKRoundTappingButton.m in Sources */,
|
||||
86C40E2A1A8D7C5C00081FAC /* ORKSignatureView.m in Sources */,
|
||||
BCFF24BD1B0798D10044EC35 /* ORKResultPredicate.m in Sources */,
|
||||
25ECC0A41AFBDD2700F3D63B /* ORKReactionTimeStimulusView.m in Sources */,
|
||||
86C40CBA1A8D7C5C00081FAC /* ORKVoiceEngine.m in Sources */,
|
||||
861D11B61AA7D073003C98A7 /* ORKTextChoiceCellGroup.m in Sources */,
|
||||
86C40CCA1A8D7C5C00081FAC /* ORKFormItemCell.m in Sources */,
|
||||
147503B81AEE807C004B17F3 /* ORKToneAudiometryContentView.m in Sources */,
|
||||
86C40CBE1A8D7C5C00081FAC /* UITouch+ORKJSONDictionary.m in Sources */,
|
||||
618DA0521A93D0D600E63AA8 /* ORKAccessibilityFunctions.m in Sources */,
|
||||
86C40DB81A8D7C5C00081FAC /* ORKSurveyAnswerCellForText.m in Sources */,
|
||||
@@ -1972,28 +2207,35 @@
|
||||
86C40D001A8D7C5C00081FAC /* ORKChoiceViewCell.m in Sources */,
|
||||
86C40C4C1A8D7C5C00081FAC /* ORKSpatialSpanTargetView.m in Sources */,
|
||||
86C40D9E1A8D7C5C00081FAC /* ORKSubheadlineLabel.m in Sources */,
|
||||
D42FEFB91AF7557000A124F8 /* ORKImageCaptureView.m in Sources */,
|
||||
86C40C401A8D7C5C00081FAC /* ORKSpatialSpanMemoryContentView.m in Sources */,
|
||||
147503BC1AEE807C004B17F3 /* ORKToneAudiometryStepViewController.m in Sources */,
|
||||
86AD910B1AB7AD1E00361FEB /* ORKNavigationContainerView.m in Sources */,
|
||||
865EA1631AB8DF750037C68E /* ORKDateTimePicker.m in Sources */,
|
||||
86C40C781A8D7C5C00081FAC /* HKSample+ORKJSONDictionary.m in Sources */,
|
||||
86C40D421A8D7C5C00081FAC /* ORKInstructionStep.m in Sources */,
|
||||
86C40C2C1A8D7C5C00081FAC /* ORKFitnessContentView.m in Sources */,
|
||||
D458520B1AF6CCFA00A2DE13 /* ORKImageCaptureCameraPreviewView.m in Sources */,
|
||||
86C40E1A1A8D7C5C00081FAC /* ORKConsentSection.m in Sources */,
|
||||
86C40C241A8D7C5C00081FAC /* ORKCountdownStep.m in Sources */,
|
||||
86C40CA21A8D7C5C00081FAC /* ORKHealthQuantityTypeRecorder.m in Sources */,
|
||||
86C40DC01A8D7C5C00081FAC /* ORKTableViewCell.m in Sources */,
|
||||
BC41942A1AE8453A00073D6B /* ORKObserver.m in Sources */,
|
||||
86C40C681A8D7C5C00081FAC /* CMAccelerometerData+ORKJSONDictionary.m in Sources */,
|
||||
86C40C701A8D7C5C00081FAC /* CMMotionActivity+ORKJSONDictionary.m in Sources */,
|
||||
147503B01AEE8071004B17F3 /* ORKAudioGenerator.m in Sources */,
|
||||
86C40D7E1A8D7C5C00081FAC /* ORKScaleValueLabel.m in Sources */,
|
||||
B12EA01A1B0D76AD00F9F554 /* ORKToneAudiometryPracticeStepViewController.m in Sources */,
|
||||
86C40D901A8D7C5C00081FAC /* ORKStep.m in Sources */,
|
||||
86C40D2E1A8D7C5C00081FAC /* ORKHeadlineLabel.m in Sources */,
|
||||
FA7A9D341B0843A9005A2BEA /* ORKConsentSignatureFormatter.m in Sources */,
|
||||
86C40CFC1A8D7C5C00081FAC /* ORKCaption1Label.m in Sources */,
|
||||
86C40E001A8D7C5C00081FAC /* ORKConsentDocument.m in Sources */,
|
||||
D442397A1AF17F5100559D96 /* ORKImageCaptureStep.m in Sources */,
|
||||
86C40CDE1A8D7C5C00081FAC /* ORKTintedImageView.m in Sources */,
|
||||
86B781BC1AA668ED00688151 /* ORKTimeIntervalPicker.m in Sources */,
|
||||
86B781BE1AA668ED00688151 /* ORKValuePicker.m in Sources */,
|
||||
86B89ABC1AB3BECC001626A4 /* ORKStepHeaderView.m in Sources */,
|
||||
86C40DE81A8D7C5C00081FAC /* UIApplication+ResearchKit.m in Sources */,
|
||||
861D11AA1AA691BB003C98A7 /* ORKScaleSliderView.m in Sources */,
|
||||
86C40C141A8D7C5C00081FAC /* ORKActiveStepQuantityView.m in Sources */,
|
||||
86C40C6C1A8D7C5C00081FAC /* CMDeviceMotion+ORKJSONDictionary.m in Sources */,
|
||||
@@ -2005,18 +2247,21 @@
|
||||
86C40C201A8D7C5C00081FAC /* ORKAudioStepViewController.m in Sources */,
|
||||
86C40D081A8D7C5C00081FAC /* ORKCountdownLabel.m in Sources */,
|
||||
86C40E121A8D7C5C00081FAC /* ORKConsentSceneViewController.m in Sources */,
|
||||
D442397E1AF17F7600559D96 /* ORKImageCaptureStepViewController.m in Sources */,
|
||||
86C40CAE1A8D7C5C00081FAC /* ORKRecorder.m in Sources */,
|
||||
86C40DAC1A8D7C5C00081FAC /* ORKSurveyAnswerCellForNumber.m in Sources */,
|
||||
86C40C541A8D7C5C00081FAC /* ORKTappingIntervalStep.m in Sources */,
|
||||
86C40D6C1A8D7C5C00081FAC /* ORKResult.m in Sources */,
|
||||
86C40CD21A8D7C5C00081FAC /* ORKInstructionStepView.m in Sources */,
|
||||
86C40D181A8D7C5C00081FAC /* ORKErrors.m in Sources */,
|
||||
25ECC09C1AFBD8B300F3D63B /* ORKReactionTimeViewController.m in Sources */,
|
||||
86C40DB01A8D7C5C00081FAC /* ORKSurveyAnswerCellForScale.m in Sources */,
|
||||
86C40C281A8D7C5C00081FAC /* ORKCountdownStepViewController.m in Sources */,
|
||||
86C40C641A8D7C5C00081FAC /* CLLocation+ORKJSONDictionary.m in Sources */,
|
||||
86C40D041A8D7C5C00081FAC /* ORKContinueButton.m in Sources */,
|
||||
86C40DEC1A8D7C5C00081FAC /* UIBarButtonItem+ORKBarButtonItem.m in Sources */,
|
||||
86C40CAA1A8D7C5C00081FAC /* ORKPedometerRecorder.m in Sources */,
|
||||
BCA5C0361AEC05F20092AC8D /* ORKStepNavigationRule.m in Sources */,
|
||||
86C40D761A8D7C5C00081FAC /* ORKScaleRangeLabel.m in Sources */,
|
||||
86C40C741A8D7C5C00081FAC /* CMPedometerData+ORKJSONDictionary.m in Sources */,
|
||||
86AD91151AB7B97E00361FEB /* ORKQuestionStepView.m in Sources */,
|
||||
@@ -2038,8 +2283,9 @@
|
||||
86C40C8E1A8D7C5C00081FAC /* ORKActiveStepViewController.m in Sources */,
|
||||
B183A5011A8535D100C76870 /* (null) in Sources */,
|
||||
86C40CE61A8D7C5C00081FAC /* ORKAnswerFormat.m in Sources */,
|
||||
25ECC0961AFBD68300F3D63B /* ORKReactionTimeStep.m in Sources */,
|
||||
BC13CE3A1B0660220044153C /* ORKNavigableOrderedTask.m in Sources */,
|
||||
B11C54A11A9EF4A700265E61 /* ORKConsentSharingStepViewController.m in Sources */,
|
||||
86C40E161A8D7C5C00081FAC /* ORKConsentSection+AssetLoading.m in Sources */,
|
||||
86C40DA81A8D7C5C00081FAC /* ORKSurveyAnswerCellForImageSelection.m in Sources */,
|
||||
86C40CCE1A8D7C5C00081FAC /* ORKImageSelectionView.m in Sources */,
|
||||
86C40D461A8D7C5C00081FAC /* ORKInstructionStepViewController.m in Sources */,
|
||||
@@ -2050,6 +2296,8 @@
|
||||
86C40CC61A8D7C5C00081FAC /* ORKCompletionStepViewController.m in Sources */,
|
||||
86C40D661A8D7C5C00081FAC /* ORKQuestionStepViewController.m in Sources */,
|
||||
86C40DF41A8D7C5C00081FAC /* ORKConsentReviewController.m in Sources */,
|
||||
147503BA1AEE807C004B17F3 /* ORKToneAudiometryStep.m in Sources */,
|
||||
FA7A9D301B083DD3005A2BEA /* ORKConsentSectionFormatter.m in Sources */,
|
||||
86C40D2A1A8D7C5C00081FAC /* ORKFormTextView.m in Sources */,
|
||||
86C40DD41A8D7C5C00081FAC /* ORKTextButton.m in Sources */,
|
||||
86C40C981A8D7C5C00081FAC /* ORKDataLogger.m in Sources */,
|
||||
@@ -2264,6 +2512,7 @@
|
||||
PRODUCT_NAME = ResearchKit;
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -2290,6 +2539,7 @@
|
||||
PRODUCT_NAME = ResearchKit;
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
||||
@@ -28,8 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// Shared header for accessibility functionality.
|
||||
|
||||
// Shared header for accessibility functionality.
|
||||
#import "UIView+ORKAccessibility.h"
|
||||
#import "ORKAccessibilityFunctions.h"
|
||||
|
||||
|
||||
@@ -28,9 +28,11 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKDefines.h"
|
||||
#import "ORKHelpers.h"
|
||||
|
||||
|
||||
@class ORKScaleSlider;
|
||||
|
||||
// Used to properly format values from the ORKScaleSlider.
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "ORKAccessibilityFunctions.h"
|
||||
#import "ORKAnswerFormat_Internal.h"
|
||||
@@ -35,6 +36,7 @@
|
||||
#import "ORKScaleSliderView.h"
|
||||
#import "UIView+ORKAccessibility.h"
|
||||
|
||||
|
||||
NSString *ORKAccessibilityFormatScaleSliderValue(CGFloat value, ORKScaleSlider *slider) {
|
||||
ORKScaleSliderView *sliderView = (ORKScaleSliderView *)[slider ork_superviewOfType:[ORKScaleSliderView class]];
|
||||
if (!slider || !sliderView) {
|
||||
|
||||
@@ -28,9 +28,11 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface UIView (ORKAccessibility)
|
||||
|
||||
@@ -28,8 +28,10 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "UIView+ORKAccessibility.h"
|
||||
|
||||
|
||||
@implementation UIView (ORKAccessibility)
|
||||
|
||||
- (UIView *)ork_superviewOfType:(Class)aClass {
|
||||
@@ -40,8 +42,7 @@
|
||||
id superview = [self superview];
|
||||
if (superview == nil) {
|
||||
return nil;
|
||||
}
|
||||
else if ([superview isKindOfClass:aClass]) {
|
||||
} else if ([superview isKindOfClass:aClass]) {
|
||||
return superview;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,9 +28,11 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface CLLocation (ORKJSONDictionary)
|
||||
|
||||
@@ -28,13 +28,14 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "CLLocation+ORKJSONDictionary.h"
|
||||
#import "ORKHelpers.h"
|
||||
|
||||
|
||||
@implementation CLLocation (ORKJSONDictionary)
|
||||
|
||||
- (NSDictionary *)ork_JSONDictionary
|
||||
{
|
||||
- (NSDictionary *)ork_JSONDictionary {
|
||||
CLLocationCoordinate2D coord = self.coordinate;
|
||||
CLLocationDistance altitude = self.altitude;
|
||||
CLLocationAccuracy horizAccuracy = self.horizontalAccuracy;
|
||||
@@ -44,32 +45,27 @@
|
||||
NSDate *timestamp = self.timestamp;
|
||||
CLFloor *floor = self.floor;
|
||||
|
||||
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObject:ORKStringFromDateISO8601(timestamp) forKey:@"timestamp"];
|
||||
NSMutableDictionary *dictionary = [@{@"timestamp" : ORKStringFromDateISO8601(timestamp)} mutableCopy];
|
||||
|
||||
if (horizAccuracy >= 0)
|
||||
{
|
||||
dict[@"coordinate"] = @{ @"latitude" : [NSDecimalNumber numberWithDouble:coord.latitude], @"longitude" : [NSDecimalNumber numberWithDouble:coord.longitude]};
|
||||
dict[@"horizontalAccuracy"] = [NSDecimalNumber numberWithDouble:horizAccuracy];
|
||||
if (horizAccuracy >= 0) {
|
||||
dictionary[@"coordinate"] = @{ @"latitude" : [NSDecimalNumber numberWithDouble:coord.latitude], @"longitude" : [NSDecimalNumber numberWithDouble:coord.longitude]};
|
||||
dictionary[@"horizontalAccuracy"] = [NSDecimalNumber numberWithDouble:horizAccuracy];
|
||||
}
|
||||
if (vertAccuracy >= 0)
|
||||
{
|
||||
dict[@"altitude"] = [NSDecimalNumber numberWithDouble:altitude];
|
||||
dict[@"verticalAccuracy"] = [NSDecimalNumber numberWithDouble:vertAccuracy];
|
||||
if (vertAccuracy >= 0) {
|
||||
dictionary[@"altitude"] = [NSDecimalNumber numberWithDouble:altitude];
|
||||
dictionary[@"verticalAccuracy"] = [NSDecimalNumber numberWithDouble:vertAccuracy];
|
||||
}
|
||||
if (course >= 0)
|
||||
{
|
||||
dict[@"course"] = [NSDecimalNumber numberWithDouble:course];
|
||||
if (course >= 0) {
|
||||
dictionary[@"course"] = [NSDecimalNumber numberWithDouble:course];
|
||||
}
|
||||
if (speed >= 0)
|
||||
{
|
||||
dict[@"speed"] = [NSDecimalNumber numberWithDouble:speed];
|
||||
if (speed >= 0) {
|
||||
dictionary[@"speed"] = [NSDecimalNumber numberWithDouble:speed];
|
||||
}
|
||||
if (floor)
|
||||
{
|
||||
dict[@"floor"] = @(floor.level);
|
||||
if (floor) {
|
||||
dictionary[@"floor"] = @(floor.level);
|
||||
}
|
||||
|
||||
return dict;
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -28,9 +28,11 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <CoreMotion/CoreMotion.h>
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface CMAccelerometerData (ORKJSONDictionary)
|
||||
|
||||
@@ -31,16 +31,16 @@
|
||||
|
||||
#import "CMAccelerometerData+ORKJSONDictionary.h"
|
||||
|
||||
|
||||
@implementation CMAccelerometerData (ORKJSONDictionary)
|
||||
|
||||
- (NSDictionary *)ork_JSONDictionary
|
||||
{
|
||||
NSDictionary *dict = @{@"timestamp": [NSDecimalNumber numberWithDouble:self.timestamp],
|
||||
@"x" : [NSDecimalNumber numberWithDouble:self.acceleration.x],
|
||||
@"y" : [NSDecimalNumber numberWithDouble:self.acceleration.y],
|
||||
@"z" : [NSDecimalNumber numberWithDouble:self.acceleration.z]
|
||||
};
|
||||
return dict;
|
||||
- (NSDictionary *)ork_JSONDictionary {
|
||||
NSDictionary *dictionary = @{@"timestamp": [NSDecimalNumber numberWithDouble:self.timestamp],
|
||||
@"x" : [NSDecimalNumber numberWithDouble:self.acceleration.x],
|
||||
@"y" : [NSDecimalNumber numberWithDouble:self.acceleration.y],
|
||||
@"z" : [NSDecimalNumber numberWithDouble:self.acceleration.z]
|
||||
};
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#import <CoreMotion/CoreMotion.h>
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface CMDeviceMotion (ORKJSONDictionary)
|
||||
|
||||
@@ -31,17 +31,17 @@
|
||||
|
||||
#import "CMDeviceMotion+ORKJSONDictionary.h"
|
||||
|
||||
|
||||
@implementation CMDeviceMotion (ORKJSONDictionary)
|
||||
|
||||
- (NSDictionary *)ork_JSONDictionary
|
||||
{
|
||||
- (NSDictionary *)ork_JSONDictionary {
|
||||
CMQuaternion attitude = self.attitude.quaternion;
|
||||
CMRotationRate rotationRate = self.rotationRate;
|
||||
CMAcceleration gravity = self.gravity;
|
||||
CMAcceleration userAccel = self.userAcceleration;
|
||||
CMCalibratedMagneticField field = self.magneticField;
|
||||
|
||||
NSDictionary *dict = @{@"timestamp": [NSDecimalNumber numberWithDouble:self.timestamp],
|
||||
NSDictionary *dictionary = @{@"timestamp": [NSDecimalNumber numberWithDouble:self.timestamp],
|
||||
@"attitude" : @{
|
||||
@"x" : [NSDecimalNumber numberWithDouble:attitude.x],
|
||||
@"y" : [NSDecimalNumber numberWithDouble:attitude.y],
|
||||
@@ -70,7 +70,7 @@
|
||||
@"accuracy" : [NSDecimalNumber numberWithDouble:field.accuracy]
|
||||
}
|
||||
};
|
||||
return dict;
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#import <CoreMotion/CoreMotion.h>
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface CMMotionActivity (ORKJSONDictionary)
|
||||
|
||||
@@ -32,57 +32,49 @@
|
||||
#import "CMMotionActivity+ORKJSONDictionary.h"
|
||||
#import "ORKHelpers.h"
|
||||
|
||||
static NSString * const kActivityUnknown = @"unknown";
|
||||
static NSString * const kActivityStationary = @"stationary";
|
||||
static NSString * const kActivityWalking = @"walking";
|
||||
static NSString * const kActivityRunning = @"running";
|
||||
static NSString * const kActivityAutomotive = @"automotive";
|
||||
static NSString * const kStartDateKey = @"startDate";
|
||||
static NSString * const kEndDateKey = @"endDate";
|
||||
|
||||
static NSString *stringFromActivityConfidence(CMMotionActivityConfidence confidence)
|
||||
{
|
||||
static NSString *const kActivityUnknown = @"unknown";
|
||||
static NSString *const kActivityStationary = @"stationary";
|
||||
static NSString *const kActivityWalking = @"walking";
|
||||
static NSString *const kActivityRunning = @"running";
|
||||
static NSString *const kActivityAutomotive = @"automotive";
|
||||
static NSString *const kStartDateKey = @"startDate";
|
||||
static NSString *const kEndDateKey = @"endDate";
|
||||
|
||||
static NSString *stringFromActivityConfidence(CMMotionActivityConfidence confidence) {
|
||||
NSDictionary *confidences = @{@(CMMotionActivityConfidenceHigh) : @"high",
|
||||
@(CMMotionActivityConfidenceMedium) : @"medium",
|
||||
@(CMMotionActivityConfidenceLow) : @"low"};
|
||||
return confidences[@(confidence)];
|
||||
}
|
||||
|
||||
static NSArray *activityArray(CMMotionActivity *activity)
|
||||
{
|
||||
NSMutableArray *ret = [NSMutableArray array];
|
||||
if (activity.unknown)
|
||||
{
|
||||
[ret addObject:kActivityUnknown];
|
||||
static NSArray *activityArray(CMMotionActivity *activity) {
|
||||
NSMutableArray *array = [NSMutableArray array];
|
||||
if (activity.unknown) {
|
||||
[array addObject:kActivityUnknown];
|
||||
}
|
||||
if (activity.stationary)
|
||||
{
|
||||
[ret addObject:kActivityStationary];
|
||||
if (activity.stationary) {
|
||||
[array addObject:kActivityStationary];
|
||||
}
|
||||
if (activity.walking)
|
||||
{
|
||||
[ret addObject:kActivityWalking];
|
||||
if (activity.walking) {
|
||||
[array addObject:kActivityWalking];
|
||||
}
|
||||
if (activity.running)
|
||||
{
|
||||
[ret addObject:kActivityRunning];
|
||||
if (activity.running) {
|
||||
[array addObject:kActivityRunning];
|
||||
}
|
||||
if (activity.automotive)
|
||||
{
|
||||
[ret addObject:kActivityAutomotive];
|
||||
if (activity.automotive) {
|
||||
[array addObject:kActivityAutomotive];
|
||||
}
|
||||
return ret;
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
static NSString * const kActivityKey = @"activity";
|
||||
static NSString *const kActivityKey = @"activity";
|
||||
|
||||
static NSString * const kConfidenceKey = @"confidence";
|
||||
static NSString *const kConfidenceKey = @"confidence";
|
||||
|
||||
@implementation CMMotionActivity (ORKJSONDictionary)
|
||||
|
||||
- (NSDictionary *)ork_JSONDictionary
|
||||
{
|
||||
- (NSDictionary *)ork_JSONDictionary {
|
||||
return @{kConfidenceKey : stringFromActivityConfidence(self.confidence),
|
||||
kActivityKey : activityArray(self),
|
||||
kStartDateKey : ORKStringFromDateISO8601(self.startDate)};
|
||||
|
||||
@@ -32,13 +32,13 @@
|
||||
#import <CoreMotion/CoreMotion.h>
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface CMPedometerData (ORKJSONDictionary)
|
||||
|
||||
- (NSDictionary *)ork_JSONDictionary;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -33,18 +33,17 @@
|
||||
#import "ORKHelpers.h"
|
||||
#import <CoreMotion/CoreMotion.h>
|
||||
|
||||
|
||||
@implementation CMPedometerData (ORKJSONDictionary)
|
||||
|
||||
- (NSDictionary *)ork_JSONDictionary
|
||||
{
|
||||
NSMutableDictionary *dict = [@{@"startDate": ORKStringFromDateISO8601(self.startDate),
|
||||
@"endDate": ORKStringFromDateISO8601(self.endDate)
|
||||
} mutableCopy];
|
||||
for (NSString *key in @[@"numberOfSteps", @"distance", @"floorsAscended", @"floorsDescended"])
|
||||
{
|
||||
[dict setValue:[self valueForKey:key] forKey:key];
|
||||
- (NSDictionary *)ork_JSONDictionary {
|
||||
NSMutableDictionary *dictionary = [@{@"startDate": ORKStringFromDateISO8601(self.startDate),
|
||||
@"endDate": ORKStringFromDateISO8601(self.endDate)
|
||||
} mutableCopy];
|
||||
for (NSString *key in @[@"numberOfSteps", @"distance", @"floorsAscended", @"floorsDescended"]) {
|
||||
[dictionary setValue:[self valueForKey:key] forKey:key];
|
||||
}
|
||||
return dict;
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#import <HealthKit/HealthKit.h>
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_OPTIONS(NSInteger, ORKSampleJSONOptions) {
|
||||
@@ -41,8 +42,7 @@ typedef NS_OPTIONS(NSInteger, ORKSampleJSONOptions) {
|
||||
};
|
||||
|
||||
/**
|
||||
* JSON serialization aid for HKSample.
|
||||
*
|
||||
JSON serialization aid for HKSample.
|
||||
*/
|
||||
@interface HKSample (ORKJSONDictionary)
|
||||
|
||||
@@ -52,8 +52,7 @@ typedef NS_OPTIONS(NSInteger, ORKSampleJSONOptions) {
|
||||
|
||||
|
||||
/**
|
||||
* JSON serialization aid for HKCorrelation.
|
||||
*
|
||||
JSON serialization aid for HKCorrelation.
|
||||
*/
|
||||
@interface HKCorrelation (ORKJSONDictionary)
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#import "HKSample+ORKJSONDictionary.h"
|
||||
#import "ORKHelpers.h"
|
||||
|
||||
|
||||
static NSString *const kHKSampleIdentifierKey = @"type"; // For compatibility with Health XML export
|
||||
static NSString *const kHKUUIDKey = @"uuid";
|
||||
static NSString *const kHKSampleStartDateKey = @"startDate";
|
||||
@@ -43,108 +44,102 @@ static NSString *const kHKUnitKey = @"unit";
|
||||
static NSString *const kHKCorrelatedObjectsKey = @"objects";
|
||||
// static NSString *const kHKSourceIdentifierKey = @"sourceBundleIdentifier";
|
||||
|
||||
@interface HKCategorySample (ORKJSONDictionary)
|
||||
|
||||
@end
|
||||
|
||||
@interface HKQuantitySample (ORKJSONDictionary)
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation HKSample (ORKJSONDictionary)
|
||||
|
||||
- (NSDictionary *)ork_JSONDictionaryWithOptions:(ORKSampleJSONOptions)options unit:(HKUnit *)unit
|
||||
{
|
||||
NSMutableDictionary *mdict = [NSMutableDictionary dictionaryWithCapacity:12];
|
||||
- (NSMutableDictionary *)ork_JSONMutableDictionaryWithOptions:(ORKSampleJSONOptions)options unit:(HKUnit *)unit {
|
||||
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithCapacity:12];
|
||||
|
||||
// Type identification
|
||||
HKSampleType *sampleType = [self sampleType];
|
||||
mdict[kHKSampleIdentifierKey] = [sampleType identifier];
|
||||
mutableDictionary[kHKSampleIdentifierKey] = [sampleType identifier];
|
||||
|
||||
// consider adding @"class" : NSStringFromClass(sampleType) ?
|
||||
|
||||
// Start and end dates
|
||||
NSDate *startDate = [self startDate];
|
||||
if (startDate)
|
||||
{
|
||||
mdict[kHKSampleStartDateKey] = ORKStringFromDateISO8601(startDate);
|
||||
if (startDate) {
|
||||
mutableDictionary[kHKSampleStartDateKey] = ORKStringFromDateISO8601(startDate);
|
||||
}
|
||||
|
||||
NSDate *endDate = [self endDate];
|
||||
if (endDate)
|
||||
{
|
||||
mdict[kHKSampleEndDateKey] = ORKStringFromDateISO8601(endDate);
|
||||
if (endDate) {
|
||||
mutableDictionary[kHKSampleEndDateKey] = ORKStringFromDateISO8601(endDate);
|
||||
}
|
||||
if (unit)
|
||||
{
|
||||
mdict[kHKUnitKey] = [unit unitString];
|
||||
if (unit) {
|
||||
mutableDictionary[kHKUnitKey] = [unit unitString];
|
||||
}
|
||||
if ((options & ORKSampleIncludeUUID))
|
||||
{
|
||||
if ((options & ORKSampleIncludeUUID)) {
|
||||
NSUUID *uuid = [self UUID];
|
||||
if (uuid)
|
||||
{
|
||||
mdict[kHKUUIDKey] = [uuid UUIDString];
|
||||
if (uuid) {
|
||||
mutableDictionary[kHKUUIDKey] = [uuid UUIDString];
|
||||
}
|
||||
}
|
||||
|
||||
if ( (options & ORKSampleIncludeMetadata) && [self.metadata count] > 0)
|
||||
{
|
||||
if ( (options & ORKSampleIncludeMetadata) && [self.metadata count] > 0) {
|
||||
NSMutableDictionary *metadata = [self.metadata mutableCopy];
|
||||
for (NSString *k in metadata)
|
||||
{
|
||||
for (NSString *k in metadata) {
|
||||
id obj = metadata[k];
|
||||
if ([obj isKindOfClass:[NSDate class]])
|
||||
{
|
||||
if ([obj isKindOfClass:[NSDate class]]) {
|
||||
metadata[k] = ORKStringFromDateISO8601(obj);
|
||||
}
|
||||
}
|
||||
|
||||
mdict[kHKMetadataKey] = metadata;
|
||||
mutableDictionary[kHKMetadataKey] = metadata;
|
||||
}
|
||||
|
||||
if (options & ORKSampleIncludeSource)
|
||||
{
|
||||
if (options & ORKSampleIncludeSource) {
|
||||
HKSource *source = [self source];
|
||||
if (source.name)
|
||||
{
|
||||
mdict[kHKSourceKey] = source.name;
|
||||
if (source.name) {
|
||||
mutableDictionary[kHKSourceKey] = source.name;
|
||||
}
|
||||
}
|
||||
|
||||
return mdict;
|
||||
return mutableDictionary;
|
||||
}
|
||||
|
||||
- (NSDictionary *)ork_JSONDictionaryWithOptions:(ORKSampleJSONOptions)options unit:(HKUnit *)unit {
|
||||
return [self ork_JSONMutableDictionaryWithOptions:options unit:unit];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface HKCategorySample (ORKJSONDictionary)
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation HKCategorySample (ORKJSONDictionary)
|
||||
|
||||
- (NSDictionary *)ork_JSONDictionaryWithOptions:(ORKSampleJSONOptions)options unit:(HKUnit *)unit
|
||||
{
|
||||
NSMutableDictionary *dict = [[super ork_JSONDictionaryWithOptions:options unit:unit] mutableCopy];
|
||||
- (NSDictionary *)ork_JSONDictionaryWithOptions:(ORKSampleJSONOptions)options unit:(HKUnit *)unit {
|
||||
NSMutableDictionary *dictionary = [self ork_JSONMutableDictionaryWithOptions:options unit:unit];
|
||||
|
||||
NSInteger value = [self value];
|
||||
dict[kHKSampleValue] = @(value);
|
||||
dictionary[kHKSampleValue] = @(value);
|
||||
|
||||
return dict;
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface HKQuantitySample (ORKJSONDictionary)
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation HKQuantitySample (ORKJSONDictionary)
|
||||
|
||||
- (NSDictionary *)ork_JSONDictionaryWithOptions:(ORKSampleJSONOptions)options unit:(HKUnit *)unit
|
||||
{
|
||||
NSMutableDictionary *dict = [[super ork_JSONDictionaryWithOptions:options unit:unit] mutableCopy];
|
||||
- (NSDictionary *)ork_JSONDictionaryWithOptions:(ORKSampleJSONOptions)options unit:(HKUnit *)unit {
|
||||
NSMutableDictionary *dictionary = [self ork_JSONMutableDictionaryWithOptions:options unit:unit];
|
||||
|
||||
HKQuantity *quantity = [self quantity];
|
||||
double value = [quantity doubleValueForUnit:unit];
|
||||
dict[kHKSampleValue] = @(value);
|
||||
dictionary[kHKSampleValue] = @(value);
|
||||
|
||||
|
||||
return dict;
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -152,29 +147,22 @@ static NSString *const kHKCorrelatedObjectsKey = @"objects";
|
||||
|
||||
@implementation HKCorrelation (ORKJSONDictionary)
|
||||
|
||||
|
||||
- (NSDictionary *)ork_JSONDictionaryWithOptions:(ORKSampleJSONOptions)options sampleTypes:(NSArray *)sampleTypes units:(NSArray *)units
|
||||
{
|
||||
NSMutableDictionary *mdict = (NSMutableDictionary *)[self ork_JSONDictionaryWithOptions:options unit:nil];
|
||||
- (NSDictionary *)ork_JSONDictionaryWithOptions:(ORKSampleJSONOptions)options sampleTypes:(NSArray *)sampleTypes units:(NSArray *)units {
|
||||
NSMutableDictionary *mutableDictionary = [self ork_JSONMutableDictionaryWithOptions:options unit:nil];
|
||||
|
||||
// The correlated objects
|
||||
NSMutableArray *correlatedObjects = [NSMutableArray arrayWithCapacity:[sampleTypes count]];
|
||||
for (HKSample *sample in self.objects)
|
||||
{
|
||||
for (HKSample *sample in self.objects) {
|
||||
NSUInteger idx = [sampleTypes indexOfObject:sample.sampleType];
|
||||
if (idx == NSNotFound)
|
||||
{
|
||||
if (idx == NSNotFound) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[correlatedObjects addObject:[sample ork_JSONDictionaryWithOptions:options unit:units[idx]]];
|
||||
}
|
||||
mdict[kHKCorrelatedObjectsKey] = correlatedObjects;
|
||||
mutableDictionary[kHKCorrelatedObjectsKey] = correlatedObjects;
|
||||
|
||||
|
||||
return mdict;
|
||||
return mutableDictionary;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -28,8 +28,10 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ORKRecorder.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
@@ -49,16 +51,18 @@ ORK_CLASS_AVAILABLE
|
||||
/**
|
||||
Returns an initialized accelerometer recorder using the specified frequency.
|
||||
|
||||
@param frequency The frequency of accelerometer data collected from CoreMotion, in hertz (Hz).
|
||||
@param identifier The unique identifier of the recorder (assigned by the recorder configuration).
|
||||
@param frequency The frequency of accelerometer data collected from CoreMotion, in hertz (Hz).
|
||||
@param step The step that requested this recorder.
|
||||
@param outputDirectory The directory in which the accelerometer data should be stored.
|
||||
|
||||
@return An initialized accelerometer recorder.
|
||||
*/
|
||||
- (instancetype)initWithFrequency:(double)frequency
|
||||
step:(ORKStep *)step
|
||||
outputDirectory:(NSURL *)outputDirectory;
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifer
|
||||
frequency:(double)frequency
|
||||
step:(nullable ORKStep *)step
|
||||
outputDirectory:(nullable NSURL *)outputDirectory;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKAccelerometerRecorder.h"
|
||||
#import "ORKDataLogger.h"
|
||||
#import "CMAccelerometerData+ORKJSONDictionary.h"
|
||||
@@ -37,36 +38,34 @@
|
||||
#import "ORKHelpers.h"
|
||||
|
||||
|
||||
@interface ORKAccelerometerRecorder()
|
||||
{
|
||||
@interface ORKAccelerometerRecorder () {
|
||||
ORKDataLogger *_logger;
|
||||
NSError *_recordingError;
|
||||
}
|
||||
|
||||
@property (nonatomic, strong) CMMotionManager *motionManager;
|
||||
|
||||
@property (nonatomic) NSTimeInterval uptime;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ORKAccelerometerRecorder
|
||||
|
||||
- (instancetype)initWithFrequency:(double)frequency step:(ORKStep *)step outputDirectory:(NSURL *)outputDirectory
|
||||
{
|
||||
self = [super initWithStep:step outputDirectory:outputDirectory];
|
||||
if (self)
|
||||
{
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier frequency:(double)frequency step:(ORKStep *)step outputDirectory:(NSURL *)outputDirectory {
|
||||
self = [super initWithIdentifier:identifier step:step outputDirectory:outputDirectory];
|
||||
if (self) {
|
||||
self.frequency = frequency;
|
||||
self.continuesInBackground = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
- (void)dealloc {
|
||||
[_logger finishCurrentLog];
|
||||
}
|
||||
|
||||
- (NSString *)recorderType
|
||||
{
|
||||
- (NSString *)recorderType {
|
||||
return @"accel";
|
||||
}
|
||||
|
||||
@@ -83,8 +82,8 @@
|
||||
}
|
||||
|
||||
- (void)start {
|
||||
|
||||
[super start];
|
||||
|
||||
self.motionManager = [self createMotionManager];
|
||||
|
||||
if (! _logger) {
|
||||
@@ -96,8 +95,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (! self.motionManager || ! self.motionManager.accelerometerAvailable)
|
||||
{
|
||||
if (! self.motionManager || ! self.motionManager.accelerometerAvailable) {
|
||||
NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain
|
||||
code:NSFeatureUnsupportedError
|
||||
userInfo:@{@"recorder" : self}];
|
||||
@@ -131,7 +129,7 @@
|
||||
}
|
||||
|
||||
- (NSDictionary *)userInfo {
|
||||
return @{ @"frequency" : @(self.frequency) };;
|
||||
return @{ @"frequency" : @(self.frequency) };
|
||||
}
|
||||
|
||||
- (void)stop {
|
||||
@@ -150,28 +148,24 @@
|
||||
[super stop];
|
||||
}
|
||||
|
||||
- (void)doStopRecording
|
||||
{
|
||||
- (void)doStopRecording {
|
||||
if (self.isRecording) {
|
||||
[self.motionManager stopAccelerometerUpdates];
|
||||
self.motionManager = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)finishRecordingWithError:(NSError *)error
|
||||
{
|
||||
- (void)finishRecordingWithError:(NSError *)error {
|
||||
[self doStopRecording];
|
||||
[super finishRecordingWithError:nil];
|
||||
}
|
||||
|
||||
- (void)reset
|
||||
{
|
||||
- (void)reset {
|
||||
[super reset];
|
||||
|
||||
_logger = nil;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)isRecording {
|
||||
return self.motionManager.accelerometerActive;
|
||||
}
|
||||
@@ -180,67 +174,65 @@
|
||||
return @"application/json";
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface ORKAccelerometerRecorderConfiguration()
|
||||
@interface ORKAccelerometerRecorderConfiguration ()
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ORKAccelerometerRecorderConfiguration
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wobjc-designated-initializers"
|
||||
- (instancetype)initWithFrequency:(double)freq {
|
||||
self = [self ork_init];
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier {
|
||||
@throw [NSException exceptionWithName:NSGenericException reason:@"Use subclass designated initializer" userInfo:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier frequency:(double)frequency {
|
||||
self = [super initWithIdentifier:identifier];
|
||||
if (self) {
|
||||
_frequency = freq;
|
||||
_frequency = frequency;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
- (ORKRecorder *)recorderForStep:(ORKStep *)step outputDirectory:(NSURL *)outputDirectory
|
||||
{
|
||||
return [[ORKAccelerometerRecorder alloc] initWithFrequency:self.frequency step:step outputDirectory:outputDirectory];
|
||||
- (ORKRecorder *)recorderForStep:(ORKStep *)step outputDirectory:(NSURL *)outputDirectory {
|
||||
return [[ORKAccelerometerRecorder alloc] initWithIdentifier:self.identifier
|
||||
frequency:self.frequency
|
||||
step:step
|
||||
outputDirectory:outputDirectory];
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self)
|
||||
{
|
||||
if (self) {
|
||||
ORK_DECODE_DOUBLE(aDecoder, frequency);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder
|
||||
{
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[super encodeWithCoder:aCoder];
|
||||
ORK_ENCODE_DOUBLE(aCoder, frequency);
|
||||
}
|
||||
|
||||
+ (BOOL)supportsSecureCoding
|
||||
{
|
||||
+ (BOOL)supportsSecureCoding {
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
BOOL isParentSame = [super isEqual:object];
|
||||
|
||||
__typeof(self) castObject = object;
|
||||
return (isParentSame &&
|
||||
(self.frequency == castObject.frequency)) ;
|
||||
(self.frequency == castObject.frequency));
|
||||
}
|
||||
|
||||
|
||||
- (ORKPermissionMask)requestedPermissionMask {
|
||||
return ORKPermissionCoreMotionAccelerometer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#import <ResearchKit/ORKStep.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
@@ -100,7 +101,6 @@ automatically navigates forward when the timer expires.
|
||||
*/
|
||||
@property (nonatomic) BOOL shouldSpeakCountDown;
|
||||
|
||||
|
||||
/**
|
||||
A Boolean value indicating whether to start the count down timer automatically when the step starts, or
|
||||
require the user to take some explicit action to start the step, such as tapping a button.
|
||||
@@ -193,7 +193,6 @@ The default value of this property is `NO`.
|
||||
*/
|
||||
@property (nonatomic, copy, nullable) NSArray *recorderConfigurations;
|
||||
|
||||
|
||||
/**
|
||||
The set of HealthKit types the step requests for reading. (read-only)
|
||||
|
||||
@@ -206,20 +205,6 @@ The default value of this property is `NO`.
|
||||
*/
|
||||
@property (nonatomic, readonly, nullable) NSSet *requestedHealthKitTypesForReading;
|
||||
|
||||
/**
|
||||
The set of access permissions required for the step. (read-only)
|
||||
|
||||
The permission mask is used by the task view controller to determine the types of
|
||||
access to request from users when they complete the initial instruction steps
|
||||
in a task. If your step requires access to APIs that limit access, include
|
||||
the permissions you require in this mask.
|
||||
|
||||
By default, the property scans the recorders and collates the permissions
|
||||
required by the recorders. Subclasses may override this implementation.
|
||||
*/
|
||||
@property (nonatomic, readonly) ORKPermissionMask requestedPermissions;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -36,10 +36,10 @@
|
||||
#import "ORKActiveStepViewController.h"
|
||||
#import "ORKRecorder_Private.h"
|
||||
|
||||
|
||||
@implementation ORKActiveStep
|
||||
|
||||
+ (Class)stepViewControllerClass
|
||||
{
|
||||
+ (Class)stepViewControllerClass {
|
||||
return [ORKActiveStepViewController class];
|
||||
}
|
||||
|
||||
@@ -53,25 +53,23 @@
|
||||
|
||||
- (BOOL)hasTitle {
|
||||
NSString *title = self.title;
|
||||
return ( title != nil && title.length > 0);
|
||||
return (title != nil && title.length > 0);
|
||||
}
|
||||
|
||||
- (BOOL)hasText {
|
||||
NSString *text = self.text;
|
||||
return ( text != nil && text.length > 0);
|
||||
return (text != nil && text.length > 0);
|
||||
}
|
||||
|
||||
- (BOOL)hasVoice {
|
||||
return ( _spokenInstruction != nil && _spokenInstruction.length > 0);
|
||||
return (_spokenInstruction != nil && _spokenInstruction.length > 0);
|
||||
}
|
||||
|
||||
- (BOOL)isRestorable {
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
+ (BOOL)supportsSecureCoding
|
||||
{
|
||||
+ (BOOL)supportsSecureCoding {
|
||||
return YES;
|
||||
}
|
||||
|
||||
@@ -83,8 +81,7 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
ORKActiveStep *step = [super copyWithZone:zone];
|
||||
step.stepDuration = self.stepDuration;
|
||||
step.shouldStartTimerAutomatically = self.shouldStartTimerAutomatically;
|
||||
@@ -102,12 +99,9 @@
|
||||
return step;
|
||||
}
|
||||
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self)
|
||||
{
|
||||
if (self ) {
|
||||
ORK_DECODE_DOUBLE(aDecoder, stepDuration);
|
||||
ORK_DECODE_BOOL(aDecoder, shouldStartTimerAutomatically);
|
||||
ORK_DECODE_BOOL(aDecoder, shouldSpeakCountDown);
|
||||
@@ -125,8 +119,7 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder
|
||||
{
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[super encodeWithCoder:aCoder];
|
||||
ORK_ENCODE_DOUBLE(aCoder, stepDuration);
|
||||
ORK_ENCODE_BOOL(aCoder, shouldStartTimerAutomatically);
|
||||
@@ -143,8 +136,6 @@
|
||||
ORK_ENCODE_OBJ(aCoder, recorderConfigurations);
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
BOOL isParentSame = [super isEqual:object];
|
||||
|
||||
@@ -162,13 +153,9 @@
|
||||
(self.shouldVibrateOnStart == castObject.shouldVibrateOnStart) &&
|
||||
(self.shouldVibrateOnFinish == castObject.shouldVibrateOnFinish) &&
|
||||
(self.shouldContinueOnFinish == castObject.shouldContinueOnFinish) &&
|
||||
(self.shouldUseNextAsSkipButton == castObject.shouldUseNextAsSkipButton)) ;
|
||||
(self.shouldUseNextAsSkipButton == castObject.shouldUseNextAsSkipButton));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- (NSSet *)requestedHealthKitTypesForReading {
|
||||
NSMutableSet *set = [NSMutableSet set];
|
||||
for (ORKRecorderConfiguration *config in self.recorderConfigurations) {
|
||||
@@ -178,11 +165,10 @@
|
||||
}
|
||||
}
|
||||
return set;
|
||||
|
||||
}
|
||||
|
||||
- (ORKPermissionMask)requestedPermissions {
|
||||
ORKPermissionMask mask = ORKPermissionNone;
|
||||
ORKPermissionMask mask = [super requestedPermissions];
|
||||
for (ORKRecorderConfiguration *config in self.recorderConfigurations) {
|
||||
mask |= [config requestedPermissionMask];
|
||||
}
|
||||
|
||||
@@ -28,15 +28,18 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit_Private.h>
|
||||
#import "ORKLabel.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ORKQuantityLabel : ORKLabel
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface ORKActiveStepQuantityView : UIView
|
||||
|
||||
@property (nonatomic, strong, nullable) NSString *title;
|
||||
@@ -44,13 +47,12 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property (nonatomic, strong, nullable) UIImage *image;
|
||||
@property (nonatomic) BOOL enabled;
|
||||
|
||||
|
||||
@property (nonatomic, readonly, strong, nullable) UILabel *titleLabel;
|
||||
@property (nonatomic, readonly, strong, nullable) UILabel *valueLabel;
|
||||
|
||||
@property (nonatomic, strong, readonly, nullable) UILabel *titleLabel;
|
||||
@property (nonatomic, strong, readonly, nullable) UILabel *valueLabel;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface ORKQuantityPairView : UIView
|
||||
|
||||
@property (nonatomic, strong, nullable) ORKActiveStepQuantityView *leftView;
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKActiveStepQuantityView.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import "ORKSkin.h"
|
||||
@@ -44,12 +45,13 @@
|
||||
@end
|
||||
|
||||
|
||||
@implementation ORKActiveStepQuantityView
|
||||
{
|
||||
@implementation ORKActiveStepQuantityView {
|
||||
ORKSubheadlineLabel *_titleLabel;
|
||||
ORKQuantityLabel *_valueLabel;
|
||||
ORKTintedImageView *_imageView;
|
||||
UIView *_valueHolder;
|
||||
|
||||
NSLayoutConstraint *_zeroWidthConstraint;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
@@ -82,8 +84,8 @@
|
||||
view.isAccessibilityElement = NO;
|
||||
}
|
||||
|
||||
[self setupConstraints];
|
||||
[self setNeedsUpdateConstraints];
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -109,69 +111,95 @@
|
||||
_imageView.image = image;
|
||||
}
|
||||
|
||||
- (void)updateConstraints {
|
||||
[self removeConstraints:[self constraints]];
|
||||
- (void)setupConstraints {
|
||||
|
||||
const CGFloat TitleBaselineToValueBaseline = 40;
|
||||
const CGFloat ValueBaselineToBottom = 36;
|
||||
|
||||
if (! _enabled) {
|
||||
NSLayoutConstraint *zeroWidthConstraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:0];
|
||||
zeroWidthConstraint.priority = UILayoutPriorityRequired-1;
|
||||
[self addConstraint:zeroWidthConstraint];
|
||||
}
|
||||
NSDictionary *views = NSDictionaryOfVariableBindings(_titleLabel, _valueLabel, _imageView);
|
||||
NSMutableArray *additionalConstraints = [NSMutableArray array];
|
||||
[additionalConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_titleLabel]" options:0 metrics:nil views:views]];
|
||||
[additionalConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_titleLabel]|" options:0 metrics:nil views:views]];
|
||||
[additionalConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_imageView]-10-[_valueLabel]|" options:NSLayoutFormatAlignAllCenterY metrics:nil views:views]];
|
||||
NSDictionary *views = NSDictionaryOfVariableBindings(_titleLabel, _valueLabel, _imageView);
|
||||
[additionalConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_titleLabel]"
|
||||
options:(NSLayoutFormatOptions)0
|
||||
metrics:nil
|
||||
views:views]];
|
||||
[additionalConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_titleLabel]|"
|
||||
options:(NSLayoutFormatOptions)0
|
||||
metrics:nil
|
||||
views:views]];
|
||||
[additionalConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_imageView]-10-[_valueLabel]|"
|
||||
options:NSLayoutFormatAlignAllCenterY
|
||||
metrics:nil
|
||||
views:views]];
|
||||
[additionalConstraints addObject:[NSLayoutConstraint constraintWithItem:_valueLabel
|
||||
attribute:NSLayoutAttributeFirstBaseline
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:_titleLabel
|
||||
attribute:NSLayoutAttributeLastBaseline
|
||||
multiplier:1 constant:TitleBaselineToValueBaseline]];
|
||||
multiplier:1.0
|
||||
constant:TitleBaselineToValueBaseline]];
|
||||
[additionalConstraints addObject:[NSLayoutConstraint constraintWithItem:self
|
||||
attribute:NSLayoutAttributeBottom
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:_valueLabel
|
||||
attribute:NSLayoutAttributeLastBaseline
|
||||
multiplier:1 constant:ValueBaselineToBottom]];
|
||||
multiplier:1.0
|
||||
constant:ValueBaselineToBottom]];
|
||||
[additionalConstraints addObject:[NSLayoutConstraint constraintWithItem:_valueHolder
|
||||
attribute:NSLayoutAttributeCenterX
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self
|
||||
attribute:NSLayoutAttributeCenterX
|
||||
multiplier:1 constant:0]];
|
||||
multiplier:1.0
|
||||
constant:0.0]];
|
||||
[additionalConstraints addObject:[NSLayoutConstraint constraintWithItem:_valueLabel
|
||||
attribute:NSLayoutAttributeTop
|
||||
relatedBy:NSLayoutRelationGreaterThanOrEqual
|
||||
toItem:_valueHolder
|
||||
attribute:NSLayoutAttributeTop
|
||||
multiplier:1 constant:0]];
|
||||
multiplier:1.0
|
||||
constant:0.0]];
|
||||
[additionalConstraints addObject:[NSLayoutConstraint constraintWithItem:_valueLabel
|
||||
attribute:NSLayoutAttributeBottom
|
||||
relatedBy:NSLayoutRelationLessThanOrEqual
|
||||
toItem:_valueHolder
|
||||
attribute:NSLayoutAttributeBottom
|
||||
multiplier:1 constant:0]];
|
||||
multiplier:1.0
|
||||
constant:0.0]];
|
||||
[additionalConstraints addObject:[NSLayoutConstraint constraintWithItem:_valueHolder
|
||||
attribute:NSLayoutAttributeLeft
|
||||
relatedBy:NSLayoutRelationGreaterThanOrEqual
|
||||
toItem:self
|
||||
attribute:NSLayoutAttributeLeft
|
||||
multiplier:1 constant:0]];
|
||||
multiplier:1.0
|
||||
constant:0.0]];
|
||||
[additionalConstraints addObject:[NSLayoutConstraint constraintWithItem:_valueHolder
|
||||
attribute:NSLayoutAttributeRight
|
||||
relatedBy:NSLayoutRelationLessThanOrEqual
|
||||
toItem:self
|
||||
attribute:NSLayoutAttributeRight
|
||||
multiplier:1 constant:0]];
|
||||
multiplier:1.0
|
||||
constant:0.0]];
|
||||
for (NSLayoutConstraint *constraint in additionalConstraints) {
|
||||
constraint.priority = UILayoutPriorityDefaultHigh;
|
||||
constraint.priority = UILayoutPriorityRequired-2;
|
||||
}
|
||||
[self addConstraints:additionalConstraints];
|
||||
|
||||
[NSLayoutConstraint activateConstraints:additionalConstraints];
|
||||
|
||||
|
||||
NSLayoutConstraint *zeroWidthConstraint = [NSLayoutConstraint constraintWithItem:self
|
||||
attribute:NSLayoutAttributeWidth
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:nil
|
||||
attribute:NSLayoutAttributeNotAnAttribute
|
||||
multiplier:1.0
|
||||
constant:0.0];
|
||||
zeroWidthConstraint.priority = UILayoutPriorityRequired-1;
|
||||
_zeroWidthConstraint = zeroWidthConstraint;
|
||||
_zeroWidthConstraint.active = !_enabled;
|
||||
}
|
||||
|
||||
- (void)updateConstraints {
|
||||
_zeroWidthConstraint.active = !_enabled;
|
||||
[super updateConstraints];
|
||||
}
|
||||
|
||||
@@ -195,10 +223,9 @@
|
||||
|
||||
@end
|
||||
|
||||
@implementation ORKQuantityPairView
|
||||
{
|
||||
|
||||
@implementation ORKQuantityPairView {
|
||||
UIView *_metricKeyline;
|
||||
NSArray *_constraints;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
@@ -215,59 +242,88 @@
|
||||
[self setKeylineHidden:NO];
|
||||
_metricKeyline.backgroundColor = [UIColor ork_midGrayTintColor];
|
||||
|
||||
|
||||
[self addSubview:_leftView];
|
||||
[self addSubview:_rightView];
|
||||
[self addSubview:_metricKeyline];
|
||||
[self setNeedsUpdateConstraints];
|
||||
|
||||
[self setupConstraints];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)updateConstraints {
|
||||
- (void)setupConstraints {
|
||||
|
||||
|
||||
if (_constraints) {
|
||||
[self removeConstraints:_constraints];
|
||||
_constraints = nil;
|
||||
}
|
||||
NSMutableArray *constraints = [NSMutableArray array];
|
||||
NSDictionary *views = NSDictionaryOfVariableBindings(_leftView, _rightView, _metricKeyline);
|
||||
|
||||
// Leave space for the keyline between these views, and then constrain it to be 1px wide and go from top to bottom baseline of metric views.
|
||||
CGFloat scale = [[UIScreen mainScreen] scale];
|
||||
NSArray *vertConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_leftView]|" options:(NSLayoutFormatOptions)0 metrics:nil views:views];
|
||||
NSArray *vertConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_leftView]|"
|
||||
options:(NSLayoutFormatOptions)0
|
||||
metrics:nil
|
||||
views:views];
|
||||
[constraints addObjectsFromArray:vertConstraints];
|
||||
|
||||
NSArray *horizConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_leftView]-s-[_rightView]-|" options:NSLayoutFormatAlignAllTop|NSLayoutFormatAlignAllBottom|NSLayoutFormatDirectionLeftToRight metrics:@{@"s":@(1/scale)} views:views];
|
||||
NSArray *horizConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_leftView]-s-[_rightView]-|"
|
||||
options:NSLayoutFormatAlignAllTop|NSLayoutFormatAlignAllBottom|NSLayoutFormatDirectionLeftToRight
|
||||
metrics:@{ @"s": @(1/scale) }
|
||||
views:views];
|
||||
for (NSLayoutConstraint *constraint in horizConstraints) {
|
||||
constraint.priority = UILayoutPriorityDefaultHigh+1;
|
||||
}
|
||||
[constraints addObjectsFromArray:horizConstraints];
|
||||
|
||||
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[_leftView][_metricKeyline(==s)]" options:NSLayoutFormatAlignAllTop|NSLayoutFormatDirectionLeftToRight metrics:@{@"s":@(1/scale)} views:views]];
|
||||
// Ensure baseline alignment of title and value
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_leftView.titleLabel
|
||||
attribute:NSLayoutAttributeFirstBaseline
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:_rightView.titleLabel
|
||||
attribute:NSLayoutAttributeFirstBaseline
|
||||
multiplier:1.0
|
||||
constant:0.0]];
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_leftView.valueLabel
|
||||
attribute:NSLayoutAttributeFirstBaseline
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:_rightView.valueLabel
|
||||
attribute:NSLayoutAttributeFirstBaseline
|
||||
multiplier:1.0
|
||||
constant:0.0]];
|
||||
|
||||
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[_leftView][_metricKeyline(==s)]"
|
||||
options:NSLayoutFormatAlignAllTop|NSLayoutFormatDirectionLeftToRight
|
||||
metrics:@{ @"s": @(1/scale) }
|
||||
views:views]];
|
||||
NSLayoutConstraint *keylineBottom = [NSLayoutConstraint constraintWithItem:_metricKeyline
|
||||
attribute:NSLayoutAttributeBottom
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:_leftView.valueLabel
|
||||
attribute:NSLayoutAttributeLastBaseline
|
||||
multiplier:1 constant:0];
|
||||
multiplier:1.0
|
||||
constant:0.0];
|
||||
[constraints addObject:keylineBottom];
|
||||
|
||||
NSLayoutConstraint *maxWidthConstraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:10000];
|
||||
NSLayoutConstraint *maxWidthConstraint = [NSLayoutConstraint constraintWithItem:self
|
||||
attribute:NSLayoutAttributeWidth
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:nil
|
||||
attribute:NSLayoutAttributeNotAnAttribute
|
||||
multiplier:1.0
|
||||
constant:ORKScreenMetricMaxDimension];
|
||||
maxWidthConstraint.priority = UILayoutPriorityRequired-2;
|
||||
[constraints addObject:maxWidthConstraint];
|
||||
|
||||
|
||||
// This constraint should be beaten out by the full-width-coverage and zero-width constraints if only one of the views is enabled.
|
||||
NSLayoutConstraint *equalWidthConstraint = [NSLayoutConstraint constraintWithItem:_leftView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:_rightView attribute:NSLayoutAttributeWidth multiplier:1 constant:0];
|
||||
NSLayoutConstraint *equalWidthConstraint = [NSLayoutConstraint constraintWithItem:_leftView
|
||||
attribute:NSLayoutAttributeWidth
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:_rightView
|
||||
attribute:NSLayoutAttributeWidth
|
||||
multiplier:1.0
|
||||
constant:0.0];
|
||||
equalWidthConstraint.priority = UILayoutPriorityDefaultLow;
|
||||
[constraints addObject:equalWidthConstraint];
|
||||
|
||||
[self addConstraints:constraints];
|
||||
_constraints = constraints;
|
||||
[super updateConstraints];
|
||||
[NSLayoutConstraint activateConstraints:constraints];
|
||||
}
|
||||
|
||||
- (void)setKeylineHidden:(BOOL)keylineHidden {
|
||||
@@ -276,4 +332,3 @@
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -28,16 +28,17 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ORKActiveStepTimer;
|
||||
|
||||
typedef void (^ORKActiveStepTimerHandler)(ORKActiveStepTimer *timer, BOOL finished);
|
||||
|
||||
|
||||
@interface ORKActiveStepTimer : NSObject
|
||||
|
||||
- (instancetype)initWithDuration:(NSTimeInterval)duration interval:(NSTimeInterval)interval runtime:(NSTimeInterval)runtime handler:(ORKActiveStepTimerHandler)handler;
|
||||
|
||||
@@ -28,24 +28,23 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKActiveStepTimer.h"
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_time.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
|
||||
static NSTimeInterval timeIntervalFromMachTime(uint64_t delta) {
|
||||
|
||||
static mach_timebase_info_data_t sTimebaseInfo;
|
||||
if ( sTimebaseInfo.denom == 0 ) {
|
||||
(void) mach_timebase_info(&sTimebaseInfo);
|
||||
}
|
||||
uint64_t elapsedNano = delta * sTimebaseInfo.numer / sTimebaseInfo.denom;
|
||||
|
||||
return elapsedNano * 1.0 / NSEC_PER_SEC;
|
||||
}
|
||||
|
||||
@implementation ORKActiveStepTimer
|
||||
{
|
||||
@implementation ORKActiveStepTimer {
|
||||
uint64_t _startTime;
|
||||
NSTimeInterval _preExistingRuntime;
|
||||
dispatch_queue_t _queue;
|
||||
@@ -54,7 +53,6 @@ static NSTimeInterval timeIntervalFromMachTime(uint64_t delta) {
|
||||
uint32_t _isRunning;
|
||||
}
|
||||
|
||||
|
||||
- (instancetype)initWithDuration:(NSTimeInterval)duration interval:(NSTimeInterval)interval runtime:(NSTimeInterval)runtime handler:(ORKActiveStepTimerHandler)handler {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
@@ -158,10 +156,10 @@ static NSTimeInterval timeIntervalFromMachTime(uint64_t delta) {
|
||||
if (_backgroundTaskIdentifier == UIBackgroundTaskInvalid) {
|
||||
return;
|
||||
}
|
||||
UIBackgroundTaskIdentifier ident = _backgroundTaskIdentifier;
|
||||
UIBackgroundTaskIdentifier identifier = _backgroundTaskIdentifier;
|
||||
_backgroundTaskIdentifier = UIBackgroundTaskInvalid;
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[[UIApplication sharedApplication] endBackgroundTask:ident];
|
||||
[[UIApplication sharedApplication] endBackgroundTask:identifier];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -178,7 +176,6 @@ static NSTimeInterval timeIntervalFromMachTime(uint64_t delta) {
|
||||
}
|
||||
|
||||
- (void)queue_resume {
|
||||
|
||||
if (_timer != NULL) {
|
||||
// Already resumed
|
||||
return;
|
||||
@@ -211,7 +208,7 @@ static NSTimeInterval timeIntervalFromMachTime(uint64_t delta) {
|
||||
_startTime = mach_absolute_time();
|
||||
dispatch_source_set_timer(_timer,
|
||||
dispatch_time(DISPATCH_TIME_NOW, timeUntilNextFire * NSEC_PER_SEC),
|
||||
_interval * NSEC_PER_SEC ,
|
||||
_interval * NSEC_PER_SEC,
|
||||
0.05 * NSEC_PER_SEC);
|
||||
dispatch_resume(_timer);
|
||||
}
|
||||
@@ -239,5 +236,4 @@ static NSTimeInterval timeIntervalFromMachTime(uint64_t delta) {
|
||||
[self queue_releaseBackgroundTask];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -28,13 +28,14 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <ResearchKit/ResearchKit_Private.h>
|
||||
#import "ORKCustomStepView_Internal.h"
|
||||
#import "ORKCountdownLabel.h"
|
||||
#import "ORKTextButton.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ORKActiveStepTimerView : ORKActiveStepCustomView
|
||||
@@ -46,4 +47,4 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKActiveStepTimerView.h"
|
||||
#import "ORKActiveStepTimer.h"
|
||||
#import "ORKHelpers.h"
|
||||
@@ -41,18 +42,14 @@
|
||||
#import "ORKStepViewController_Internal.h"
|
||||
|
||||
|
||||
@implementation ORKActiveStepTimerView
|
||||
{
|
||||
@implementation ORKActiveStepTimerView {
|
||||
BOOL _started;
|
||||
BOOL _registeredForNotifications;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self)
|
||||
{
|
||||
|
||||
if (self) {
|
||||
// Count Down
|
||||
{
|
||||
_countDownLabel = [ORKCountdownLabel new];
|
||||
@@ -61,10 +58,8 @@
|
||||
|
||||
[self addSubview:_countDownLabel];
|
||||
}
|
||||
|
||||
// Count down start button
|
||||
{
|
||||
|
||||
_startTimerButton = [ORKTextButton new];
|
||||
[_startTimerButton setTitle:ORKLocalizedString(@"BUTTON_START_TIMER", nil) forState:UIControlStateNormal];
|
||||
[_startTimerButton addTarget:self action:@selector(startTimerButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
|
||||
@@ -76,16 +71,14 @@
|
||||
_countDownLabel.accessibilityTraits |= UIAccessibilityTraitUpdatesFrequently;
|
||||
|
||||
[self setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)applicationDidBecomeActive:(NSNotification *)notification {
|
||||
ORKActiveStepViewController *vc = self.activeStepViewController;
|
||||
if (vc) {
|
||||
[self updateDisplay:vc];
|
||||
ORKActiveStepViewController *viewController = self.activeStepViewController;
|
||||
if (viewController) {
|
||||
[self updateDisplay:viewController];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +86,7 @@
|
||||
if (registered == _registeredForNotifications) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
registered = _registeredForNotifications;
|
||||
NSNotificationCenter *nfc = [NSNotificationCenter defaultCenter];
|
||||
if (registered) {
|
||||
@@ -107,9 +100,7 @@
|
||||
[self setRegisteredForNotifications:(self.window != nil)];
|
||||
}
|
||||
|
||||
|
||||
- (void)setStep:(ORKActiveStep *)step
|
||||
{
|
||||
- (void)setStep:(ORKActiveStep *)step {
|
||||
_step = step;
|
||||
_countDownLabel.hidden = !(_step.hasCountDown);
|
||||
BOOL hasTimerButton = (_step.hasCountDown && _step.shouldStartTimerAutomatically == NO);
|
||||
@@ -121,19 +112,16 @@
|
||||
[self setNeedsUpdateConstraints];
|
||||
}
|
||||
|
||||
- (void)startTimerButtonTapped:(id)sender
|
||||
{
|
||||
- (void)startTimerButtonTapped:(id)sender {
|
||||
[self.activeStepViewController start];
|
||||
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, _countDownLabel);
|
||||
}
|
||||
|
||||
|
||||
- (void)updateDisplay:(ORKActiveStepViewController *)viewController {
|
||||
NSInteger countDownValue = (NSInteger)round(viewController.timeRemaining);
|
||||
[_countDownLabel setCountDownValue:countDownValue];
|
||||
}
|
||||
|
||||
|
||||
- (void)resetStep:(ORKActiveStepViewController *)viewController {
|
||||
self.step = (ORKActiveStep *)viewController.step;
|
||||
}
|
||||
@@ -147,8 +135,7 @@
|
||||
|
||||
- (void)resumeStep:(ORKActiveStepViewController *)viewController {
|
||||
self.step = (ORKActiveStep *)viewController.step;
|
||||
if ([viewController timerActive])
|
||||
{
|
||||
if ([viewController timerActive]) {
|
||||
_startTimerButton.alpha = 0;
|
||||
[self updateDisplay:viewController];
|
||||
}
|
||||
@@ -157,44 +144,57 @@
|
||||
- (void)finishStep:(ORKActiveStepViewController *)viewController {
|
||||
}
|
||||
|
||||
- (void)setNeedsUpdateConstraints
|
||||
{
|
||||
- (void)setNeedsUpdateConstraints {
|
||||
[NSLayoutConstraint deactivateConstraints:[self constraints]];
|
||||
[super setNeedsUpdateConstraints];
|
||||
|
||||
}
|
||||
|
||||
- (void)updateConstraints
|
||||
{
|
||||
- (void)updateConstraints {
|
||||
NSDictionary *dictionary = NSDictionaryOfVariableBindings(_countDownLabel, _startTimerButton);
|
||||
NSDictionary *metrics = @{@"CS" : @(2)};
|
||||
ORKEnableAutoLayoutForViews([dictionary allValues]);
|
||||
|
||||
for (UIView *v in [dictionary allValues])
|
||||
{
|
||||
[self addConstraint:[NSLayoutConstraint constraintWithItem:v attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationLessThanOrEqual toItem:self attribute:NSLayoutAttributeWidth multiplier:1 constant:0]];
|
||||
[self addConstraint:[NSLayoutConstraint constraintWithItem:v attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationLessThanOrEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]];
|
||||
for (UIView *view in [dictionary allValues]) {
|
||||
[self addConstraint:[NSLayoutConstraint
|
||||
constraintWithItem:view
|
||||
attribute:NSLayoutAttributeWidth
|
||||
relatedBy:NSLayoutRelationLessThanOrEqual
|
||||
toItem:self
|
||||
attribute:NSLayoutAttributeWidth
|
||||
multiplier:1
|
||||
constant:0]];
|
||||
[self addConstraint:[NSLayoutConstraint
|
||||
constraintWithItem:view
|
||||
attribute:NSLayoutAttributeCenterX
|
||||
relatedBy:NSLayoutRelationLessThanOrEqual
|
||||
toItem:self
|
||||
attribute:NSLayoutAttributeCenterX
|
||||
multiplier:1
|
||||
constant:0]];
|
||||
}
|
||||
|
||||
if (! _countDownLabel.hidden)
|
||||
{
|
||||
if (! _countDownLabel.hidden) {
|
||||
NSMutableString *verticalLayout = [NSMutableString new];
|
||||
[verticalLayout appendString:@"V:|[_countDownLabel]"];
|
||||
if (! _startTimerButton.hidden)
|
||||
{
|
||||
if (! _startTimerButton.hidden) {
|
||||
[verticalLayout appendString:@"-CS-[_startTimerButton]"];
|
||||
}
|
||||
[verticalLayout appendString:@"|"];
|
||||
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:verticalLayout options:NSLayoutFormatAlignAllCenterX metrics:metrics views:dictionary]];
|
||||
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:verticalLayout
|
||||
options:NSLayoutFormatAlignAllCenterX
|
||||
metrics:metrics
|
||||
views:dictionary]];
|
||||
} else {
|
||||
[self addConstraint:[NSLayoutConstraint constraintWithItem:self
|
||||
attribute:NSLayoutAttributeHeight
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:nil
|
||||
attribute:NSLayoutAttributeNotAnAttribute
|
||||
multiplier:1
|
||||
constant:0]];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:0]];
|
||||
}
|
||||
|
||||
[super updateConstraints];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
/**
|
||||
The `ORKActiveStepViewController` class is the base class for displaying `ORKActiveStep`
|
||||
subclasses. The predefined active tasks defined in `ORKOrderedTask` all make use
|
||||
@@ -105,10 +104,8 @@ ORK_CLASS_AVAILABLE
|
||||
*/
|
||||
@property (nonatomic, strong, readonly, nullable) NSArray *recorders;
|
||||
|
||||
|
||||
/// @name Active step life cycle
|
||||
|
||||
|
||||
/**
|
||||
A Boolean value that indicates whether the step has finished. (read-only)
|
||||
|
||||
@@ -118,8 +115,7 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
In addition, when a step is finished, all recorders are stopped.
|
||||
*/
|
||||
@property (nonatomic, assign, getter=isFinished, readonly) BOOL finished;
|
||||
|
||||
@property (nonatomic, assign, readonly, getter=isFinished) BOOL finished;
|
||||
|
||||
/**
|
||||
A Boolean value that indicates whether the step has started. (read-only)
|
||||
@@ -140,7 +136,6 @@ ORK_CLASS_AVAILABLE
|
||||
*/
|
||||
- (void)stepDidFinish;
|
||||
|
||||
|
||||
/**
|
||||
A Boolean value that indicates whether to suspend the step if the app is not
|
||||
active or the screen is off.
|
||||
|
||||
@@ -48,7 +48,8 @@
|
||||
#import "ORKStepHeaderView_Internal.h"
|
||||
#import "ORKActiveStepView.h"
|
||||
|
||||
@interface ORKActiveStepViewController (){
|
||||
|
||||
@interface ORKActiveStepViewController () {
|
||||
ORKActiveStepView *_activeStepView;
|
||||
ORKActiveStepTimer *_activeStepTimer;
|
||||
|
||||
@@ -76,10 +77,8 @@
|
||||
_timerUpdateInterval = 1;
|
||||
}
|
||||
return self;
|
||||
|
||||
}
|
||||
|
||||
|
||||
- (void)applicationWillResignActive:(NSNotification *)notification {
|
||||
if (self.suspendIfInactive) {
|
||||
[self suspend];
|
||||
@@ -100,8 +99,7 @@
|
||||
return _activeStepView;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
_activeStepView = [[ORKActiveStepView alloc] initWithFrame:self.view.bounds];
|
||||
@@ -119,8 +117,8 @@
|
||||
|
||||
[self prepareStep];
|
||||
}
|
||||
- (void)stepDidChange
|
||||
{
|
||||
|
||||
- (void)stepDidChange {
|
||||
[super stepDidChange];
|
||||
_activeStepView.activeStep = [self activeStep];
|
||||
[self updateContinueButtonItem];
|
||||
@@ -129,20 +127,17 @@
|
||||
[self prepareStep];
|
||||
}
|
||||
|
||||
- (UIView *)customViewContainer
|
||||
{
|
||||
__unused UIView *v = [self view];
|
||||
- (UIView *)customViewContainer {
|
||||
__unused UIView *view = [self view];
|
||||
return _activeStepView.customViewContainer;
|
||||
}
|
||||
|
||||
- (UIImageView *)imageView
|
||||
{
|
||||
__unused UIView *v = [self view];
|
||||
- (ORKTintedImageView *)imageView {
|
||||
__unused UIView *view = [self view];
|
||||
return _activeStepView.imageView;
|
||||
}
|
||||
|
||||
- (void)setCustomView:(UIView *)customView
|
||||
{
|
||||
- (void)setCustomView:(UIView *)customView {
|
||||
_customView = customView;
|
||||
[_activeStepView setStepView:_customView];
|
||||
}
|
||||
@@ -154,21 +149,16 @@
|
||||
[self.taskViewController setRegisteredScrollView:_activeStepView];
|
||||
}
|
||||
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
[super viewDidAppear:animated];
|
||||
ORK_Log_Debug(@"%@",self);
|
||||
|
||||
// Wait for animation complete
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
if ([[self activeStep] shouldStartTimerAutomatically])
|
||||
{
|
||||
if ([[self activeStep] shouldStartTimerAutomatically]) {
|
||||
[self start];
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
@@ -187,22 +177,18 @@
|
||||
[self updateContinueButtonItem];
|
||||
}
|
||||
|
||||
- (void)setLearnMoreButtonItem:(UIBarButtonItem *)learnMoreButtonItem
|
||||
{
|
||||
- (void)setLearnMoreButtonItem:(UIBarButtonItem *)learnMoreButtonItem {
|
||||
[super setLearnMoreButtonItem:learnMoreButtonItem];
|
||||
_activeStepView.headerView.learnMoreButtonItem = self.learnMoreButtonItem;
|
||||
}
|
||||
|
||||
- (void)setSkipButtonItem:(UIBarButtonItem *)skipButtonItem
|
||||
{
|
||||
- (void)setSkipButtonItem:(UIBarButtonItem *)skipButtonItem {
|
||||
[super setSkipButtonItem:skipButtonItem];
|
||||
_activeStepView.continueSkipContainer.skipButtonItem = skipButtonItem;
|
||||
}
|
||||
|
||||
- (void)setFinished:(BOOL)finished
|
||||
{
|
||||
- (void)setFinished:(BOOL)finished {
|
||||
_finished = finished;
|
||||
|
||||
_activeStepView.continueSkipContainer.continueEnabled = finished;
|
||||
}
|
||||
|
||||
@@ -215,28 +201,21 @@
|
||||
#pragma mark - transition
|
||||
|
||||
- (void)recordersDidChange {
|
||||
|
||||
}
|
||||
|
||||
- (void)recordersWillStart {
|
||||
|
||||
}
|
||||
|
||||
- (void)recordersWillStop {
|
||||
|
||||
}
|
||||
|
||||
|
||||
- (void)prepareRecorders
|
||||
{
|
||||
- (void)prepareRecorders {
|
||||
// Stop any existing recorders
|
||||
[self recordersWillStop];
|
||||
for (ORKRecorder *recorder in self.recorders)
|
||||
{
|
||||
for (ORKRecorder *recorder in self.recorders) {
|
||||
recorder.delegate = nil;
|
||||
[recorder stop];
|
||||
}
|
||||
|
||||
NSMutableArray *recorders = [NSMutableArray array];
|
||||
|
||||
for (ORKRecorderConfiguration * provider in self.activeStep.recorderConfigurations) {
|
||||
@@ -249,7 +228,6 @@
|
||||
|
||||
[recorders addObject:recorder];
|
||||
}
|
||||
|
||||
self.recorders = recorders;
|
||||
|
||||
[self recordersDidChange];
|
||||
@@ -261,8 +239,7 @@
|
||||
}
|
||||
|
||||
- (void)prepareStep {
|
||||
|
||||
if (self.activeStep==nil) {
|
||||
if (self.activeStep == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -271,13 +248,10 @@
|
||||
ORK_Log_Debug(@"%@", self);
|
||||
_activeStepView.activeStep = self.activeStep;
|
||||
|
||||
if ([self.activeStep hasCountDown])
|
||||
{
|
||||
if ([self.activeStep hasCountDown]) {
|
||||
ORKActiveStepTimerView *timerView = [ORKActiveStepTimerView new];
|
||||
_activeStepView.activeCustomView = timerView;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
_activeStepView.activeCustomView = nil;
|
||||
}
|
||||
_activeStepView.activeCustomView.activeStepViewController = self;
|
||||
@@ -291,7 +265,6 @@
|
||||
[self recordersWillStart];
|
||||
// Start recorders
|
||||
for (ORKRecorder *recorder in self.recorders) {
|
||||
|
||||
[recorder viewController:self willStartStepWithView:self.customViewContainer];
|
||||
[recorder start];
|
||||
}
|
||||
@@ -304,8 +277,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)playSound
|
||||
{
|
||||
- (void)playSound {
|
||||
if (_alertSoundURL == nil) {
|
||||
_alertSoundURL = [NSURL URLWithString:@"/System/Library/Audio/UISounds/short_low_high.caf"];
|
||||
AudioServicesCreateSystemSoundID((__bridge CFURLRef)(_alertSoundURL), &_alertSound);
|
||||
@@ -321,14 +293,11 @@
|
||||
|
||||
[self startRecorders];
|
||||
|
||||
|
||||
if (self.activeStep.shouldVibrateOnStart) {
|
||||
|
||||
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
|
||||
}
|
||||
|
||||
if (self.activeStep.shouldPlaySoundOnStart) {
|
||||
|
||||
[self playSound];
|
||||
}
|
||||
|
||||
@@ -337,7 +306,7 @@
|
||||
// Let VO speak "Step x of y" before the instruction.
|
||||
// If VO is not running, the text is spoken immediately.
|
||||
ORKAccessibilityPerformBlockAfterDelay(1.5, ^{
|
||||
[[ORKVoiceEngine sharedVoiceEngine] speakText:(NSString *__nonnull)self.activeStep.spokenInstruction];
|
||||
[[ORKVoiceEngine sharedVoiceEngine] speakText:self.activeStep.spokenInstruction];
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -366,7 +335,6 @@
|
||||
[_activeStepView.activeCustomView resumeStep:self];
|
||||
}
|
||||
|
||||
|
||||
- (void)finish {
|
||||
ORK_Log_Debug(@"%@",self);
|
||||
if (self.finished) {
|
||||
@@ -405,14 +373,12 @@
|
||||
_activeStepTimer = nil;
|
||||
}
|
||||
|
||||
- (void)startTimer
|
||||
{
|
||||
- (void)startTimer {
|
||||
[self resetTimer];
|
||||
|
||||
NSTimeInterval stepDuration = self.activeStep.stepDuration;
|
||||
|
||||
if (stepDuration > 0) {
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
_activeStepTimer = [[ORKActiveStepTimer alloc] initWithDuration:stepDuration
|
||||
interval:_timerUpdateInterval
|
||||
@@ -426,7 +392,6 @@
|
||||
}
|
||||
|
||||
- (void)countDownTimerFired:(ORKActiveStepTimer *)timer finished:(BOOL)finished {
|
||||
|
||||
if (finished) {
|
||||
[self finish];
|
||||
}
|
||||
@@ -462,14 +427,12 @@
|
||||
|
||||
#pragma mark - action handlers
|
||||
|
||||
- (void)stepDidFinish
|
||||
{
|
||||
- (void)stepDidFinish {
|
||||
}
|
||||
|
||||
#pragma mark - ORKRecorderDelegate
|
||||
|
||||
- (void)recorder:(ORKRecorder *)recorder didCompleteWithResult:(ORKResult *)result {
|
||||
|
||||
_recorderResults = [_recorderResults arrayByAddingObject:result];
|
||||
[self notifyDelegateOnResultChange];
|
||||
}
|
||||
@@ -492,25 +455,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
static NSString *const _ORKFinishedRestoreKey = @"finished";
|
||||
static NSString *const _ORKRecorderResultsRestoreKey = @"recorderResults";
|
||||
|
||||
static NSString * const _ORKFinishedRestoreKey = @"finished";
|
||||
static NSString * const _ORKRecorderResultsRestoreKey = @"recorderResults";
|
||||
|
||||
- (void)encodeRestorableStateWithCoder:(NSCoder *)coder
|
||||
{
|
||||
- (void)encodeRestorableStateWithCoder:(NSCoder *)coder {
|
||||
[super encodeRestorableStateWithCoder:coder];
|
||||
|
||||
[coder encodeBool:_finished forKey:_ORKFinishedRestoreKey];
|
||||
[coder encodeObject:_recorderResults forKey:_ORKRecorderResultsRestoreKey];
|
||||
}
|
||||
|
||||
- (void)decodeRestorableStateWithCoder:(NSCoder *)coder
|
||||
{
|
||||
- (void)decodeRestorableStateWithCoder:(NSCoder *)coder {
|
||||
[super decodeRestorableStateWithCoder:coder];
|
||||
|
||||
self.finished = [coder decodeBoolForKey:_ORKFinishedRestoreKey];
|
||||
_recorderResults = [coder decodeObjectOfClass:[NSArray class] forKey:_ORKRecorderResultsRestoreKey];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -29,20 +29,20 @@
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#import "ORKActiveStepViewController.h"
|
||||
#import "ORKActiveStepTimer.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ORKActiveStepView;
|
||||
|
||||
@interface ORKActiveStepViewController ()
|
||||
|
||||
|
||||
/**
|
||||
* The customViewContainer allows custom view to be its subview.
|
||||
* @note When ORKTouchRecorder is present, its gesture recognizer attaches to customViewContainer.
|
||||
The customViewContainer allows custom view to be its subview.
|
||||
|
||||
@note When ORKTouchRecorder is present, its gesture recognizer attaches to customViewContainer.
|
||||
*/
|
||||
@property (nonatomic, strong, readonly, nullable) UIView *customViewContainer;
|
||||
|
||||
@@ -50,10 +50,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@property (nonatomic, readonly) NSTimeInterval timeRemaining;
|
||||
@property (nonatomic, readonly) BOOL timerActive;
|
||||
|
||||
@property (nonatomic, assign) NSTimeInterval timerUpdateInterval;
|
||||
|
||||
|
||||
@property (nonatomic, assign, getter=isStarted) BOOL started;
|
||||
|
||||
- (void)countDownTimerFired:(ORKActiveStepTimer *)timer finished:(BOOL)finished; // Let subclass receive timer fires
|
||||
@@ -61,6 +59,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- (void)applicationWillResignActive:(NSNotification *)notification;
|
||||
- (void)applicationDidBecomeActive:(NSNotification *)notification;
|
||||
|
||||
- (void)stopRecorders;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -28,12 +28,14 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ORKActiveStep.h>
|
||||
|
||||
|
||||
@interface ORKActiveStep ()
|
||||
|
||||
/**
|
||||
* Convenience methods.
|
||||
Convenience methods.
|
||||
*/
|
||||
- (BOOL)startsFinished;
|
||||
- (BOOL)hasCountDown;
|
||||
@@ -41,5 +43,4 @@
|
||||
- (BOOL)hasText;
|
||||
- (BOOL)hasVoice;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -28,9 +28,11 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "ORKCustomStepView_Internal.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ORKAudioContentView : ORKActiveStepCustomView
|
||||
@@ -38,6 +40,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property (nonatomic, copy, nullable) UIColor *keyColor;
|
||||
@property (nonatomic, copy, nullable) UIColor *alertColor;
|
||||
|
||||
@property (nonatomic, assign) BOOL failed;
|
||||
@property (nonatomic, assign, getter=isFinished) BOOL finished;
|
||||
@property (nonatomic, assign) NSTimeInterval timeLeft;
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKAudioContentView.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import "ORKSkin.h"
|
||||
@@ -35,6 +36,7 @@
|
||||
#import "ORKHeadlineLabel.h"
|
||||
#import "ORKAccessibility.h"
|
||||
|
||||
|
||||
// The central blue region.
|
||||
static const CGFloat GraphViewBlueZoneHeight = 170;
|
||||
|
||||
@@ -53,6 +55,7 @@ static const CGFloat GraphViewRedZoneHeight = 25;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
static const CGFloat kValueLineWidth = 4.5;
|
||||
static const CGFloat kValueLineMargin = 1.5;
|
||||
|
||||
@@ -61,17 +64,23 @@ static const CGFloat kValueLineMargin = 1.5;
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
NSLayoutConstraint *c1 = [NSLayoutConstraint constraintWithItem:self
|
||||
attribute:NSLayoutAttributeWidth
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:CGFLOAT_MAX];
|
||||
c1.priority = UILayoutPriorityFittingSizeLevel;
|
||||
NSLayoutConstraint *c2 = [NSLayoutConstraint constraintWithItem:self
|
||||
NSLayoutConstraint *constraint1 = [NSLayoutConstraint constraintWithItem:self
|
||||
attribute:NSLayoutAttributeWidth
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:nil
|
||||
attribute:NSLayoutAttributeNotAnAttribute
|
||||
multiplier:1
|
||||
constant:CGFLOAT_MAX];
|
||||
constraint1.priority = UILayoutPriorityFittingSizeLevel;
|
||||
NSLayoutConstraint *constraint2 = [NSLayoutConstraint constraintWithItem:self
|
||||
attribute:NSLayoutAttributeHeight
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:CGFLOAT_MAX];
|
||||
c2.priority = UILayoutPriorityFittingSizeLevel;
|
||||
[NSLayoutConstraint activateConstraints:@[c1,c2]];
|
||||
toItem:nil
|
||||
attribute:NSLayoutAttributeNotAnAttribute
|
||||
multiplier:1
|
||||
constant:CGFLOAT_MAX];
|
||||
constraint2.priority = UILayoutPriorityFittingSizeLevel;
|
||||
[NSLayoutConstraint activateConstraints:@[constraint1, constraint2]];
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
_values = @[@(0.2),@(0.6),@(0.55), @(0.1), @(0.75), @(0.7)];
|
||||
@@ -101,39 +110,39 @@ static const CGFloat kValueLineMargin = 1.5;
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)rect {
|
||||
CGRect r = self.bounds;
|
||||
CGRect bounds = self.bounds;
|
||||
|
||||
CGContextRef ctx = UIGraphicsGetCurrentContext();
|
||||
CGContextSetFillColorWithColor(ctx, [UIColor whiteColor].CGColor);
|
||||
CGContextFillRect(ctx, r);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
|
||||
CGContextFillRect(context, bounds);
|
||||
|
||||
CGFloat scale = [self.window.screen scale];
|
||||
|
||||
CGFloat midY = CGRectGetMidY(r);
|
||||
CGFloat maxX = CGRectGetMaxX(r);
|
||||
CGFloat halfHeight = r.size.height/2;
|
||||
CGContextSaveGState(ctx);
|
||||
CGFloat midY = CGRectGetMidY(bounds);
|
||||
CGFloat maxX = CGRectGetMaxX(bounds);
|
||||
CGFloat halfHeight = bounds.size.height/2;
|
||||
CGContextSaveGState(context);
|
||||
{
|
||||
UIBezierPath *centerLine = [UIBezierPath new];
|
||||
[centerLine moveToPoint:(CGPoint){.x=0,.y=midY}];
|
||||
[centerLine addLineToPoint:(CGPoint){.x=maxX,.y=midY}];
|
||||
|
||||
CGContextSetLineWidth(ctx, 1/scale);
|
||||
CGContextSetLineWidth(context, 1/scale);
|
||||
[_keyColor setStroke];
|
||||
CGFloat lengths[2] = {3,3};
|
||||
CGContextSetLineDash(ctx, 0, lengths, 2);
|
||||
CGContextSetLineDash(context, 0, lengths, 2);
|
||||
|
||||
[centerLine stroke];
|
||||
}
|
||||
CGContextRestoreGState(ctx);
|
||||
CGContextRestoreGState(context);
|
||||
|
||||
CGFloat lineStep = kValueLineMargin + kValueLineWidth;
|
||||
|
||||
CGContextSaveGState(ctx);
|
||||
CGContextSaveGState(context);
|
||||
{
|
||||
CGFloat x = maxX - lineStep/2;
|
||||
CGContextSetLineWidth(ctx, kValueLineWidth);
|
||||
CGContextSetLineCap(ctx, kCGLineCapRound);
|
||||
CGContextSetLineWidth(context, kValueLineWidth);
|
||||
CGContextSetLineCap(context, kCGLineCapRound);
|
||||
|
||||
UIBezierPath *path1 = [UIBezierPath new];
|
||||
path1.lineCapStyle = kCGLineCapRound;
|
||||
@@ -143,16 +152,16 @@ static const CGFloat kValueLineMargin = 1.5;
|
||||
for (NSNumber *value in [_values reverseObjectEnumerator]) {
|
||||
CGFloat floatValue = [value doubleValue];
|
||||
|
||||
UIBezierPath *p = nil;
|
||||
UIBezierPath *path = nil;
|
||||
if (floatValue > _alertThreshold) {
|
||||
p = path1;
|
||||
path = path1;
|
||||
[_alertColor setStroke];
|
||||
} else {
|
||||
p = path2;
|
||||
path = path2;
|
||||
[_keyColor setStroke];
|
||||
}
|
||||
[p moveToPoint:(CGPoint){.x=x,.y=midY-floatValue*halfHeight}];
|
||||
[p addLineToPoint:(CGPoint){.x=x,.y=midY+floatValue*halfHeight}];
|
||||
[path moveToPoint:(CGPoint){.x=x,.y=midY-floatValue*halfHeight}];
|
||||
[path addLineToPoint:(CGPoint){.x=x,.y=midY+floatValue*halfHeight}];
|
||||
|
||||
x -= lineStep;
|
||||
|
||||
@@ -169,29 +178,29 @@ static const CGFloat kValueLineMargin = 1.5;
|
||||
[path2 stroke];
|
||||
|
||||
}
|
||||
CGContextRestoreGState(ctx);
|
||||
|
||||
CGContextRestoreGState(context);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface ORKAudioTimerLabel : ORKLabel
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ORKAudioTimerLabel
|
||||
|
||||
+ (UIFont *)defaultFont {
|
||||
|
||||
UIFontDescriptor *descriptor = [UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleSubheadline];
|
||||
UIFontDescriptor *alternativeDescriptor = ORKFontDescriptorForLightStylisticAlternative(descriptor);
|
||||
return [UIFont fontWithDescriptor:alternativeDescriptor size:[alternativeDescriptor pointSize]+4];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@interface ORKAudioContentView()
|
||||
|
||||
@interface ORKAudioContentView ()
|
||||
|
||||
@property (nonatomic, strong) ORKHeadlineLabel *alertLabel;
|
||||
@property (nonatomic, strong) UILabel *timerLabel;
|
||||
@@ -199,8 +208,8 @@ static const CGFloat kValueLineMargin = 1.5;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ORKAudioContentView
|
||||
{
|
||||
|
||||
@implementation ORKAudioContentView {
|
||||
NSArray *_constraints;
|
||||
NSMutableArray *_samples;
|
||||
UIColor *_keyColor;
|
||||
@@ -209,8 +218,7 @@ static const CGFloat kValueLineMargin = 1.5;
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
CGFloat margin = ORKStandardMarginForView(self);
|
||||
self.layoutMargins = (UIEdgeInsets){.left=2*margin,.right=2*margin};
|
||||
self.layoutMargins = ORKStandardFullScreenLayoutMarginsForView(self);
|
||||
|
||||
self.alertLabel = [ORKHeadlineLabel new];
|
||||
_alertLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
@@ -243,6 +251,12 @@ static const CGFloat kValueLineMargin = 1.5;
|
||||
[self applyKeyColor];
|
||||
}
|
||||
|
||||
-(void)setFailed:(BOOL)failed {
|
||||
_failed = failed;
|
||||
_alertLabel.text = failed ? ORKLocalizedString(@"AUDIO_GENERIC_ERROR_LABEL", nil) : ORKLocalizedString(@"AUDIO_TOO_LOUD_LABEL", nil);
|
||||
[self updateAlertLabelHidden];
|
||||
}
|
||||
|
||||
- (void)setFinished:(BOOL)finished {
|
||||
_finished = finished;
|
||||
[self updateAlertLabelHidden];
|
||||
@@ -265,7 +279,6 @@ static const CGFloat kValueLineMargin = 1.5;
|
||||
|
||||
- (void)setAlertColor:(UIColor *)alertColor {
|
||||
_alertColor = alertColor;
|
||||
|
||||
_alertLabel.textColor = alertColor;
|
||||
_graphView.alertColor = alertColor;
|
||||
}
|
||||
@@ -281,21 +294,34 @@ static const CGFloat kValueLineMargin = 1.5;
|
||||
NSDictionary *views = NSDictionaryOfVariableBindings(_timerLabel, _alertLabel, _graphView);
|
||||
[constraints addObjectsFromArray:
|
||||
[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_graphView]-[_alertLabel]|"
|
||||
options:(NSLayoutFormatOptions)0
|
||||
metrics:nil views:views]];
|
||||
options:0
|
||||
metrics:nil
|
||||
views:views]];
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_alertLabel
|
||||
attribute:NSLayoutAttributeCenterX
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self
|
||||
attribute:NSLayoutAttributeCenterX
|
||||
multiplier:1 constant:0]];
|
||||
multiplier:1
|
||||
constant:0]];
|
||||
|
||||
const CGFloat sideMargin = self.layoutMargins.left + (2 * ORKStandardLeftMarginForTableViewCell(self));
|
||||
const CGFloat innerMargin = 2;
|
||||
|
||||
[constraints addObjectsFromArray:
|
||||
[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_graphView]-2-[_timerLabel]-|"
|
||||
[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-sideMargin-[_graphView]-innerMargin-[_timerLabel]-sideMargin-|"
|
||||
options:NSLayoutFormatAlignAllCenterY
|
||||
metrics:nil views:views]];
|
||||
metrics:@{@"sideMargin": @(sideMargin), @"innerMargin": @(innerMargin)}
|
||||
views:views]];
|
||||
|
||||
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_graphView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:(GraphViewBlueZoneHeight+GraphViewRedZoneHeight*2)]];
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_graphView
|
||||
attribute:NSLayoutAttributeHeight
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:nil
|
||||
attribute:NSLayoutAttributeNotAnAttribute
|
||||
multiplier:1
|
||||
constant:(GraphViewBlueZoneHeight+GraphViewRedZoneHeight*2)]];
|
||||
|
||||
_constraints = constraints;
|
||||
[NSLayoutConstraint activateConstraints:constraints];
|
||||
@@ -317,17 +343,16 @@ static const CGFloat kValueLineMargin = 1.5;
|
||||
static NSDateComponentsFormatter *_formatter = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
NSDateComponentsFormatter *fmt = [NSDateComponentsFormatter new];
|
||||
fmt.unitsStyle = NSDateComponentsFormatterUnitsStylePositional;
|
||||
fmt.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorPad;
|
||||
fmt.allowedUnits = NSCalendarUnitMinute | NSCalendarUnitSecond;
|
||||
_formatter = fmt;
|
||||
NSDateComponentsFormatter *formatter = [NSDateComponentsFormatter new];
|
||||
formatter.unitsStyle = NSDateComponentsFormatterUnitsStylePositional;
|
||||
formatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorPad;
|
||||
formatter.allowedUnits = NSCalendarUnitMinute | NSCalendarUnitSecond;
|
||||
_formatter = formatter;
|
||||
});
|
||||
|
||||
NSString *s = [_formatter stringFromTimeInterval:MAX(round(_timeLeft),0)];
|
||||
_timerLabel.text = s;
|
||||
_timerLabel.hidden = (s == nil);
|
||||
|
||||
NSString *string = [_formatter stringFromTimeInterval:MAX(round(_timeLeft),0)];
|
||||
_timerLabel.text = string;
|
||||
_timerLabel.hidden = (string == nil);
|
||||
}
|
||||
|
||||
- (void)updateGraphSamples {
|
||||
@@ -337,8 +362,8 @@ static const CGFloat kValueLineMargin = 1.5;
|
||||
|
||||
- (void)updateAlertLabelHidden {
|
||||
NSNumber *sample = [_samples lastObject];
|
||||
BOOL hide = _finished || !([sample doubleValue] > _alertThreshold);
|
||||
_alertLabel.hidden = hide;
|
||||
BOOL show = (! _finished && ([sample doubleValue] > _alertThreshold)) || _failed;
|
||||
_alertLabel.hidden = !show;
|
||||
}
|
||||
|
||||
- (void)setSamples:(NSArray *)samples {
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
Copyright (c) 2015, Shazino SAS. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder(s) nor the names of any contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission. No license is granted to the trademarks of
|
||||
the copyright holders even if such marks are included in this software.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
---
|
||||
|
||||
This software is based on the original source by Matt Gallagher.
|
||||
http://www.cocoawithlove.com/2010/10/ios-tone-generator-introduction-to.html
|
||||
|
||||
Copyright (c) 2009-2011 Matt Gallagher. All rights reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied warranty. In
|
||||
no event will the authors be held liable for any damages arising from the use
|
||||
of this software. Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not claim
|
||||
that you wrote the original software. If you use this software in a product,
|
||||
an acknowledgment in the product documentation would be appreciated but is
|
||||
not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#import <ResearchKit/ORKRecorder.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
The `ORKAudioGenerator` class represents an audio tone generator.
|
||||
*/
|
||||
ORK_CLASS_AVAILABLE
|
||||
@interface ORKAudioGenerator : NSObject
|
||||
|
||||
/**
|
||||
Plays a tone at a specific frequency in stereo.
|
||||
|
||||
The sound is a "pure" sinusoid tone.
|
||||
|
||||
@param frequency The audio frequency in hertz.
|
||||
*/
|
||||
- (void)playSoundAtFrequency:(double)frequency;
|
||||
|
||||
/**
|
||||
Plays a tone at a specific frequency on a specific channel, with a fade-in effect.
|
||||
|
||||
The sound is a "pure" sinusoid tone.
|
||||
The fade-in effect is applied linearly for the peak amplitude, from a 0 to 1 factor.
|
||||
|
||||
@param frequency The audio frequency in hertz.
|
||||
@param channel The audio channel (left or right).
|
||||
@param duration The fade-in duration.
|
||||
*/
|
||||
- (void)playSoundAtFrequency:(double)frequency
|
||||
onChannel:(ORKAudioChannel)channel
|
||||
fadeInDuration:(NSTimeInterval)duration;
|
||||
|
||||
/**
|
||||
Stops the audio being played.
|
||||
*/
|
||||
- (void)stop;
|
||||
|
||||
/**
|
||||
Returns the peak audio volume being currently played, in decibels (dB).
|
||||
|
||||
@return The current audio volume in decibels.
|
||||
*/
|
||||
- (double)volumeInDecibels;
|
||||
|
||||
/**
|
||||
Returns the peak audio volume amplitude being currently played (from 0 to 1).
|
||||
|
||||
@return The current audio volume amplitude.
|
||||
*/
|
||||
- (double)volumeAmplitude;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
Copyright (c) 2015, Shazino SAS. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder(s) nor the names of any contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission. No license is granted to the trademarks of
|
||||
the copyright holders even if such marks are included in this software.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
---
|
||||
|
||||
This software is based on the original source by Matt Gallagher.
|
||||
http://www.cocoawithlove.com/2010/10/ios-tone-generator-introduction-to.html
|
||||
|
||||
Copyright (c) 2009-2011 Matt Gallagher. All rights reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied warranty. In
|
||||
no event will the authors be held liable for any damages arising from the use
|
||||
of this software. Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not claim
|
||||
that you wrote the original software. If you use this software in a product,
|
||||
an acknowledgment in the product documentation would be appreciated but is
|
||||
not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#import "ORKAudioGenerator.h"
|
||||
|
||||
@import AudioToolbox;
|
||||
|
||||
@interface ORKAudioGenerator () {
|
||||
@public
|
||||
AudioComponentInstance _toneUnit;
|
||||
|
||||
@public
|
||||
double _frequency;
|
||||
double _theta;
|
||||
ORKAudioChannel _activeChannel;
|
||||
BOOL _playsStereo;
|
||||
double _fadeInFactor;
|
||||
NSTimeInterval _fadeInDuration;
|
||||
}
|
||||
|
||||
- (void)setupAudioSession;
|
||||
- (void)createToneUnit;
|
||||
- (void)play;
|
||||
- (void)handleInterruption:(id)sender;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
const double ORKSineWaveToneGeneratorAmplitudeDefault = 0.03f;
|
||||
const double ORKSineWaveToneGeneratorSampleRateDefault = 44100.0f;
|
||||
|
||||
OSStatus ORKAudioGeneratorRenderTone(void *inRefCon,
|
||||
AudioUnitRenderActionFlags *ioActionFlags,
|
||||
const AudioTimeStamp *inTimeStamp,
|
||||
UInt32 inBusNumber,
|
||||
UInt32 inNumberFrames,
|
||||
AudioBufferList *ioData) {
|
||||
// Fixed amplitude is good enough for our purposes
|
||||
const double amplitude = ORKSineWaveToneGeneratorAmplitudeDefault;
|
||||
|
||||
// Get the tone parameters out of the view controller
|
||||
ORKAudioGenerator *audioGenerator = (__bridge ORKAudioGenerator *)inRefCon;
|
||||
double theta = audioGenerator->_theta;
|
||||
double theta_increment = 2.0 * M_PI * audioGenerator->_frequency / ORKSineWaveToneGeneratorSampleRateDefault;
|
||||
|
||||
double fadeInFactor = audioGenerator->_fadeInFactor;
|
||||
|
||||
// This is a mono tone generator so we only need the first buffer
|
||||
Float32 *bufferActive = (Float32 *)ioData->mBuffers[audioGenerator->_activeChannel].mData;
|
||||
Float32 *bufferNonActive = (Float32 *)ioData->mBuffers[1 - audioGenerator->_activeChannel].mData;
|
||||
|
||||
// Generate the samples
|
||||
for (UInt32 frame = 0; frame < inNumberFrames; frame++) {
|
||||
double bufferValue = sin(theta) * amplitude * pow(10, 2 * fadeInFactor - 2);
|
||||
bufferActive[frame] = bufferValue;
|
||||
if (audioGenerator->_playsStereo) {
|
||||
bufferNonActive[frame] = bufferValue;
|
||||
}
|
||||
else {
|
||||
bufferNonActive[frame] = 0;
|
||||
}
|
||||
|
||||
theta += theta_increment;
|
||||
if (theta > 2.0 * M_PI) {
|
||||
theta -= 2.0 * M_PI;
|
||||
}
|
||||
|
||||
fadeInFactor += 1/(ORKSineWaveToneGeneratorSampleRateDefault * audioGenerator->_fadeInDuration);
|
||||
if (fadeInFactor >= 1) {
|
||||
fadeInFactor = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Store the theta back in the view controller
|
||||
audioGenerator->_theta = theta;
|
||||
audioGenerator->_fadeInFactor = fadeInFactor;
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
|
||||
@implementation ORKAudioGenerator
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
[self setupAudioSession];
|
||||
|
||||
// Automatically stop and then restart audio playback when the app resigns active.
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self stop];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)applicationDidBecomeActive:(NSNotification *)notification {
|
||||
if (_toneUnit) {
|
||||
__unused OSErr err = AudioOutputUnitStart(_toneUnit);
|
||||
NSAssert1(err == noErr, @"Error starting unit: %hd", err);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(NSNotification *)notification {
|
||||
if (_toneUnit) {
|
||||
__unused OSErr err = AudioOutputUnitStop(_toneUnit);
|
||||
NSAssert1(err == noErr, @"Error stopping unit: %hd", err);
|
||||
}
|
||||
}
|
||||
|
||||
- (double)volumeInDecibels {
|
||||
return 20 * log(self.volumeAmplitude);
|
||||
}
|
||||
|
||||
- (double)volumeAmplitude {
|
||||
return ORKSineWaveToneGeneratorAmplitudeDefault * pow(10, 2 * _fadeInFactor - 2);
|
||||
}
|
||||
|
||||
- (void)playSoundAtFrequency:(double)playFrequency {
|
||||
_frequency = playFrequency;
|
||||
_fadeInFactor = 0;
|
||||
_fadeInDuration = 0.5;
|
||||
_playsStereo = YES;
|
||||
|
||||
[self play];
|
||||
}
|
||||
|
||||
- (void)playSoundAtFrequency:(double)playFrequency
|
||||
onChannel:(ORKAudioChannel)playChannel
|
||||
fadeInDuration:(NSTimeInterval)duration {
|
||||
_frequency = playFrequency;
|
||||
_activeChannel = playChannel;
|
||||
_fadeInFactor = 0;
|
||||
_fadeInDuration = duration;
|
||||
_playsStereo = NO;
|
||||
|
||||
[self play];
|
||||
}
|
||||
|
||||
- (void)play {
|
||||
if (!_toneUnit) {
|
||||
[self createToneUnit];
|
||||
|
||||
// Stop changing parameters on the unit
|
||||
OSErr err = AudioUnitInitialize(_toneUnit);
|
||||
NSAssert1(err == noErr, @"Error initializing unit: %hd", err);
|
||||
|
||||
// Start playback
|
||||
err = AudioOutputUnitStart(_toneUnit);
|
||||
NSAssert1(err == noErr, @"Error starting unit: %hd", err);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)stop {
|
||||
if (_toneUnit) {
|
||||
AudioOutputUnitStop(_toneUnit);
|
||||
AudioUnitUninitialize(_toneUnit);
|
||||
AudioComponentInstanceDispose(_toneUnit);
|
||||
_toneUnit = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setupAudioSession {
|
||||
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
|
||||
BOOL ok;
|
||||
NSError *setCategoryError = nil;
|
||||
ok = [audioSession setCategory:AVAudioSessionCategoryPlayback error:&setCategoryError];
|
||||
NSAssert1(ok, @"Audio error %@", setCategoryError);
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(handleInterruption:)
|
||||
name:AVAudioSessionInterruptionNotification
|
||||
object:audioSession];
|
||||
}
|
||||
|
||||
- (void)createToneUnit {
|
||||
// Configure the search parameters to find the default playback output unit
|
||||
// (called the kAudioUnitSubType_RemoteIO on iOS but
|
||||
// kAudioUnitSubType_DefaultOutput on Mac OS X)
|
||||
AudioComponentDescription defaultOutputDescription;
|
||||
defaultOutputDescription.componentType = kAudioUnitType_Output;
|
||||
defaultOutputDescription.componentSubType = kAudioUnitSubType_RemoteIO;
|
||||
defaultOutputDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
defaultOutputDescription.componentFlags = 0;
|
||||
defaultOutputDescription.componentFlagsMask = 0;
|
||||
|
||||
// Get the default playback output unit
|
||||
AudioComponent defaultOutput = AudioComponentFindNext(NULL, &defaultOutputDescription);
|
||||
NSAssert(defaultOutput, @"Can't find default output");
|
||||
|
||||
// Create a new unit based on this that we'll use for output
|
||||
OSErr err = AudioComponentInstanceNew(defaultOutput, &_toneUnit);
|
||||
NSAssert1(_toneUnit, @"Error creating unit: %hd", err);
|
||||
|
||||
// Set our tone rendering function on the unit
|
||||
AURenderCallbackStruct input;
|
||||
input.inputProc = ORKAudioGeneratorRenderTone;
|
||||
input.inputProcRefCon = (__bridge void *)(self);
|
||||
err = AudioUnitSetProperty(_toneUnit,
|
||||
kAudioUnitProperty_SetRenderCallback,
|
||||
kAudioUnitScope_Input,
|
||||
0,
|
||||
&input,
|
||||
sizeof(input));
|
||||
NSAssert1(err == noErr, @"Error setting callback: %hd", err);
|
||||
|
||||
// Set the format to 32 bit, single channel, floating point, linear PCM
|
||||
const int four_bytes_per_float = 4;
|
||||
const int eight_bits_per_byte = 8;
|
||||
AudioStreamBasicDescription streamFormat;
|
||||
streamFormat.mSampleRate = ORKSineWaveToneGeneratorSampleRateDefault;
|
||||
streamFormat.mFormatID = kAudioFormatLinearPCM;
|
||||
streamFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
|
||||
streamFormat.mBytesPerPacket = four_bytes_per_float;
|
||||
streamFormat.mFramesPerPacket = 1;
|
||||
streamFormat.mBytesPerFrame = four_bytes_per_float;
|
||||
streamFormat.mChannelsPerFrame = 2;
|
||||
streamFormat.mBitsPerChannel = four_bytes_per_float * eight_bits_per_byte;
|
||||
err = AudioUnitSetProperty (_toneUnit,
|
||||
kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Input,
|
||||
0,
|
||||
&streamFormat,
|
||||
sizeof(AudioStreamBasicDescription));
|
||||
NSAssert1(err == noErr, @"Error setting stream format: %hd", err);
|
||||
}
|
||||
|
||||
- (void)handleInterruption:(id)sender {
|
||||
[self stop];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -28,9 +28,11 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ORKRecorder.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
@@ -58,20 +60,22 @@ ORK_CLASS_AVAILABLE
|
||||
Passed to AVAudioRecorder`'s `-initWithURL:settings:error:`
|
||||
For information on the settings available for an audio recorder, see "AV Foundation Audio Settings Constants".
|
||||
*/
|
||||
@property (nonatomic, copy, readonly, nullable) NSDictionary *recorderSettings;
|
||||
@property (nonatomic, copy, readonly) NSDictionary *recorderSettings;
|
||||
|
||||
/**
|
||||
Returns an initialized audio recorder using the specified settings, step, and output directory.
|
||||
|
||||
@param recorderSettings The settings for the recording session.
|
||||
@param step The step that requested this recording.
|
||||
@param outputDirectory The directory in which the audio output should be stored.
|
||||
@param identifier The unique identifier of the recorder (assigned by the recorder configuration).
|
||||
@param recorderSettings The settings for the recording session.
|
||||
@param step The step that requested this recorder.
|
||||
@param outputDirectory The directory in which the audio output should be stored.
|
||||
|
||||
@return An initialized audio recorder.
|
||||
*/
|
||||
- (instancetype)initWithRecorderSettings:(NSDictionary *)recorderSettings
|
||||
step:(ORKStep *)step
|
||||
outputDirectory:(NSURL *)outputDirectory NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||
recorderSettings:(nullable NSDictionary *)recorderSettings
|
||||
step:(nullable ORKStep *)step
|
||||
outputDirectory:(nullable NSURL *)outputDirectory NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
Reference to the audio recorder being used.
|
||||
|
||||
@@ -28,12 +28,14 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKAudioRecorder.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import "ORKRecorder_Internal.h"
|
||||
#import "ORKRecorder_Private.h"
|
||||
#import "ORKDefines_Private.h"
|
||||
|
||||
|
||||
@interface ORKAudioRecorder ()
|
||||
|
||||
@property (nonatomic, strong) AVAudioRecorder *audioRecorder;
|
||||
@@ -42,41 +44,34 @@
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ORKAudioRecorder
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
- (void)dealloc {
|
||||
ORK_Log_Debug(@"Remove audiorecorder %p", self);
|
||||
[_audioRecorder stop];
|
||||
_audioRecorder = nil;
|
||||
}
|
||||
|
||||
|
||||
+ (NSDictionary *)defaultRecorderSettings
|
||||
{
|
||||
return [NSDictionary
|
||||
dictionaryWithObjectsAndKeys:
|
||||
@(kAudioFormatMPEG4AAC), AVFormatIDKey,
|
||||
@(AVAudioQualityMin), AVEncoderAudioQualityKey,
|
||||
@(2), AVNumberOfChannelsKey,
|
||||
@(44100.0), AVSampleRateKey,
|
||||
nil];
|
||||
+ (NSDictionary *)defaultRecorderSettings {
|
||||
return @{AVFormatIDKey : @(kAudioFormatMPEG4AAC),
|
||||
AVEncoderAudioQualityKey : @(AVAudioQualityMin),
|
||||
AVNumberOfChannelsKey : @(2),
|
||||
AVSampleRateKey : @(44100.0)};
|
||||
}
|
||||
|
||||
- (instancetype)initWithRecorderSettings:(NSDictionary *)recorderSettings
|
||||
step:(ORKStep *)step
|
||||
outputDirectory:(NSURL *)outputDirectory
|
||||
{
|
||||
self = [super initWithStep:step outputDirectory:outputDirectory];
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||
recorderSettings:(NSDictionary *)recorderSettings
|
||||
step:(ORKStep *)step
|
||||
outputDirectory:(NSURL *)outputDirectory {
|
||||
self = [super initWithIdentifier:identifier step:step outputDirectory:outputDirectory];
|
||||
if (self) {
|
||||
|
||||
self.continuesInBackground = YES;
|
||||
if (! recorderSettings)
|
||||
{
|
||||
if (! recorderSettings) {
|
||||
recorderSettings = [[self class] defaultRecorderSettings];
|
||||
}
|
||||
if (! [recorderSettings isKindOfClass:[NSDictionary class]])
|
||||
{
|
||||
if (! [recorderSettings isKindOfClass:[NSDictionary class]]) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"recorderSettings should be a dictionary" userInfo:recorderSettings];
|
||||
}
|
||||
self.recorderSettings = recorderSettings;
|
||||
@@ -85,7 +80,9 @@
|
||||
}
|
||||
|
||||
- (void)start {
|
||||
|
||||
if (self.outputDirectory == nil) {
|
||||
@throw [NSException exceptionWithName:NSDestinationInvalidException reason:@"audioRecorder requires an output directory" userInfo:nil];
|
||||
}
|
||||
// Only create the file when we should actually start recording.
|
||||
if (! _audioRecorder) {
|
||||
|
||||
@@ -103,7 +100,6 @@
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ORK_Log_Debug(@"Create audioRecorder %p", self);
|
||||
_audioRecorder = [[AVAudioRecorder alloc]
|
||||
initWithURL:soundFileURL
|
||||
@@ -115,16 +111,14 @@
|
||||
}
|
||||
|
||||
#if ! TARGET_IPHONE_SIMULATOR
|
||||
if (!_audioRecorder.recording)
|
||||
{
|
||||
if (!_audioRecorder.recording) {
|
||||
[_audioRecorder prepareToRecord];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ! TARGET_IPHONE_SIMULATOR
|
||||
if (!_audioRecorder.recording)
|
||||
{
|
||||
if (!_audioRecorder.recording) {
|
||||
[_audioRecorder prepareToRecord];
|
||||
[_audioRecorder record];
|
||||
}
|
||||
@@ -136,7 +130,6 @@
|
||||
- (void)stop {
|
||||
if (! _audioRecorder) {
|
||||
// Error has already been returned.
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -161,43 +154,39 @@
|
||||
unsigned int recorderFormat = [recorderSettings[AVFormatIDKey] unsignedIntValue];
|
||||
|
||||
NSString *contentType = @"audio";
|
||||
switch (recorderFormat)
|
||||
{
|
||||
case kAudioFormatLinearPCM:
|
||||
{
|
||||
switch (recorderFormat) {
|
||||
case kAudioFormatLinearPCM: {
|
||||
int numBits = [recorderSettings[AVLinearPCMBitDepthKey] intValue] ? : 16;
|
||||
contentType = [NSString stringWithFormat:@"audio/L%d", numBits];
|
||||
break;
|
||||
}
|
||||
case kAudioFormatAC3:
|
||||
case kAudioFormatAC3: {
|
||||
contentType = @"audio/ac3";
|
||||
break;
|
||||
}
|
||||
case kAudioFormatMPEG4AAC:
|
||||
case kAudioFormatMPEG4CELP:
|
||||
case kAudioFormatMPEG4HVXC:
|
||||
case kAudioFormatMPEG4TwinVQ:
|
||||
case kAudioFormatAppleLossless:
|
||||
case kAudioFormatAppleLossless: {
|
||||
contentType = @"audio/m4a";
|
||||
break;
|
||||
case kAudioFormatULaw:
|
||||
}
|
||||
case kAudioFormatULaw: {
|
||||
contentType = @"audio/basic";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return contentType;
|
||||
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)recorderType
|
||||
{
|
||||
- (NSString *)recorderType {
|
||||
return @"audio";
|
||||
}
|
||||
|
||||
- (void)doStopRecording
|
||||
{
|
||||
- (void)doStopRecording {
|
||||
if (self.isRecording) {
|
||||
#if ! TARGET_IPHONE_SIMULATOR
|
||||
#if !TARGET_IPHONE_SIMULATOR
|
||||
[_audioRecorder stop];
|
||||
|
||||
[self applyFileProtection:ORKFileProtectionComplete toFileAtURL:[self recordingFileURL]];
|
||||
@@ -205,48 +194,44 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)finishRecordingWithError:(NSError *)error
|
||||
{
|
||||
- (void)finishRecordingWithError:(NSError *)error {
|
||||
[self doStopRecording];
|
||||
|
||||
[super finishRecordingWithError:error];
|
||||
}
|
||||
|
||||
- (NSString *)extension
|
||||
{
|
||||
- (NSString *)extension {
|
||||
NSDictionary *recorderSettings = [self recorderSettings];
|
||||
unsigned int recorderFormat = [recorderSettings[AVFormatIDKey] unsignedIntValue];
|
||||
|
||||
NSString *extension = @"au";
|
||||
switch (recorderFormat)
|
||||
{
|
||||
switch (recorderFormat) {
|
||||
case kAudioFormatLinearPCM:
|
||||
{
|
||||
extension = @"pcm";
|
||||
break;
|
||||
}
|
||||
case kAudioFormatAC3:
|
||||
case kAudioFormatAC3: {
|
||||
extension = @"ac3";
|
||||
break;
|
||||
}
|
||||
case kAudioFormatMPEG4AAC:
|
||||
case kAudioFormatMPEG4CELP:
|
||||
case kAudioFormatMPEG4HVXC:
|
||||
case kAudioFormatMPEG4TwinVQ:
|
||||
case kAudioFormatAppleLossless:
|
||||
case kAudioFormatAppleLossless: {
|
||||
extension = @"m4a";
|
||||
break;
|
||||
}
|
||||
}
|
||||
return extension;
|
||||
}
|
||||
|
||||
- (NSURL *)recordingFileURL
|
||||
{
|
||||
- (NSURL *)recordingFileURL {
|
||||
return [[self recordingDirectoryURL] URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", [self logName], [self extension]]];
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)recreateFileWithError:(NSError * __autoreleasing *)error
|
||||
{
|
||||
- (BOOL)recreateFileWithError:(NSError * __autoreleasing *)error {
|
||||
NSURL *url = [self recordingFileURL];
|
||||
if (! url) {
|
||||
if (error) {
|
||||
@@ -255,34 +240,34 @@
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
|
||||
if (! [fm createDirectoryAtURL:url withIntermediateDirectories:YES attributes:nil error:error]) {
|
||||
if (! [fileManager createDirectoryAtURL:url withIntermediateDirectories:YES attributes:nil error:error]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if ([fm fileExistsAtPath:[url path]]) {
|
||||
if (! [fm removeItemAtPath:[url path] error:error]) {
|
||||
if ([fileManager fileExistsAtPath:[url path]]) {
|
||||
if (! [fileManager removeItemAtPath:[url path] error:error]) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
[fm createFileAtPath:[url path] contents:nil attributes:nil];
|
||||
[fm setAttributes:@{NSFileProtectionKey : ORKFileProtectionFromMode(ORKFileProtectionCompleteUnlessOpen)} ofItemAtPath:[url path] error:error];
|
||||
[fileManager createFileAtPath:[url path] contents:nil attributes:nil];
|
||||
[fileManager setAttributes:@{NSFileProtectionKey : ORKFileProtectionFromMode(ORKFileProtectionCompleteUnlessOpen)} ofItemAtPath:[url path] error:error];
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
- (void)reset {
|
||||
[_audioRecorder stop];
|
||||
_audioRecorder = nil;
|
||||
[super reset];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface ORKAudioRecorderConfiguration ()
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -290,13 +275,15 @@
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wobjc-designated-initializers"
|
||||
- (instancetype)initWithRecorderSettings:(NSDictionary *)recorderSettings
|
||||
{
|
||||
self = [self ork_init];
|
||||
if (self)
|
||||
{
|
||||
if (recorderSettings && ! [recorderSettings isKindOfClass:[NSDictionary class]])
|
||||
{
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier {
|
||||
@throw [NSException exceptionWithName:NSGenericException reason:@"Use subclass designated initializer" userInfo:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||
recorderSettings:(NSDictionary *)recorderSettings {
|
||||
self = [super initWithIdentifier:identifier];
|
||||
if (self) {
|
||||
if (recorderSettings && ! [recorderSettings isKindOfClass:[NSDictionary class]]) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"recorderSettings should be a dictionary" userInfo:recorderSettings];
|
||||
}
|
||||
_recorderSettings = recorderSettings;
|
||||
@@ -306,31 +293,27 @@
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
- (ORKRecorder *)recorderForStep:(ORKStep *)step
|
||||
outputDirectory:(NSURL *)outputDirectory
|
||||
{
|
||||
return [[ORKAudioRecorder alloc] initWithRecorderSettings:(NSDictionary *__nonnull)self.recorderSettings
|
||||
step:step
|
||||
outputDirectory:outputDirectory];
|
||||
outputDirectory:(NSURL *)outputDirectory {
|
||||
return [[ORKAudioRecorder alloc] initWithIdentifier:self.identifier
|
||||
recorderSettings:self.recorderSettings
|
||||
step:step
|
||||
outputDirectory:outputDirectory];
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self)
|
||||
{
|
||||
if (self) {
|
||||
ORK_DECODE_OBJ_CLASS(aDecoder, recorderSettings, NSDictionary);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder
|
||||
{
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[super encodeWithCoder:aCoder];
|
||||
ORK_ENCODE_OBJ(aCoder, recorderSettings);
|
||||
}
|
||||
|
||||
+ (BOOL)supportsSecureCoding
|
||||
{
|
||||
+ (BOOL)supportsSecureCoding {
|
||||
return YES;
|
||||
}
|
||||
|
||||
@@ -339,12 +322,11 @@
|
||||
|
||||
__typeof(self) castObject = object;
|
||||
return (isParentSame &&
|
||||
ORKEqualObjects(self.recorderSettings, castObject.recorderSettings)) ;
|
||||
ORKEqualObjects(self.recorderSettings, castObject.recorderSettings));
|
||||
}
|
||||
|
||||
|
||||
- (ORKPermissionMask)requestedPermissionMask {
|
||||
return ORKPermissionAudioRecording;
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
@@ -28,15 +28,15 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ORKActiveStep.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
ORK_CLASS_AVAILABLE
|
||||
@interface ORKAudioStep : ORKActiveStep
|
||||
|
||||
@property (nonatomic, assign) NSTimeInterval duration;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -28,69 +28,39 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKAudioStep.h"
|
||||
#import "ORKAudioStepViewController.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import "ORKStep_Private.h"
|
||||
|
||||
|
||||
@implementation ORKAudioStep
|
||||
|
||||
+ (Class)stepViewControllerClass {
|
||||
return [ORKAudioStepViewController class];
|
||||
}
|
||||
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier {
|
||||
self = [super initWithIdentifier:identifier];
|
||||
if (self) {
|
||||
self.shouldShowDefaultTimer = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)validateParameters {
|
||||
|
||||
[super validateParameters];
|
||||
|
||||
NSTimeInterval const ORKAudioTaskMinimumDuration = 5.0;
|
||||
|
||||
if ( self.duration < ORKAudioTaskMinimumDuration)
|
||||
{
|
||||
if ( self.stepDuration < ORKAudioTaskMinimumDuration) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"duration can not be shorter than %@ seconds.", @(ORKAudioTaskMinimumDuration)] userInfo:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
ORKAudioStep *step = [super copyWithZone:zone];
|
||||
step.duration = self.duration;
|
||||
return step;
|
||||
}
|
||||
|
||||
- (BOOL)startsFinished {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self)
|
||||
{
|
||||
ORK_DECODE_DOUBLE(aDecoder, duration);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
ORK_ENCODE_DOUBLE(aCoder, duration);
|
||||
}
|
||||
|
||||
+ (BOOL)supportsSecureCoding
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
BOOL isParentSame = [super isEqual:object];
|
||||
|
||||
__typeof(self) castObject = object;
|
||||
return (isParentSame &&
|
||||
(self.duration == castObject.duration)) ;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -28,8 +28,10 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit_Private.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
ORK_CLASS_AVAILABLE
|
||||
@@ -37,4 +39,4 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKAudioStepViewController.h"
|
||||
#import "ORKAudioContentView.h"
|
||||
#import "ORKActiveStepViewController_Internal.h"
|
||||
@@ -41,22 +42,22 @@
|
||||
#import "ORKActiveStepView.h"
|
||||
#import "ORKCustomStepView_Internal.h"
|
||||
|
||||
|
||||
@interface ORKAudioStepViewController ()
|
||||
|
||||
@property (nonatomic, strong) AVAudioRecorder *avAudioRecorder;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ORKAudioStepViewController
|
||||
{
|
||||
|
||||
@implementation ORKAudioStepViewController {
|
||||
ORKAudioContentView *_audioContentView;
|
||||
ORKAudioRecorder *_audioRecorder;
|
||||
|
||||
ORKActiveStepTimer *_timer;
|
||||
NSError *_audioRecorderError;
|
||||
}
|
||||
|
||||
- (instancetype)initWithStep:(ORKStep *)step {
|
||||
|
||||
self = [super initWithStep:step];
|
||||
if (self) {
|
||||
// Continue audio recording in the background
|
||||
@@ -68,15 +69,13 @@
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
_audioContentView = [ORKAudioContentView new];
|
||||
_audioContentView.timeLeft = self.audioStep.duration;
|
||||
_audioContentView.timeLeft = self.audioStep.stepDuration;
|
||||
self.activeStepView.activeCustomView = _audioContentView;
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
[super viewDidAppear:animated];
|
||||
|
||||
[self start];
|
||||
}
|
||||
|
||||
@@ -93,7 +92,6 @@
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_audioRecorder = audioRecorder;
|
||||
[self audioRecorderDidChange];
|
||||
}
|
||||
@@ -103,26 +101,26 @@
|
||||
}
|
||||
|
||||
- (void)doSample {
|
||||
if (_audioRecorderError) {
|
||||
return;
|
||||
}
|
||||
[_avAudioRecorder updateMeters];
|
||||
float value = [_avAudioRecorder averagePowerForChannel:0];
|
||||
|
||||
// Assume value is in range roughly -60dB to 0dB
|
||||
float clampedValue = MAX(value/60.0, -1) + 1;
|
||||
|
||||
[_audioContentView addSample:@(clampedValue)];
|
||||
_audioContentView.timeLeft = [_timer duration] - [_timer runtime];
|
||||
}
|
||||
|
||||
- (void)startNewTimerIfNeeded {
|
||||
if (! _timer) {
|
||||
|
||||
NSTimeInterval duration = self.audioStep.duration;
|
||||
NSTimeInterval duration = self.audioStep.stepDuration;
|
||||
__weak typeof(self) weakSelf = self;
|
||||
_timer = [[ORKActiveStepTimer alloc] initWithDuration:duration interval:duration/100 runtime:0 handler:^(ORKActiveStepTimer *timer, BOOL finished) {
|
||||
typeof(self) strongSelf = weakSelf;
|
||||
[strongSelf doSample];
|
||||
if (finished) {
|
||||
[self finish];
|
||||
[strongSelf finish];
|
||||
}
|
||||
}];
|
||||
[_timer resume];
|
||||
@@ -130,16 +128,15 @@
|
||||
_audioContentView.finished = NO;
|
||||
}
|
||||
|
||||
|
||||
- (void)start {
|
||||
[super start];
|
||||
[self audioRecorderDidChange];
|
||||
|
||||
[_timer reset];
|
||||
_timer = nil;
|
||||
[self startNewTimerIfNeeded];
|
||||
|
||||
}
|
||||
|
||||
- (void)suspend {
|
||||
[super suspend];
|
||||
[_timer pause];
|
||||
@@ -147,13 +144,18 @@
|
||||
[_audioContentView addSample:@(0)];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resume {
|
||||
[super resume];
|
||||
[self audioRecorderDidChange];
|
||||
[self startNewTimerIfNeeded];
|
||||
[_timer resume];
|
||||
}
|
||||
|
||||
- (void)finish {
|
||||
if (_audioRecorderError) {
|
||||
return;
|
||||
}
|
||||
[super finish];
|
||||
[_timer reset];
|
||||
_timer = nil;
|
||||
@@ -166,7 +168,12 @@
|
||||
- (void)setAvAudioRecorder:(AVAudioRecorder *)recorder {
|
||||
_avAudioRecorder = nil;
|
||||
_avAudioRecorder = recorder;
|
||||
|
||||
}
|
||||
|
||||
- (void) recorder:(ORKRecorder *)recorder didFailWithError:(NSError *)error {
|
||||
[super recorder:recorder didFailWithError:error];
|
||||
_audioRecorderError = error;
|
||||
_audioContentView.failed = YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -28,9 +28,11 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
#import <ResearchKit/ORKActiveStep.h>
|
||||
|
||||
|
||||
/**
|
||||
The `ORKCountdownStep` class represents a step that displays a label and a
|
||||
countdown for a time equal to its duration.
|
||||
|
||||
@@ -28,18 +28,18 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKCountdownStep.h"
|
||||
#import "ORKCountdownStepViewController.h"
|
||||
|
||||
|
||||
@implementation ORKCountdownStep
|
||||
|
||||
+ (Class)stepViewControllerClass
|
||||
{
|
||||
+ (Class)stepViewControllerClass {
|
||||
return [ORKCountdownStepViewController class];
|
||||
}
|
||||
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||
{
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier {
|
||||
self = [super initWithIdentifier:identifier];
|
||||
if (self) {
|
||||
self.shouldStartTimerAutomatically = YES;
|
||||
@@ -56,8 +56,7 @@
|
||||
|
||||
NSTimeInterval const ORKCountdownStepMinimumDuration = 3.0;
|
||||
|
||||
if ( self.stepDuration < ORKCountdownStepMinimumDuration)
|
||||
{
|
||||
if ( self.stepDuration < ORKCountdownStepMinimumDuration) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"duration can not be shorter than %@ seconds.", @(ORKCountdownStepMinimumDuration)] userInfo:nil];
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,10 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit_Private.h>
|
||||
|
||||
|
||||
/**
|
||||
The `ORKCountdownStepViewController` class represents the step view controller that corresponds to an `ORKCountdownStep`.
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKCountdownStepViewController.h"
|
||||
#import "ORKCustomStepView_Internal.h"
|
||||
#import "ORKActiveStepViewController_internal.h"
|
||||
@@ -41,6 +42,7 @@
|
||||
#import "ORKAccessibility.h"
|
||||
#import "ORKActiveStepView.h"
|
||||
|
||||
|
||||
@interface ORKCountDownViewLabel : ORKLabel
|
||||
|
||||
@end
|
||||
@@ -51,6 +53,7 @@
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
@interface ORKCountdownView : ORKActiveStepCustomView
|
||||
|
||||
@property (nonatomic, strong) ORKSubheadlineLabel *textLabel;
|
||||
@@ -61,15 +64,14 @@
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ORKCountdownView {
|
||||
CAShapeLayer *_circleLayer;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
|
||||
_textLabel = [ORKSubheadlineLabel new];
|
||||
_textLabel.textAlignment = NSTextAlignmentCenter;
|
||||
_textLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
@@ -93,9 +95,18 @@
|
||||
|
||||
NSDictionary *views = NSDictionaryOfVariableBindings(_textLabel, _timeLabel, _progressView);
|
||||
|
||||
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_textLabel]-(>=0)-[_progressView(==d)]|" options:NSLayoutFormatDirectionLeadingToTrailing|NSLayoutFormatAlignAllCenterX metrics:metrics views:views]];
|
||||
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(>=0)-[_textLabel]-(>=0)-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:metrics views:views]];
|
||||
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(>=0)-[_progressView(==d)]-(>=0)-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:metrics views:views]];
|
||||
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_textLabel]-(>=0)-[_progressView(==d)]|"
|
||||
options:NSLayoutFormatDirectionLeadingToTrailing|NSLayoutFormatAlignAllCenterX
|
||||
metrics:metrics
|
||||
views:views]];
|
||||
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(>=0)-[_textLabel]-(>=0)-|"
|
||||
options:NSLayoutFormatDirectionLeadingToTrailing
|
||||
metrics:metrics
|
||||
views:views]];
|
||||
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(>=0)-[_progressView(==d)]-(>=0)-|"
|
||||
options:NSLayoutFormatDirectionLeadingToTrailing
|
||||
metrics:metrics
|
||||
views:views]];
|
||||
[self addConstraint:[NSLayoutConstraint constraintWithItem:_progressView
|
||||
attribute:NSLayoutAttributeCenterX
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
@@ -134,13 +145,11 @@
|
||||
_circleLayer.strokeColor = self.tintColor.CGColor;
|
||||
_circleLayer.lineWidth = 1;
|
||||
|
||||
|
||||
[_progressView.layer.sublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)];
|
||||
[_progressView.layer addSublayer:_circleLayer];
|
||||
|
||||
_textLabel.isAccessibilityElement = NO;
|
||||
_timeLabel.isAccessibilityElement = NO;
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -150,7 +159,6 @@
|
||||
}
|
||||
|
||||
- (void)startAnimateWithDuration:(NSTimeInterval)duration {
|
||||
|
||||
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"strokeEnd"];
|
||||
animation.duration = duration*2;
|
||||
animation.removedOnCompletion = YES;
|
||||
@@ -158,7 +166,6 @@
|
||||
animation.keyTimes = @[@(0.0), @(0.5), @(1.0)];
|
||||
animation.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
|
||||
[_circleLayer addAnimation:animation forKey:@"drawCircleAnimation"];
|
||||
|
||||
}
|
||||
|
||||
#pragma mark Accessibility
|
||||
@@ -184,12 +191,12 @@
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ORKCountdownStepViewController {
|
||||
NSInteger _countDown;
|
||||
}
|
||||
|
||||
- (instancetype)initWithStep:(ORKStep *)step {
|
||||
|
||||
self = [super initWithStep:step];
|
||||
if (self) {
|
||||
self.suspendIfInactive = NO;
|
||||
@@ -226,7 +233,6 @@
|
||||
}
|
||||
|
||||
- (void)countDownTimerFired:(ORKActiveStepTimer *)timer finished:(BOOL)finished {
|
||||
|
||||
_countDown = MAX((_countDown-1), 0);
|
||||
[self updateCountdownLabel];
|
||||
|
||||
@@ -240,17 +246,13 @@
|
||||
[super countDownTimerFired:timer finished:finished];
|
||||
}];
|
||||
UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, ORKLocalizedString(@"AX_ANNOUNCE_BEGIN_TASK", nil));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, [@(_countDown) stringValue]);
|
||||
[super countDownTimerFired:timer finished:finished];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
[super countDownTimerFired:timer finished:finished];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ORKDataLogger;
|
||||
@@ -94,14 +95,12 @@ ORK_CLASS_AVAILABLE
|
||||
/**
|
||||
Returns a data logger with an `ORKJSONLogFormatter`.
|
||||
|
||||
@param url The URL of the directory in which to place log files.
|
||||
@param logName The prefix on the log file name in an ASCII string. Note that
|
||||
the string must not contain the hyphen character ("-"), because a hyphen is used as a separator in the log naming scheme.
|
||||
@param delegate The initial delegate. May be `nil`.
|
||||
@param url The URL of the directory in which to place log files.
|
||||
@param logName The prefix on the log file name in an ASCII string. Note that the string must not contain the hyphen character ("-"), because a hyphen is used as a separator in the log naming scheme.
|
||||
@param delegate The initial delegate. May be `nil`.
|
||||
*/
|
||||
+ (ORKDataLogger *)JSONDataLoggerWithDirectory:(NSURL *)url logName:(NSString *)logName delegate:(nullable id<ORKDataLoggerDelegate>)delegate;
|
||||
|
||||
|
||||
/**
|
||||
Returns an initialized data logger using the specified URL, log name, formatter, and delegate.
|
||||
|
||||
@@ -110,6 +109,7 @@ ORK_CLASS_AVAILABLE
|
||||
the string must not contain the hyphen character ("-"), because a hyphen is used as a separator in the log naming scheme.
|
||||
@param formatter The type of formatter to use for the log, such as `ORKJSONLogFormatter`.
|
||||
@param delegate The initial delegate. May be `nil`.
|
||||
|
||||
@return An initialized data logger.
|
||||
*/
|
||||
- (instancetype)initWithDirectory:(NSURL *)url logName:(NSString *)logName formatter:(ORKLogFormatter *)formatter delegate:(nullable id<ORKDataLoggerDelegate>)delegate NS_DESIGNATED_INITIALIZER;
|
||||
@@ -160,7 +160,8 @@ ORK_CLASS_AVAILABLE
|
||||
than through this object.
|
||||
|
||||
@param block The block to call during enumeration.
|
||||
@param error Any error detected during the enumeration
|
||||
@param error Any error detected during the enumeration.
|
||||
|
||||
@return `YES` if the enumeration was successful; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)enumerateLogs:(void (^)(NSURL *logFileUrl, BOOL *stop))block error:(NSError * __autoreleasing *)error;
|
||||
@@ -174,7 +175,8 @@ ORK_CLASS_AVAILABLE
|
||||
than through this object.
|
||||
|
||||
@param block The block to call during enumeration.
|
||||
@param error Any error detected during the enumeration
|
||||
@param error Any error detected during the enumeration.
|
||||
|
||||
@return `YES` if the enumeration was successful; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)enumerateLogsNeedingUpload:(void (^)(NSURL *logFileUrl, BOOL *stop))block error:(NSError * __autoreleasing *)error;
|
||||
@@ -188,7 +190,8 @@ ORK_CLASS_AVAILABLE
|
||||
than through this object.
|
||||
|
||||
@param block The block to call during enumeration.
|
||||
@param error Any error detected during the enumeration
|
||||
@param error Any error detected during the enumeration.
|
||||
|
||||
@return `YES` if the enumeration was successful; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)enumerateLogsAlreadyUploaded:(void (^)(NSURL *logFileUrl, BOOL *stop))block error:(NSError * __autoreleasing *)error;
|
||||
@@ -204,6 +207,7 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@param object Should be an object of a class that is accepted by the logFormatter.
|
||||
@param error Error output, if the append fails.
|
||||
|
||||
@return `YES` if appending succeeds; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)append:(id)object error:(NSError * __autoreleasing *)error;
|
||||
@@ -216,6 +220,7 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@param objects An array of objects of a class that is accepted by the logFormatter.
|
||||
@param error Error output, if the append fails.
|
||||
|
||||
@return `YES` if appending succeeds; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)appendObjects:(NSArray *)objects error:(NSError * __nullable __autoreleasing *)error;
|
||||
@@ -224,6 +229,7 @@ ORK_CLASS_AVAILABLE
|
||||
Checks whether a file has been marked as uploaded.
|
||||
|
||||
@param url The URL to check.
|
||||
|
||||
@return `YES` if the uploaded attribute has been set on the file and the file exists; otherwise,
|
||||
`NO`.
|
||||
*/
|
||||
@@ -240,6 +246,7 @@ ORK_CLASS_AVAILABLE
|
||||
@param uploaded A Boolean value that indicates whether to mark the file uploaded or not uploaded.
|
||||
@param url The URL to mark.
|
||||
@param error The error that occurred, if the operation fails.
|
||||
|
||||
@return `YES` if adding or removing the attribute succeeded; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)markFileUploaded:(BOOL)uploaded atURL:(NSURL *)url error:(NSError * __nullable __autoreleasing *)error;
|
||||
@@ -253,6 +260,7 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@param fileURLs The array of files that should be removed.
|
||||
@param error The error that occurred, if the operation fails.
|
||||
|
||||
@return `YES` if removing the files succeeded; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)removeUploadedFiles:(NSArray *)fileURLs withError:(NSError * __nullable __autoreleasing *)error;
|
||||
@@ -261,13 +269,14 @@ ORK_CLASS_AVAILABLE
|
||||
Removes all files managed by this logger (files that have the `logName` prefix).
|
||||
|
||||
@param error The error that occurred, if operation fails.
|
||||
|
||||
@return `YES` if removing the files succeeded.; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)removeAllFilesWithError:(NSError *__nullable __autoreleasing *)error;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/**
|
||||
The `ORKLogFormatter` class represents the base (default) log formatter, which appends data
|
||||
blindly to a log file.
|
||||
@@ -285,6 +294,7 @@ ORK_CLASS_AVAILABLE
|
||||
Returns a Boolean value that indicates whether the log formatter can serialize the specified type of object.
|
||||
|
||||
@param c The class of object to serialize.
|
||||
|
||||
@return `YES` if the log formatter can serialize this object class; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)canAcceptLogObjectOfClass:(Class)c;
|
||||
@@ -293,6 +303,7 @@ ORK_CLASS_AVAILABLE
|
||||
Returns a Boolean value that indicates whether the log formatter can serialize the specified type of object.
|
||||
|
||||
@param object The object to serialize.
|
||||
|
||||
@return `YES` if the log formatter can serialize `object`; otherwise, `NO`
|
||||
*/
|
||||
- (BOOL)canAcceptLogObject:(id)object;
|
||||
@@ -304,6 +315,7 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@param fileHandle The file handle to which to write.
|
||||
@param error The error output, on failure.
|
||||
|
||||
@return `YES` if the write succeeds; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)beginLogWithFileHandle:(NSFileHandle *)fileHandle error:(NSError * __nullable __autoreleasing *)error;
|
||||
@@ -314,6 +326,7 @@ ORK_CLASS_AVAILABLE
|
||||
@param object The object to write.
|
||||
@param fileHandle The file handle to which to write.
|
||||
@param error The error output, on failure.
|
||||
|
||||
@return `YES` if the write succeeds; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)appendObject:(id)object fileHandle:(NSFileHandle *)fileHandle error:(NSError * __nullable __autoreleasing *)error;
|
||||
@@ -324,12 +337,14 @@ ORK_CLASS_AVAILABLE
|
||||
@param objects The objects to write.
|
||||
@param fileHandle The file handle to which to write.
|
||||
@param error The error output, on failure.
|
||||
|
||||
@return `YES` if the write succeeds; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)appendObjects:(NSArray *)objects fileHandle:(NSFileHandle *)fileHandle error:(NSError * __nullable __autoreleasing *)error;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/**
|
||||
The `ORKJSONLogFormatter` class represents a log formatter for producing JSON output.
|
||||
|
||||
@@ -352,7 +367,6 @@ ORK_CLASS_AVAILABLE
|
||||
The `ORKDataLoggerManagerDelegate` protocol defines methods a delegate can implement to receive notifications
|
||||
when the data loggers managed by a `ORKDataLoggerManager` reach a certain file size threshold.
|
||||
*/
|
||||
|
||||
ORK_CLASS_AVAILABLE
|
||||
@protocol ORKDataLoggerManagerDelegate <NSObject>
|
||||
|
||||
@@ -377,6 +391,7 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/**
|
||||
The `ORKDataLoggerManager` class represents a manager for multiple `ORKDataLogger` instances,
|
||||
which tracks the total size of log files produced and can notify its delegate
|
||||
@@ -415,6 +430,7 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@param directory The file URL of the directory where the data loggers should coexist.
|
||||
@param delegate The delegate to receive notifications.
|
||||
|
||||
@return An initialized data logger manager.
|
||||
*/
|
||||
- (instancetype)initWithDirectory:(NSURL *)directory delegate:(nullable id<ORKDataLoggerManagerDelegate>)delegate NS_DESIGNATED_INITIALIZER;
|
||||
@@ -440,6 +456,7 @@ ORK_CLASS_AVAILABLE
|
||||
This method throws an exception if a logger already exists with the specified log name.
|
||||
|
||||
@param logName The log name prefix for the data logger.
|
||||
|
||||
@return The `ORKDataLogger` object that was added.
|
||||
*/
|
||||
- (ORKDataLogger *)addJSONDataLoggerForLogName:(NSString *)logName;
|
||||
@@ -449,6 +466,7 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@param logName The log name prefix for the data logger.
|
||||
@param formatter The log formatter instance to use for this logger.
|
||||
|
||||
@return The `ORKDataLogger` object that was added, or the existing one if one already existed for
|
||||
that log name.
|
||||
*/
|
||||
@@ -458,6 +476,7 @@ ORK_CLASS_AVAILABLE
|
||||
Retrieves the already existing data logger for a log name.
|
||||
|
||||
@param logName The log name prefix for the data logger.
|
||||
|
||||
@return The `ORKDataLogger` object that was retrieved, or `nil` if one already existed for that log name.
|
||||
*/
|
||||
- (nullable ORKDataLogger *)dataLoggerForLogName:(NSString *)logName;
|
||||
@@ -479,6 +498,7 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@param block The block to call during enumeration.
|
||||
@param error The error, on failure.
|
||||
|
||||
@return `YES` if the enumeration succeeds; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)enumerateLogsNeedingUpload:(void (^)(ORKDataLogger *dataLogger, NSURL *logFileUrl, BOOL *stop))block error:(NSError * __autoreleasing *)error;
|
||||
@@ -489,8 +509,9 @@ ORK_CLASS_AVAILABLE
|
||||
Use this method to indicate that the specified files should no longer be marked uploaded (for example, because
|
||||
the upload did not succeed).
|
||||
|
||||
@param fileURLs The array of file URLs that should no longer be marked uploaded.
|
||||
@param fileURLs The array of file URLs that should no longer be marked uploaded.
|
||||
@param error The error, on failure.
|
||||
|
||||
@return `YES` if the operation succeeds; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)unmarkUploadedFiles:(NSArray *)fileURLs error:(NSError * __nullable __autoreleasing *)error;
|
||||
@@ -502,8 +523,9 @@ ORK_CLASS_AVAILABLE
|
||||
that may relate to any of the data loggers. It is an error to pass a URL which would not
|
||||
belong to one of the loggers managed by this manager.
|
||||
|
||||
@param fileURLs The array of file URLs that should be removed.
|
||||
@param fileURLs The array of file URLs that should be removed.
|
||||
@param error The error, on failure.
|
||||
|
||||
@return `YES` if the operation succeeds; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)removeUploadedFiles:(NSArray *)fileURLs error:(NSError * __nullable __autoreleasing *)error;
|
||||
@@ -514,16 +536,13 @@ ORK_CLASS_AVAILABLE
|
||||
This method removes uploaded logs first, followed by the oldest log files across
|
||||
all of the data loggers, until the total usage falls below a threshold.
|
||||
|
||||
@param bytes The threshold down to which to remove old log files. File
|
||||
removal stops when the total bytes managed by all the data
|
||||
loggers reaches this threshold.
|
||||
@param bytes The threshold down to which to remove old log files. File removal stops when the total bytes managed by all the data loggers reaches this threshold.
|
||||
@param error The error, on failure.
|
||||
|
||||
@return `YES` if the operation succeeds; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)removeOldAndUploadedLogsToThreshold:(unsigned long long)bytes error:(NSError * __nullable __autoreleasing *)error;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -31,25 +31,32 @@
|
||||
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*
|
||||
Exposing a minimal set of extra facilities to permit unit testing.
|
||||
*/
|
||||
@interface ORKDataLogger ()
|
||||
|
||||
|
||||
@interface ORKDataLogger()
|
||||
- (nullable NSFileHandle *)fileHandle;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@protocol ORKDataLoggerExtendedDelegate <ORKDataLoggerDelegate>
|
||||
|
||||
@optional
|
||||
- (void)dataLoggerThresholdsDidChange:(ORKDataLogger *)dataLogger;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface NSURL (ORKDataLogger)
|
||||
|
||||
- (BOOL)ork_isUploaded;
|
||||
- (BOOL)ork_setUploaded:(BOOL)uploaded error:(NSError * __autoreleasing *)error;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -30,6 +30,18 @@
|
||||
|
||||
#import <ResearchKit/ORKRecorder.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class CMDeviceMotion;
|
||||
|
||||
@protocol ORKDeviceMotionRecorderDelegate <ORKRecorderDelegate>
|
||||
|
||||
@optional
|
||||
|
||||
- (void)deviceMotionRecorderDidUpdateWithMotion:(CMDeviceMotion *)motion;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
The `ORKDeviceMotionRecorder` class represents a recorder that requests and collects device motion data from CoreMotion at a fixed frequency.
|
||||
|
||||
@@ -40,18 +52,25 @@ ORK_CLASS_AVAILABLE
|
||||
@interface ORKDeviceMotionRecorder : ORKRecorder
|
||||
|
||||
/**
|
||||
* The frequency of motion data collection from CoreMotion in hertz (Hz).
|
||||
The frequency of motion data collection from CoreMotion in hertz (Hz).
|
||||
*/
|
||||
@property (nonatomic, readonly) double frequency;
|
||||
|
||||
/**
|
||||
Returns an initialized device motion recorder using the specified frequency.
|
||||
|
||||
@param frequency The frequency of motion data collection from CoreMotion in hertz (Hz).
|
||||
@param identifier The unique identifier of the recorder (assigned by the recorder configuration).
|
||||
@param frequency The frequency of motion data collection from CoreMotion in hertz (Hz).
|
||||
@param step The step that requested this recorder.
|
||||
@param outputDirectory The directory in which the device motion data should be stored.
|
||||
|
||||
@return An initialized motion data recorder.
|
||||
*/
|
||||
- (instancetype)initWithFrequency:(double)frequency
|
||||
step:(ORKStep *)step
|
||||
outputDirectory:(NSURL *)outputDirectory NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||
frequency:(double)frequency
|
||||
step:(nullable ORKStep *)step
|
||||
outputDirectory:(nullable NSURL *)outputDirectory NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKDeviceMotionRecorder.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import "ORKRecorder_Internal.h"
|
||||
@@ -36,12 +37,13 @@
|
||||
#import <CoreMotion/CoreMotion.h>
|
||||
#import "CMDeviceMotion+ORKJSONDictionary.h"
|
||||
|
||||
@interface ORKDeviceMotionRecorder()
|
||||
{
|
||||
|
||||
@interface ORKDeviceMotionRecorder () {
|
||||
ORKDataLogger *_logger;
|
||||
}
|
||||
|
||||
@property (nonatomic, strong) CMMotionManager *motionManager;
|
||||
|
||||
@property (nonatomic) NSTimeInterval uptime;
|
||||
|
||||
@end
|
||||
@@ -49,35 +51,28 @@
|
||||
|
||||
@implementation ORKDeviceMotionRecorder
|
||||
|
||||
|
||||
- (instancetype)initWithFrequency:(double)frequency
|
||||
step:(ORKStep *)step
|
||||
outputDirectory:(NSURL *)outputDirectory
|
||||
{
|
||||
self = [super initWithStep:step
|
||||
outputDirectory:(NSURL *)outputDirectory];
|
||||
if (self)
|
||||
{
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||
frequency:(double)frequency
|
||||
step:(ORKStep *)step
|
||||
outputDirectory:(NSURL *)outputDirectory {
|
||||
self = [super initWithIdentifier:identifier
|
||||
step:step
|
||||
outputDirectory:outputDirectory];
|
||||
if (self) {
|
||||
self.frequency = frequency;
|
||||
self.continuesInBackground = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
- (void)dealloc {
|
||||
[_logger finishCurrentLog];
|
||||
}
|
||||
|
||||
- (void)setFrequency:(double)frequency
|
||||
{
|
||||
if (frequency <= 0)
|
||||
{
|
||||
- (void)setFrequency:(double)frequency {
|
||||
if (frequency <= 0) {
|
||||
_frequency = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
_frequency = frequency;
|
||||
}
|
||||
}
|
||||
@@ -87,7 +82,6 @@
|
||||
}
|
||||
|
||||
- (void)start {
|
||||
|
||||
[super start];
|
||||
|
||||
if (! _logger) {
|
||||
@@ -114,6 +108,10 @@
|
||||
if (data)
|
||||
{
|
||||
success = [_logger append:[data ork_JSONDictionary] error:&error];
|
||||
id delegate = self.delegate;
|
||||
if ([delegate respondsToSelector:@selector(deviceMotionRecorderDidUpdateWithMotion:)]) {
|
||||
[delegate deviceMotionRecorderDidUpdateWithMotion:data];
|
||||
}
|
||||
}
|
||||
if (!success)
|
||||
{
|
||||
@@ -124,13 +122,10 @@
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)recorderType
|
||||
{
|
||||
- (NSString *)recorderType {
|
||||
return @"deviceMotion";
|
||||
}
|
||||
|
||||
|
||||
- (void)stop {
|
||||
[self doStopRecording];
|
||||
[_logger finishCurrentLog];
|
||||
@@ -146,16 +141,14 @@
|
||||
[super stop];
|
||||
}
|
||||
|
||||
- (void)doStopRecording
|
||||
{
|
||||
- (void)doStopRecording {
|
||||
if (self.isRecording) {
|
||||
[self.motionManager stopDeviceMotionUpdates];
|
||||
self.motionManager = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)finishRecordingWithError:(NSError *)error
|
||||
{
|
||||
- (void)finishRecordingWithError:(NSError *)error {
|
||||
[self doStopRecording];
|
||||
[super finishRecordingWithError:error];
|
||||
}
|
||||
@@ -168,8 +161,7 @@
|
||||
return @"application/json";
|
||||
}
|
||||
|
||||
- (void)reset
|
||||
{
|
||||
- (void)reset {
|
||||
[super reset];
|
||||
|
||||
_logger = nil;
|
||||
@@ -178,7 +170,8 @@
|
||||
@end
|
||||
|
||||
|
||||
@interface ORKDeviceMotionRecorderConfiguration()
|
||||
@interface ORKDeviceMotionRecorderConfiguration ()
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -186,8 +179,12 @@
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wobjc-designated-initializers"
|
||||
- (instancetype)initWithFrequency:(double)freq {
|
||||
self = [super ork_init];
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier {
|
||||
@throw [NSException exceptionWithName:NSGenericException reason:@"Use subclass designated initializer" userInfo:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier frequency:(double)freq {
|
||||
self = [super initWithIdentifier:identifier];
|
||||
if (self) {
|
||||
_frequency = freq;
|
||||
}
|
||||
@@ -195,32 +192,27 @@
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
- (ORKRecorder *)recorderForStep:(ORKStep *)step outputDirectory:(NSURL *)outputDirectory
|
||||
{
|
||||
return [[ORKDeviceMotionRecorder alloc] initWithFrequency:self.frequency
|
||||
step:step
|
||||
outputDirectory:(NSURL *)outputDirectory];
|
||||
- (ORKRecorder *)recorderForStep:(ORKStep *)step outputDirectory:(NSURL *)outputDirectory {
|
||||
return [[ORKDeviceMotionRecorder alloc] initWithIdentifier:self.identifier
|
||||
frequency:self.frequency
|
||||
step:step
|
||||
outputDirectory:outputDirectory];
|
||||
}
|
||||
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self)
|
||||
{
|
||||
if (self) {
|
||||
ORK_DECODE_DOUBLE(aDecoder, frequency);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder
|
||||
{
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[super encodeWithCoder:aCoder];
|
||||
ORK_ENCODE_DOUBLE(aCoder, frequency);
|
||||
}
|
||||
|
||||
+ (BOOL)supportsSecureCoding
|
||||
{
|
||||
+ (BOOL)supportsSecureCoding {
|
||||
return YES;
|
||||
}
|
||||
|
||||
@@ -229,7 +221,7 @@
|
||||
|
||||
__typeof(self) castObject = object;
|
||||
return (isParentSame &&
|
||||
(self.frequency == castObject.frequency)) ;
|
||||
(self.frequency == castObject.frequency));
|
||||
}
|
||||
|
||||
- (ORKPermissionMask)requestedPermissionMask {
|
||||
|
||||
@@ -28,8 +28,10 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKCustomStepView_Internal.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ORKFitnessContentView : ORKActiveStepCustomView
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKFitnessContentView.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import <CoreMotion/CoreMotion.h>
|
||||
@@ -35,11 +36,11 @@
|
||||
#import "ORKActiveStepQuantityView.h"
|
||||
#import "ORKTintedImageView.h"
|
||||
|
||||
|
||||
// #define LAYOUT_TEST 1
|
||||
// #define LAYOUT_DEBUG 1
|
||||
|
||||
@interface ORKFitnessContentView()
|
||||
{
|
||||
@interface ORKFitnessContentView () {
|
||||
ORKQuantityLabel *_timerLabel;
|
||||
ORKQuantityPairView *_quantityPairView;
|
||||
UIView *_imageSpacer1;
|
||||
@@ -54,6 +55,7 @@
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ORKFitnessContentView
|
||||
|
||||
- (ORKActiveStepQuantityView *)distanceView {
|
||||
@@ -104,11 +106,14 @@
|
||||
self.backgroundColor = [[UIColor redColor] colorWithAlphaComponent:0.2];
|
||||
_quantityPairView.backgroundColor = [[UIColor orangeColor] colorWithAlphaComponent:0.2];
|
||||
#endif
|
||||
|
||||
|
||||
[self setDistanceInMeters:0];
|
||||
[self heartRateView].title = ORKLocalizedString(@"FITNESS_HEARTRATE_TITLE", nil);
|
||||
|
||||
[self addSubview:_quantityPairView];
|
||||
[self addSubview:_imageView];
|
||||
[self addSubview:_timerLabel];
|
||||
[self setNeedsUpdateConstraints];
|
||||
[self setupConstraints];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(localeDidChange:) name:NSCurrentLocaleDidChangeNotification object:nil];
|
||||
|
||||
@@ -125,7 +130,6 @@
|
||||
_lengthFormatter = [NSLengthFormatter new];
|
||||
_lengthFormatter.numberFormatter.maximumFractionDigits = 1;
|
||||
_lengthFormatter.numberFormatter.maximumSignificantDigits = 3;
|
||||
|
||||
}
|
||||
|
||||
- (void)localeDidChange:(NSNotification *)notification {
|
||||
@@ -133,9 +137,6 @@
|
||||
[self setDistanceInMeters:_distanceInMeters];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
- (void)willMoveToWindow:(UIWindow *)newWindow {
|
||||
[super willMoveToWindow:newWindow];
|
||||
_screenType = ORKGetScreenTypeForWindow(newWindow);
|
||||
@@ -149,40 +150,94 @@
|
||||
_topConstraint.constant = (CaptionBaselineToTimerTop - CaptionBaselineToStepViewTop);
|
||||
}
|
||||
|
||||
- (void)updateConstraints {
|
||||
|
||||
|
||||
if (_constraints) {
|
||||
[self removeConstraints:_constraints];
|
||||
_constraints = nil;
|
||||
}
|
||||
- (void)setupConstraints {
|
||||
NSMutableArray *constraints = [NSMutableArray array];
|
||||
NSDictionary *views = NSDictionaryOfVariableBindings(_timerLabel, _imageView, _quantityPairView, _imageSpacer1, _imageSpacer2);
|
||||
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_timerLabel][_imageSpacer1(>=0)][_imageView]" options:NSLayoutFormatAlignAllCenterX metrics:nil views:views]];
|
||||
_topConstraint = [NSLayoutConstraint constraintWithItem:_timerLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1 constant:0];
|
||||
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_timerLabel][_imageSpacer1(>=0)][_imageView]"
|
||||
options:NSLayoutFormatAlignAllCenterX
|
||||
metrics:nil
|
||||
views:views]];
|
||||
_topConstraint = [NSLayoutConstraint constraintWithItem:_timerLabel
|
||||
attribute:NSLayoutAttributeTop
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self
|
||||
attribute:NSLayoutAttributeTop
|
||||
multiplier:1
|
||||
constant:0];
|
||||
[self updateConstraintConstants];
|
||||
[constraints addObject:_topConstraint];
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_timerLabel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]];
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_timerLabel attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationLessThanOrEqual toItem:self attribute:NSLayoutAttributeWidth multiplier:1 constant:0]];
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_imageView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationLessThanOrEqual toItem:self attribute:NSLayoutAttributeWidth multiplier:1 constant:0]];
|
||||
[constraints addObject:[NSLayoutConstraint
|
||||
constraintWithItem:_timerLabel
|
||||
attribute:NSLayoutAttributeCenterX
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self
|
||||
attribute:NSLayoutAttributeCenterX
|
||||
multiplier:1
|
||||
constant:0]];
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_timerLabel
|
||||
attribute:NSLayoutAttributeWidth
|
||||
relatedBy:NSLayoutRelationLessThanOrEqual
|
||||
toItem:self attribute:NSLayoutAttributeWidth
|
||||
multiplier:1
|
||||
constant:0]];
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_imageView
|
||||
attribute:NSLayoutAttributeWidth
|
||||
relatedBy:NSLayoutRelationLessThanOrEqual
|
||||
toItem:self attribute:NSLayoutAttributeWidth
|
||||
multiplier:1
|
||||
constant:0]];
|
||||
|
||||
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_imageView][_imageSpacer2(>=0)][_quantityPairView]|" options:0 metrics:nil views:views]];
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_imageSpacer1 attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:0]];
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_imageSpacer2 attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:0]];
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_imageSpacer1 attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:_imageSpacer2 attribute:NSLayoutAttributeHeight multiplier:1 constant:0]];
|
||||
NSLayoutConstraint *c1 = [NSLayoutConstraint constraintWithItem:_imageSpacer1 attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:1000];
|
||||
c1.priority = UILayoutPriorityDefaultLow-1;
|
||||
[constraints addObject:c1];
|
||||
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_imageView][_imageSpacer2(>=0)][_quantityPairView]|"
|
||||
options:0
|
||||
metrics:nil
|
||||
views:views]];
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_imageSpacer1
|
||||
attribute:NSLayoutAttributeWidth
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:nil
|
||||
attribute:NSLayoutAttributeNotAnAttribute
|
||||
multiplier:1
|
||||
constant:0]];
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_imageSpacer2
|
||||
attribute:NSLayoutAttributeWidth
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:nil
|
||||
attribute:NSLayoutAttributeNotAnAttribute
|
||||
multiplier:1
|
||||
constant:0]];
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_imageSpacer1
|
||||
attribute:NSLayoutAttributeHeight
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:_imageSpacer2
|
||||
attribute:NSLayoutAttributeHeight
|
||||
multiplier:1
|
||||
constant:0]];
|
||||
NSLayoutConstraint *constraint1 = [NSLayoutConstraint constraintWithItem:_imageSpacer1
|
||||
attribute:NSLayoutAttributeHeight
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:nil
|
||||
attribute:NSLayoutAttributeNotAnAttribute
|
||||
multiplier:1
|
||||
constant:1000];
|
||||
constraint1.priority = UILayoutPriorityDefaultLow-1;
|
||||
[constraints addObject:constraint1];
|
||||
|
||||
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_quantityPairView]|" options:(NSLayoutFormatOptions)0 metrics:nil views:views]];
|
||||
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_quantityPairView]|"
|
||||
options:(NSLayoutFormatOptions)0
|
||||
metrics:nil
|
||||
views:views]];
|
||||
|
||||
NSLayoutConstraint *maxWidthConstraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:10000];
|
||||
NSLayoutConstraint *maxWidthConstraint = [NSLayoutConstraint constraintWithItem:self
|
||||
attribute:NSLayoutAttributeWidth
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:nil
|
||||
attribute:NSLayoutAttributeNotAnAttribute
|
||||
multiplier:1
|
||||
constant:ORKScreenMetricMaxDimension];
|
||||
maxWidthConstraint.priority = UILayoutPriorityRequired-1;
|
||||
[constraints addObject:maxWidthConstraint];
|
||||
|
||||
[self addConstraints:constraints];
|
||||
_constraints = constraints;
|
||||
[super updateConstraints];
|
||||
}
|
||||
|
||||
- (void)setImage:(UIImage *)image {
|
||||
@@ -191,9 +246,15 @@
|
||||
|
||||
_imageRatioConstraint.active = NO;
|
||||
|
||||
CGSize sz = image.size;
|
||||
if (sz.width > 0 && sz.height > 0) {
|
||||
_imageRatioConstraint = [NSLayoutConstraint constraintWithItem:_imageView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:_imageView attribute:NSLayoutAttributeWidth multiplier:sz.height/sz.width constant:0];
|
||||
CGSize size = image.size;
|
||||
if (size.width > 0 && size.height > 0) {
|
||||
_imageRatioConstraint = [NSLayoutConstraint constraintWithItem:_imageView
|
||||
attribute:NSLayoutAttributeHeight
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:_imageView
|
||||
attribute:NSLayoutAttributeWidth
|
||||
multiplier:size
|
||||
.height/size.width constant:0];
|
||||
_imageRatioConstraint.active = YES;
|
||||
}
|
||||
}
|
||||
@@ -213,7 +274,6 @@
|
||||
- (void)setHeartRate:(NSString *)heartRate {
|
||||
_heartRate = heartRate;
|
||||
[self heartRateView].value = heartRate;
|
||||
[self heartRateView].title = ORKLocalizedString(@"FITNESS_HEARTRATE_TITLE", nil);
|
||||
}
|
||||
|
||||
- (void)updateKeylineVisible {
|
||||
|
||||
@@ -28,9 +28,11 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
#import <ResearchKit/ORKActiveStep.h>
|
||||
|
||||
|
||||
/**
|
||||
Fitness step.
|
||||
|
||||
|
||||
@@ -28,30 +28,36 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKFitnessStep.h"
|
||||
#import "ORKFitnessStepViewController.h"
|
||||
|
||||
|
||||
@implementation ORKFitnessStep
|
||||
|
||||
+ (Class)stepViewControllerClass {
|
||||
return [ORKFitnessStepViewController class];
|
||||
}
|
||||
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier {
|
||||
self = [super initWithIdentifier:identifier];
|
||||
if (self) {
|
||||
self.shouldShowDefaultTimer = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)validateParameters {
|
||||
|
||||
[super validateParameters];
|
||||
|
||||
NSTimeInterval const ORKFitnessStepMinimumDuration = 5.0;
|
||||
|
||||
if ( self.stepDuration < ORKFitnessStepMinimumDuration)
|
||||
{
|
||||
if ( self.stepDuration < ORKFitnessStepMinimumDuration) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"rest duration can not be shorter than %@ seconds.", @(ORKFitnessStepMinimumDuration)] userInfo:nil];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
ORKFitnessStep *step = [super copyWithZone:zone];
|
||||
return step;
|
||||
}
|
||||
@@ -60,6 +66,4 @@
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -28,8 +28,10 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit_Private.h>
|
||||
|
||||
|
||||
/**
|
||||
Step view controller corresponding to `ORKFitnessStep`.
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKFitnessStepViewController.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import "ORKStep_Private.h"
|
||||
@@ -41,20 +42,18 @@
|
||||
#import "ORKActiveStepView.h"
|
||||
|
||||
|
||||
@interface ORKFitnessStepViewController () <ORKHealthQuantityTypeRecorderDelegate,ORKPedometerRecorderDelegate>
|
||||
{
|
||||
@interface ORKFitnessStepViewController () <ORKHealthQuantityTypeRecorderDelegate,ORKPedometerRecorderDelegate> {
|
||||
NSInteger _intendedSteps;
|
||||
ORKFitnessContentView *_contentView;
|
||||
NSNumberFormatter *_hrFormatter;
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ORKFitnessStepViewController
|
||||
|
||||
|
||||
- (instancetype)initWithStep:(ORKStep *)step {
|
||||
|
||||
- (instancetype)initWithStep:(ORKStep *)step {
|
||||
self = [super initWithStep:step];
|
||||
if (self) {
|
||||
self.suspendIfInactive = NO;
|
||||
|
||||
@@ -28,8 +28,10 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ORKRecorder.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ORKHealthQuantityTypeRecorder;
|
||||
@@ -37,11 +39,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@protocol ORKHealthQuantityTypeRecorderDelegate <ORKRecorderDelegate>
|
||||
|
||||
@optional
|
||||
|
||||
- (void)healthQuantityTypeRecorderDidUpdate:(ORKHealthQuantityTypeRecorder *)healthQuantityTypeRecorder;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/**
|
||||
The `ORKHealthQuantityTypeRecorder` class represents a recorder for collecting real time sample data from HealthKit, such as heart rate, during
|
||||
an active task.
|
||||
@@ -49,14 +51,28 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
ORK_CLASS_AVAILABLE
|
||||
@interface ORKHealthQuantityTypeRecorder : ORKRecorder
|
||||
|
||||
@property (nonatomic, readonly, copy) HKQuantityType *quantityType;
|
||||
@property (nonatomic, readonly, copy) HKUnit *unit;
|
||||
@property (nonatomic, readonly, copy, nullable) HKQuantitySample *lastSample;
|
||||
@property (nonatomic, copy, readonly) HKQuantityType *quantityType;
|
||||
|
||||
- (instancetype)initWithHealthQuantityType:(HKQuantityType *)quantityType
|
||||
unit:(HKUnit *)unit
|
||||
step:(ORKStep *)step
|
||||
outputDirectory:(NSURL *)outputDirectory NS_DESIGNATED_INITIALIZER;
|
||||
@property (nonatomic, copy, readonly) HKUnit *unit;
|
||||
|
||||
@property (nonatomic, copy, readonly, nullable) HKQuantitySample *lastSample;
|
||||
|
||||
/**
|
||||
Returns an initialized health quantity type recorder using the specified quantity type and unit.
|
||||
|
||||
@param identifier The unique identifier of the recorder (assigned by the recorder configuration).
|
||||
@param quantityType The quantity type that should be collected during the active task.
|
||||
@param unit The unit for the data that should be collected and serialized.
|
||||
@param step The step that requested this recorder.
|
||||
@param outputDirectory The directory in which the HealthKit data should be stored.
|
||||
|
||||
@return An initialized health quantity type recorder.
|
||||
*/
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||
healthQuantityType:(HKQuantityType *)quantityType
|
||||
unit:(HKUnit *)unit
|
||||
step:(nullable ORKStep *)step
|
||||
outputDirectory:(nullable NSURL *)outputDirectory NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKHealthQuantityTypeRecorder.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import "ORKDataLogger.h"
|
||||
@@ -35,8 +36,8 @@
|
||||
#import "ORKRecorder_Internal.h"
|
||||
#import "HKSample+ORKJSONDictionary.h"
|
||||
|
||||
@interface ORKHealthQuantityTypeRecorder()
|
||||
{
|
||||
|
||||
@interface ORKHealthQuantityTypeRecorder () {
|
||||
ORKDataLogger *_logger;
|
||||
BOOL _isRecording;
|
||||
HKHealthStore *_healthStore;
|
||||
@@ -46,23 +47,20 @@
|
||||
HKQuantitySample *_lastSample;
|
||||
}
|
||||
|
||||
@property (nonatomic, strong) NSError *recordingError;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ORKHealthQuantityTypeRecorder
|
||||
|
||||
|
||||
- (instancetype)initWithHealthQuantityType:(HKQuantityType *)quantityType
|
||||
unit:(HKUnit *)unit
|
||||
step:(ORKStep *)step
|
||||
outputDirectory:(NSURL *)outputDirectory
|
||||
{
|
||||
self = [super initWithStep:step
|
||||
outputDirectory:(NSURL *)outputDirectory];
|
||||
if (self)
|
||||
{
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||
healthQuantityType:(HKQuantityType *)quantityType
|
||||
unit:(HKUnit *)unit
|
||||
step:(ORKStep *)step
|
||||
outputDirectory:(NSURL *)outputDirectory {
|
||||
self = [super initWithIdentifier:identifier
|
||||
step:step
|
||||
outputDirectory:outputDirectory];
|
||||
if (self) {
|
||||
NSParameterAssert(quantityType != nil);
|
||||
NSParameterAssert(unit != nil);
|
||||
// Quantity type and unit are immutable, so should be equivalent to -copy
|
||||
@@ -74,9 +72,7 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
- (void)dealloc {
|
||||
[_logger finishCurrentLog];
|
||||
}
|
||||
|
||||
@@ -89,7 +85,6 @@
|
||||
if (delegate && [delegate respondsToSelector:@selector(healthQuantityTypeRecorderDidUpdate:)]) {
|
||||
[delegate healthQuantityTypeRecorderDidUpdate:self];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static const NSInteger _HealthAnchoredQueryLimit = 100;
|
||||
@@ -123,19 +118,15 @@ static const NSInteger _HealthAnchoredQueryLimit = 100;
|
||||
// Do another fetch immediately rather than wait for an observation
|
||||
[self doFetchNewData];
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
- (void)doFetchNewData {
|
||||
if (! _healthStore || ! _isRecording) {
|
||||
return;
|
||||
}
|
||||
NSAssert(_samplePredicate != nil, @"Sample predicate should be non-nil if recording");
|
||||
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
HKAnchoredObjectQuery *anchoredQuery = [[HKAnchoredObjectQuery alloc]
|
||||
initWithType:_quantityType
|
||||
@@ -157,12 +148,9 @@ static const NSInteger _HealthAnchoredQueryLimit = 100;
|
||||
[_healthStore executeQuery:anchoredQuery];
|
||||
}
|
||||
|
||||
|
||||
- (void)start {
|
||||
|
||||
[super start];
|
||||
|
||||
|
||||
if (! _logger) {
|
||||
NSError *err = nil;
|
||||
_logger = [self makeJSONDataLoggerWithError:&err];
|
||||
@@ -172,8 +160,7 @@ static const NSInteger _HealthAnchoredQueryLimit = 100;
|
||||
}
|
||||
}
|
||||
|
||||
if (! [HKHealthStore isHealthDataAvailable])
|
||||
{
|
||||
if (! [HKHealthStore isHealthDataAvailable]) {
|
||||
[self finishRecordingWithError:[NSError errorWithDomain:NSCocoaErrorDomain
|
||||
code:NSFeatureUnsupportedError
|
||||
userInfo:@{@"recorder" : self}]];
|
||||
@@ -230,13 +217,10 @@ static const NSInteger _HealthAnchoredQueryLimit = 100;
|
||||
[_healthStore executeQuery:_observerQuery];
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)recorderType
|
||||
{
|
||||
- (NSString *)recorderType {
|
||||
return _quantityType.identifier;
|
||||
}
|
||||
|
||||
|
||||
- (void)stop {
|
||||
if (! _isRecording) {
|
||||
return;
|
||||
@@ -251,14 +235,12 @@ static const NSInteger _HealthAnchoredQueryLimit = 100;
|
||||
fileUrl = logFileUrl;
|
||||
} error:&error];
|
||||
|
||||
|
||||
[self reportFileResultWithFile:fileUrl error:error];
|
||||
|
||||
[super stop];
|
||||
}
|
||||
|
||||
- (void)doStopRecording
|
||||
{
|
||||
- (void)doStopRecording {
|
||||
if (_isRecording) {
|
||||
NSAssert(_observerQuery != nil, @"Observer query should be non-nil when recording");
|
||||
[_healthStore stopQuery:_observerQuery];
|
||||
@@ -271,8 +253,7 @@ static const NSInteger _HealthAnchoredQueryLimit = 100;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)finishRecordingWithError:(NSError *)error
|
||||
{
|
||||
- (void)finishRecordingWithError:(NSError *)error {
|
||||
[self doStopRecording];
|
||||
[super finishRecordingWithError:error];
|
||||
}
|
||||
@@ -285,8 +266,7 @@ static const NSInteger _HealthAnchoredQueryLimit = 100;
|
||||
return @"application/json";
|
||||
}
|
||||
|
||||
- (void)reset
|
||||
{
|
||||
- (void)reset {
|
||||
[super reset];
|
||||
|
||||
_logger = nil;
|
||||
@@ -294,15 +274,22 @@ static const NSInteger _HealthAnchoredQueryLimit = 100;
|
||||
|
||||
@end
|
||||
|
||||
@interface ORKHealthQuantityTypeRecorderConfiguration()
|
||||
|
||||
@interface ORKHealthQuantityTypeRecorderConfiguration ()
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ORKHealthQuantityTypeRecorderConfiguration
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wobjc-designated-initializers"
|
||||
- (instancetype)initWithHealthQuantityType:(HKQuantityType *)quantityType unit:(HKUnit *)unit {
|
||||
self = [super ork_init];
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier {
|
||||
@throw [NSException exceptionWithName:NSGenericException reason:@"Use subclass designated initializer" userInfo:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier healthQuantityType:(HKQuantityType *)quantityType unit:(HKUnit *)unit {
|
||||
self = [super initWithIdentifier:identifier];
|
||||
if (self) {
|
||||
NSParameterAssert(quantityType != nil);
|
||||
NSParameterAssert(unit != nil);
|
||||
@@ -314,34 +301,29 @@ static const NSInteger _HealthAnchoredQueryLimit = 100;
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
- (ORKRecorder *)recorderForStep:(ORKStep *)step outputDirectory:(NSURL *)outputDirectory
|
||||
{
|
||||
return [[ORKHealthQuantityTypeRecorder alloc] initWithHealthQuantityType:_quantityType
|
||||
unit:_unit
|
||||
step:step
|
||||
outputDirectory:outputDirectory];
|
||||
- (ORKRecorder *)recorderForStep:(ORKStep *)step outputDirectory:(NSURL *)outputDirectory {
|
||||
return [[ORKHealthQuantityTypeRecorder alloc] initWithIdentifier:self.identifier
|
||||
healthQuantityType:_quantityType
|
||||
unit:_unit
|
||||
step:step
|
||||
outputDirectory:outputDirectory];
|
||||
}
|
||||
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self)
|
||||
{
|
||||
if (self) {
|
||||
ORK_DECODE_OBJ_CLASS(aDecoder, quantityType, HKQuantityType);
|
||||
ORK_DECODE_OBJ_CLASS(aDecoder, unit, HKUnit);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder
|
||||
{
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
ORK_ENCODE_OBJ(aCoder, quantityType);
|
||||
ORK_ENCODE_OBJ(aCoder, unit);
|
||||
}
|
||||
|
||||
+ (BOOL)supportsSecureCoding
|
||||
{
|
||||
+ (BOOL)supportsSecureCoding {
|
||||
return YES;
|
||||
}
|
||||
|
||||
@@ -351,10 +333,9 @@ static const NSInteger _HealthAnchoredQueryLimit = 100;
|
||||
__typeof(self) castObject = object;
|
||||
return (isParentSame &&
|
||||
ORKEqualObjects(self.quantityType, castObject.quantityType)&&
|
||||
ORKEqualObjects(self.unit, castObject.unit)) ;
|
||||
ORKEqualObjects(self.unit, castObject.unit));
|
||||
}
|
||||
|
||||
|
||||
- (NSSet *)requestedHealthKitTypesForReading {
|
||||
return [NSSet setWithObject:_quantityType];
|
||||
}
|
||||
|
||||
@@ -28,8 +28,12 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ORKRecorder.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
The `ORKLocationRecorder` class represents a recorder for collecting location data from CoreLocation.
|
||||
|
||||
@@ -41,4 +45,19 @@
|
||||
ORK_CLASS_AVAILABLE
|
||||
@interface ORKLocationRecorder : ORKRecorder
|
||||
|
||||
/**
|
||||
Returns an initialized location recorder.
|
||||
|
||||
@param identifier The unique identifier of the recorder (assigned by the recorder configuration).
|
||||
@param step The step that requested this recorder.
|
||||
@param outputDirectory The directory in which the location data should be stored.
|
||||
|
||||
@return An initialized location recorder.
|
||||
*/
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||
step:(nullable ORKStep *)step
|
||||
outputDirectory:(nullable NSURL *)outputDirectory NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKLocationRecorder.h"
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
#import "CLLocation+ORKJSONDictionary.h"
|
||||
@@ -35,13 +36,15 @@
|
||||
#import "ORKRecorder_Internal.h"
|
||||
#import "ORKRecorder_Private.h"
|
||||
|
||||
@interface ORKLocationRecorder() <CLLocationManagerDelegate>
|
||||
{
|
||||
|
||||
@interface ORKLocationRecorder () <CLLocationManagerDelegate> {
|
||||
ORKDataLogger *_logger;
|
||||
NSError *_recordingError;
|
||||
BOOL _started;
|
||||
}
|
||||
|
||||
@property (nonatomic, strong) CLLocationManager *locationManager;
|
||||
|
||||
@property (nonatomic) NSTimeInterval uptime;
|
||||
|
||||
@end
|
||||
@@ -49,19 +52,15 @@
|
||||
|
||||
@implementation ORKLocationRecorder
|
||||
|
||||
|
||||
- (instancetype)initWithStep:(ORKStep *)step outputDirectory:(NSURL *)outputDirectory
|
||||
{
|
||||
self = [super initWithStep:step outputDirectory:outputDirectory];
|
||||
if (self)
|
||||
{
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier step:(ORKStep *)step outputDirectory:(NSURL *)outputDirectory {
|
||||
self = [super initWithIdentifier:identifier step:step outputDirectory:outputDirectory];
|
||||
if (self) {
|
||||
self.continuesInBackground = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
- (void)dealloc {
|
||||
[_logger finishCurrentLog];
|
||||
}
|
||||
|
||||
@@ -86,8 +85,7 @@
|
||||
}
|
||||
|
||||
self.locationManager = [self createLocationManager];
|
||||
if ([CLLocationManager authorizationStatus] <= kCLAuthorizationStatusDenied)
|
||||
{
|
||||
if ([CLLocationManager authorizationStatus] <= kCLAuthorizationStatusDenied) {
|
||||
[self.locationManager requestWhenInUseAuthorization];
|
||||
}
|
||||
self.locationManager.pausesLocationUpdatesAutomatically = NO;
|
||||
@@ -115,7 +113,6 @@
|
||||
[self doStopRecording];
|
||||
[_logger finishCurrentLog];
|
||||
|
||||
|
||||
NSError *error = _recordingError;
|
||||
_recordingError = nil;
|
||||
__block NSURL *fileUrl = nil;
|
||||
@@ -129,13 +126,11 @@
|
||||
}
|
||||
|
||||
- (void)locationManager:(CLLocationManager *)manager
|
||||
didUpdateLocations:(NSArray *)locations
|
||||
{
|
||||
didUpdateLocations:(NSArray *)locations {
|
||||
BOOL success = YES;
|
||||
NSParameterAssert([locations count] >= 0);
|
||||
NSError *error = nil;
|
||||
if (locations)
|
||||
{
|
||||
if (locations) {
|
||||
NSMutableArray *dictionaries = [NSMutableArray arrayWithCapacity:[locations count]];
|
||||
[locations enumerateObjectsUsingBlock:^(CLLocation *obj, NSUInteger idx, BOOL *stop) {
|
||||
NSDictionary *d = [obj ork_JSONDictionary];
|
||||
@@ -144,8 +139,7 @@
|
||||
|
||||
success = [_logger appendObjects:dictionaries error:&error];
|
||||
}
|
||||
if (!success)
|
||||
{
|
||||
if (!success) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
_recordingError = error;
|
||||
[self stop];
|
||||
@@ -153,8 +147,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)finishRecordingWithError:(NSError *)error
|
||||
{
|
||||
- (void)finishRecordingWithError:(NSError *)error {
|
||||
[self doStopRecording];
|
||||
[super finishRecordingWithError:nil];
|
||||
}
|
||||
@@ -163,14 +156,12 @@
|
||||
return [CLLocationManager locationServicesEnabled] && (self.locationManager != nil) && ([CLLocationManager authorizationStatus] > kCLAuthorizationStatusDenied);
|
||||
}
|
||||
|
||||
- (void)reset
|
||||
{
|
||||
- (void)reset {
|
||||
[super reset];
|
||||
|
||||
_logger = nil;
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)mimeType {
|
||||
return @"application/json";
|
||||
}
|
||||
@@ -178,35 +169,36 @@
|
||||
@end
|
||||
|
||||
|
||||
@interface ORKLocationRecorderConfiguration()
|
||||
@interface ORKLocationRecorderConfiguration ()
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ORKLocationRecorderConfiguration
|
||||
|
||||
- (ORKRecorder *)recorderForStep:(ORKStep *)step outputDirectory:(NSURL *)outputDirectory {
|
||||
return [[ORKLocationRecorder alloc] initWithStep:step outputDirectory:outputDirectory];
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier {
|
||||
return [super initWithIdentifier:identifier];
|
||||
}
|
||||
|
||||
|
||||
- (ORKRecorder *)recorderForStep:(ORKStep *)step outputDirectory:(NSURL *)outputDirectory {
|
||||
return [[ORKLocationRecorder alloc] initWithIdentifier:self.identifier step:step outputDirectory:outputDirectory];
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
self = [super initWithCoder:aDecoder];
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (BOOL)supportsSecureCoding
|
||||
{
|
||||
+ (BOOL)supportsSecureCoding {
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
BOOL isParentSame = [super isEqual:object];
|
||||
|
||||
return isParentSame;
|
||||
}
|
||||
|
||||
|
||||
- (ORKPermissionMask)requestedPermissionMask {
|
||||
return ORKPermissionCoreLocation;
|
||||
}
|
||||
|
||||
@@ -28,8 +28,10 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ORKRecorder.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ORKPedometerRecorder;
|
||||
@@ -37,11 +39,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@protocol ORKPedometerRecorderDelegate <ORKRecorderDelegate>
|
||||
|
||||
@optional
|
||||
|
||||
- (void)pedometerRecorderDidUpdate:(ORKPedometerRecorder *)pedometerRecorder;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/**
|
||||
A recorder that requests and collects device motion data from CoreMotion at a fixed frequency.
|
||||
|
||||
@@ -52,13 +54,24 @@ ORK_CLASS_AVAILABLE
|
||||
@interface ORKPedometerRecorder : ORKRecorder
|
||||
|
||||
@property (nonatomic, readonly, nullable) NSDate *lastUpdateDate;
|
||||
|
||||
@property (nonatomic, readonly) NSInteger totalNumberOfSteps;
|
||||
|
||||
// Negative if an invalid value.
|
||||
@property (nonatomic, readonly) NSInteger totalDistance;
|
||||
|
||||
- (instancetype)initWithStep:(ORKStep *)step
|
||||
outputDirectory:(NSURL *)outputDirectory NS_DESIGNATED_INITIALIZER;
|
||||
/**
|
||||
Returns an initialized pedometer recorder.
|
||||
|
||||
@param identifier The unique identifier of the recorder (assigned by the recorder configuration).
|
||||
@param step The step that requested this recorder.
|
||||
@param outputDirectory The directory in which the pedometer data should be stored.
|
||||
|
||||
@return An initialized pedometer recorder.
|
||||
*/
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||
step:(nullable ORKStep *)step
|
||||
outputDirectory:(nullable NSURL *)outputDirectory NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -28,49 +28,43 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKPedometerRecorder.h"
|
||||
#import "ORKDataLogger.h"
|
||||
#import "CMPedometerData+ORKJSONDictionary.h"
|
||||
#import "ORKRecorder_Internal.h"
|
||||
#import "ORKRecorder_Private.h"
|
||||
|
||||
@interface ORKPedometerRecorder()
|
||||
{
|
||||
|
||||
@interface ORKPedometerRecorder () {
|
||||
ORKDataLogger *_logger;
|
||||
BOOL _isRecording;
|
||||
}
|
||||
|
||||
@property (nonatomic, strong) CMPedometer *pedometer;
|
||||
@property (nonatomic, strong) NSError *recordingError;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ORKPedometerRecorder
|
||||
|
||||
|
||||
- (instancetype)initWithStep:(ORKStep *)step
|
||||
outputDirectory:(NSURL *)outputDirectory
|
||||
{
|
||||
self = [super initWithStep:step
|
||||
outputDirectory:(NSURL *)outputDirectory];
|
||||
if (self)
|
||||
{
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier
|
||||
step:(ORKStep *)step
|
||||
outputDirectory:(NSURL *)outputDirectory {
|
||||
self = [super initWithIdentifier:identifier
|
||||
step:step
|
||||
outputDirectory:outputDirectory];
|
||||
if (self) {
|
||||
self.continuesInBackground = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
- (void)dealloc {
|
||||
[_logger finishCurrentLog];
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (void)updateStatisticsWithData:(CMPedometerData *)pedometerData {
|
||||
|
||||
_lastUpdateDate = pedometerData.endDate;
|
||||
_totalNumberOfSteps = [pedometerData.numberOfSteps integerValue];
|
||||
if (pedometerData.distance) {
|
||||
@@ -90,7 +84,6 @@
|
||||
}
|
||||
|
||||
- (void)start {
|
||||
|
||||
[super start];
|
||||
|
||||
_lastUpdateDate = nil;
|
||||
@@ -108,8 +101,7 @@
|
||||
|
||||
self.pedometer = [self createPedometer];
|
||||
|
||||
if (! [[self.pedometer class] isStepCountingAvailable])
|
||||
{
|
||||
if (! [[self.pedometer class] isStepCountingAvailable]) {
|
||||
[self finishRecordingWithError:[NSError errorWithDomain:NSCocoaErrorDomain
|
||||
code:NSFeatureUnsupportedError
|
||||
userInfo:@{@"recorder" : self}]];
|
||||
@@ -121,16 +113,14 @@
|
||||
[self.pedometer startPedometerUpdatesFromDate:[NSDate date] withHandler:^(CMPedometerData *pedometerData, NSError *error) {
|
||||
|
||||
BOOL success = NO;
|
||||
if (pedometerData)
|
||||
{
|
||||
if (pedometerData) {
|
||||
success = [_logger append:[pedometerData ork_JSONDictionary] error:&error];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
__typeof(self) strongSelf = weakSelf;
|
||||
[strongSelf updateStatisticsWithData:pedometerData];
|
||||
});
|
||||
}
|
||||
if (!success || error)
|
||||
{
|
||||
if (!success || error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
__typeof(self) strongSelf = weakSelf;
|
||||
[strongSelf finishRecordingWithError:error];
|
||||
@@ -139,13 +129,10 @@
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)recorderType
|
||||
{
|
||||
- (NSString *)recorderType {
|
||||
return @"pedometer";
|
||||
}
|
||||
|
||||
|
||||
- (void)stop {
|
||||
[self doStopRecording];
|
||||
[_logger finishCurrentLog];
|
||||
@@ -154,17 +141,15 @@
|
||||
__block NSURL *fileUrl = nil;
|
||||
[_logger enumerateLogs:^(NSURL *logFileUrl, BOOL *stop) {
|
||||
fileUrl = logFileUrl;
|
||||
} error:&error];
|
||||
|
||||
|
||||
}
|
||||
error:&error];
|
||||
|
||||
[self reportFileResultWithFile:fileUrl error:error];
|
||||
|
||||
[super stop];
|
||||
}
|
||||
|
||||
- (void)doStopRecording
|
||||
{
|
||||
- (void)doStopRecording {
|
||||
if (_isRecording) {
|
||||
[self.pedometer stopPedometerUpdates];
|
||||
_isRecording = NO;
|
||||
@@ -172,8 +157,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)finishRecordingWithError:(NSError *)error
|
||||
{
|
||||
- (void)finishRecordingWithError:(NSError *)error {
|
||||
[self doStopRecording];
|
||||
[super finishRecordingWithError:error];
|
||||
}
|
||||
@@ -186,8 +170,7 @@
|
||||
return @"application/json";
|
||||
}
|
||||
|
||||
- (void)reset
|
||||
{
|
||||
- (void)reset {
|
||||
[super reset];
|
||||
|
||||
_logger = nil;
|
||||
@@ -196,47 +179,40 @@
|
||||
@end
|
||||
|
||||
|
||||
@interface ORKPedometerRecorderConfiguration()
|
||||
@interface ORKPedometerRecorderConfiguration ()
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ORKPedometerRecorderConfiguration
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super ork_init];
|
||||
return self;
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier {
|
||||
return [super initWithIdentifier:identifier];
|
||||
}
|
||||
|
||||
- (ORKRecorder *)recorderForStep:(ORKStep *)step outputDirectory:(NSURL *)outputDirectory
|
||||
{
|
||||
return [[ORKPedometerRecorder alloc] initWithStep:step
|
||||
outputDirectory:outputDirectory];
|
||||
- (ORKRecorder *)recorderForStep:(ORKStep *)step outputDirectory:(NSURL *)outputDirectory {
|
||||
return [[ORKPedometerRecorder alloc] initWithIdentifier:self.identifier
|
||||
step:step
|
||||
outputDirectory:outputDirectory];
|
||||
}
|
||||
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
self = [super initWithCoder:aDecoder];
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (BOOL)supportsSecureCoding
|
||||
{
|
||||
+ (BOOL)supportsSecureCoding {
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
BOOL isParentSame = [super isEqual:object];
|
||||
|
||||
return (isParentSame) ;
|
||||
return (isParentSame);
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (ORKPermissionMask)requestedPermissionMask {
|
||||
return ORKPermissionCoreMotionActivity;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
Copyright (c) 2015, James Cox. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder(s) nor the names of any contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission. No license is granted to the trademarks of
|
||||
the copyright holders even if such marks are included in this software.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKCustomStepView_Internal.h"
|
||||
|
||||
|
||||
@interface ORKReactionTimeContentView : ORKActiveStepCustomView
|
||||
|
||||
- (void)setStimulusHidden:(BOOL)hidden;
|
||||
|
||||
- (void)startSuccessAnimationWithDuration:(NSTimeInterval)duration completion:(nullable void (^)(void))completion;
|
||||
|
||||
- (void)startFailureAnimationWithDuration:(NSTimeInterval)duration completion:(nullable void (^)(void))completion;
|
||||
|
||||
- (void)resetAfterDelay:(NSTimeInterval)delay completion:(nullable void (^)(void))completion;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
Copyright (c) 2015, James Cox. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder(s) nor the names of any contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission. No license is granted to the trademarks of
|
||||
the copyright holders even if such marks are included in this software.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKReactionTimeContentView.h"
|
||||
#import "ORKReactionTimeStimulusView.h"
|
||||
#import "ORKNavigationContainerView.h"
|
||||
|
||||
|
||||
@interface ORKReactionTimeContentView ()
|
||||
|
||||
@property (nonatomic, strong) ORKReactionTimeStimulusView *stimulusView;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ORKReactionTimeContentView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
self.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[self addStimulusView];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)startSuccessAnimationWithDuration:(NSTimeInterval)duration completion:(void (^)(void))completion {
|
||||
[_stimulusView startSuccessAnimationWithDuration:duration completion:completion];
|
||||
}
|
||||
|
||||
- (void)startFailureAnimationWithDuration:(NSTimeInterval)duration completion:(void (^)(void))completion {
|
||||
[_stimulusView startFailureAnimationWithDuration:duration completion:completion];
|
||||
}
|
||||
|
||||
- (void)resetAfterDelay:(NSTimeInterval)delay completion:(nullable void (^)(void))completion {
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
_stimulusView.hidden = YES;
|
||||
if (completion) {
|
||||
completion();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)addStimulusView {
|
||||
if (!_stimulusView) {
|
||||
_stimulusView = [ORKReactionTimeStimulusView new];
|
||||
_stimulusView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
_stimulusView.backgroundColor = self.tintColor;
|
||||
[self addSubview:_stimulusView];
|
||||
[self setUpStimulusViewConstraints];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setStimulusHidden:(BOOL)hidden {
|
||||
_stimulusView.hidden = hidden;
|
||||
}
|
||||
|
||||
- (void)setUpStimulusViewConstraints {
|
||||
NSMutableArray *constraints = [NSMutableArray array];
|
||||
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_stimulusView
|
||||
attribute:NSLayoutAttributeCenterX
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self
|
||||
attribute:NSLayoutAttributeCenterX
|
||||
multiplier:1.0
|
||||
constant:0.0]];
|
||||
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_stimulusView
|
||||
attribute:NSLayoutAttributeTop
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self
|
||||
attribute:NSLayoutAttributeTop
|
||||
multiplier:1.0
|
||||
constant:8.0]];
|
||||
|
||||
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_stimulusView]-(>=0)-|"
|
||||
options:NSLayoutFormatAlignAllCenterX
|
||||
metrics:nil
|
||||
views:NSDictionaryOfVariableBindings(_stimulusView)]];
|
||||
|
||||
[NSLayoutConstraint activateConstraints:constraints];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
Copyright (c) 2015, James Cox. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder(s) nor the names of any contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission. No license is granted to the trademarks of
|
||||
the copyright holders even if such marks are included in this software.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
#import <AudioToolbox/AudioServices.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
ORK_CLASS_AVAILABLE
|
||||
@interface ORKReactionTimeStep : ORKActiveStep
|
||||
|
||||
@property (nonatomic, assign) NSTimeInterval maximumStimulusInterval;
|
||||
|
||||
@property (nonatomic, assign) NSTimeInterval minimumStimulusInterval;
|
||||
|
||||
@property (nonatomic, assign) NSTimeInterval timeout;
|
||||
|
||||
@property (nonatomic, assign) NSInteger numberOfAttempts;
|
||||
|
||||
@property (nonatomic, assign) double thresholdAcceleration;
|
||||
|
||||
@property (nonatomic, assign) SystemSoundID successSound;
|
||||
|
||||
@property (nonatomic, assign) SystemSoundID timeoutSound;
|
||||
|
||||
@property (nonatomic, assign) SystemSoundID failureSound;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
Copyright (c) 2015, James Cox. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder(s) nor the names of any contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission. No license is granted to the trademarks of
|
||||
the copyright holders even if such marks are included in this software.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKReactionTimeStep.h"
|
||||
#import "ORKReactionTimeViewController.h"
|
||||
#import "ORKHelpers.h"
|
||||
|
||||
|
||||
@implementation ORKReactionTimeStep
|
||||
|
||||
+ (Class)stepViewControllerClass {
|
||||
return [ORKReactionTimeViewController class];
|
||||
}
|
||||
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier {
|
||||
self = [super initWithIdentifier:identifier];
|
||||
self.shouldContinueOnFinish = YES;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
ORKReactionTimeStep *step = [super copyWithZone:zone];
|
||||
step.maximumStimulusInterval = self.maximumStimulusInterval;
|
||||
step.minimumStimulusInterval = self.minimumStimulusInterval;
|
||||
step.thresholdAcceleration = self.thresholdAcceleration;
|
||||
step.timeout = self.timeout;
|
||||
step.numberOfAttempts = self.numberOfAttempts;
|
||||
step.successSound = self.successSound;
|
||||
step.timeoutSound = self.timeoutSound;
|
||||
step.failureSound = self.failureSound;
|
||||
return step;
|
||||
}
|
||||
|
||||
- (void)validateParameters {
|
||||
[super validateParameters];
|
||||
|
||||
if (self.minimumStimulusInterval <= 0) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException
|
||||
reason:@"minimumStimulusInterval must be greater than zero"
|
||||
userInfo:nil];
|
||||
}
|
||||
if (self.maximumStimulusInterval < self.minimumStimulusInterval) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException
|
||||
reason:@"maximumStimulusInterval can not be less than minimumStimulusInterval"
|
||||
userInfo:nil];
|
||||
}
|
||||
if (self.thresholdAcceleration <= 0) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException
|
||||
reason:@"thresholdAcceleration must be greater than zero"
|
||||
userInfo:nil];
|
||||
}
|
||||
if (self.timeout <= 0) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException
|
||||
reason:@"timeout must be greater than zero"
|
||||
userInfo:nil];
|
||||
}
|
||||
if (self.numberOfAttempts <= 0) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException
|
||||
reason:@"numberOfAttempts must be greater than zero"
|
||||
userInfo:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self) {
|
||||
ORK_DECODE_DOUBLE(aDecoder, maximumStimulusInterval);
|
||||
ORK_DECODE_DOUBLE(aDecoder, minimumStimulusInterval);
|
||||
ORK_DECODE_DOUBLE(aDecoder, thresholdAcceleration);
|
||||
ORK_DECODE_DOUBLE(aDecoder, timeout);
|
||||
ORK_DECODE_UINT32(aDecoder, successSound);
|
||||
ORK_DECODE_UINT32(aDecoder, timeoutSound);
|
||||
ORK_DECODE_UINT32(aDecoder, failureSound);
|
||||
ORK_DECODE_INTEGER(aDecoder, numberOfAttempts);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[super encodeWithCoder:aCoder];
|
||||
ORK_ENCODE_DOUBLE(aCoder, maximumStimulusInterval);
|
||||
ORK_ENCODE_DOUBLE(aCoder, minimumStimulusInterval);
|
||||
ORK_ENCODE_DOUBLE(aCoder, thresholdAcceleration);
|
||||
ORK_ENCODE_DOUBLE(aCoder, timeout);
|
||||
ORK_ENCODE_UINT32(aCoder, successSound);
|
||||
ORK_ENCODE_UINT32(aCoder, timeoutSound);
|
||||
ORK_ENCODE_UINT32(aCoder, failureSound);
|
||||
ORK_ENCODE_INTEGER(aCoder, numberOfAttempts);
|
||||
}
|
||||
|
||||
+ (BOOL)supportsSecureCoding {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
BOOL isParentSame = [super isEqual:object];
|
||||
|
||||
__typeof(self) castObject = object;
|
||||
return (isParentSame &&
|
||||
(self.maximumStimulusInterval == castObject.maximumStimulusInterval) &&
|
||||
(self.minimumStimulusInterval == castObject.minimumStimulusInterval) &&
|
||||
(self.thresholdAcceleration == castObject.thresholdAcceleration) &&
|
||||
(self.timeout == castObject.timeout) &&
|
||||
(self.successSound == castObject.successSound) &&
|
||||
(self.timeoutSound == castObject.timeoutSound) &&
|
||||
(self.failureSound == castObject.failureSound) &&
|
||||
(self.numberOfAttempts == castObject.numberOfAttempts));
|
||||
}
|
||||
|
||||
- (BOOL)allowsBackNavigation {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)startsFinished {
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Copyright (c) 2015, James Cox. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder(s) nor the names of any contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission. No license is granted to the trademarks of
|
||||
the copyright holders even if such marks are included in this software.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKCustomStepView_Internal.h"
|
||||
|
||||
|
||||
@interface ORKReactionTimeStimulusView : UIView
|
||||
|
||||
- (void)reset;
|
||||
|
||||
- (void)startSuccessAnimationWithDuration:(NSTimeInterval)duration completion:(nullable void(^)(void))completion;
|
||||
|
||||
- (void)startFailureAnimationWithDuration:(NSTimeInterval)duration completion:(nullable void(^)(void))completion;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
Copyright (c) 2015, James Cox. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder(s) nor the names of any contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission. No license is granted to the trademarks of
|
||||
the copyright holders even if such marks are included in this software.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKReactionTimeStimulusView.h"
|
||||
|
||||
|
||||
@implementation ORKReactionTimeStimulusView {
|
||||
CAShapeLayer *_tickLayer;
|
||||
CAShapeLayer *_crossLayer;
|
||||
}
|
||||
|
||||
static const CGFloat RoundReactionTimeViewDiameter = 122;
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.layer.cornerRadius = RoundReactionTimeViewDiameter * 0.5;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CGSize)intrinsicContentSize {
|
||||
return CGSizeMake(RoundReactionTimeViewDiameter, RoundReactionTimeViewDiameter);
|
||||
}
|
||||
|
||||
- (void)reset {
|
||||
[_tickLayer removeFromSuperlayer];
|
||||
[_crossLayer removeFromSuperlayer];
|
||||
_tickLayer = nil;
|
||||
_crossLayer = nil;
|
||||
self.layer.backgroundColor = self.tintColor.CGColor;
|
||||
}
|
||||
|
||||
- (void)startSuccessAnimationWithDuration:(NSTimeInterval)duration completion:(void(^)(void))completion {
|
||||
if (self.hidden) {
|
||||
if (completion) {
|
||||
completion();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
[self addTickLayer];
|
||||
[CATransaction begin];
|
||||
[CATransaction setCompletionBlock:completion];
|
||||
CAMediaTimingFunction *timing = [[CAMediaTimingFunction alloc] initWithControlPoints:0.180739998817444 :0 :0.577960014343262 :0.918200016021729];
|
||||
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
|
||||
[animation setTimingFunction:timing];
|
||||
animation.removedOnCompletion = NO;
|
||||
[animation setFillMode:kCAFillModeForwards];
|
||||
animation.fromValue = @(0);
|
||||
animation.toValue = @(1);
|
||||
animation.duration = duration;
|
||||
[_tickLayer addAnimation:animation forKey:@"strokeEnd"];
|
||||
[CATransaction commit];
|
||||
}
|
||||
|
||||
- (void)startFailureAnimationWithDuration:(NSTimeInterval)duration completion:(void(^)(void))completion {
|
||||
self.hidden = NO;
|
||||
|
||||
self.layer.backgroundColor = [UIColor clearColor].CGColor;
|
||||
[self addCrossLayer];
|
||||
[CATransaction begin];
|
||||
[CATransaction setCompletionBlock:completion];
|
||||
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
|
||||
[animation setFillMode:kCAFillModeForwards];
|
||||
animation.fromValue = @([(CAShapeLayer *)[_crossLayer presentationLayer] strokeEnd]);
|
||||
animation.toValue = @(1);
|
||||
animation.duration = duration;
|
||||
_crossLayer.strokeEnd = 1;
|
||||
[_crossLayer addAnimation:animation forKey:@"strokeEnd"];
|
||||
[CATransaction commit];
|
||||
}
|
||||
|
||||
- (void)setHidden:(BOOL)hidden {
|
||||
[self reset];
|
||||
[super setHidden:hidden];
|
||||
}
|
||||
|
||||
- (void)addCrossLayer {
|
||||
_crossLayer = [self lineDrawingLayer];
|
||||
_crossLayer.strokeColor = [UIColor redColor].CGColor;
|
||||
_crossLayer.path = [self crossPath];
|
||||
[self.layer addSublayer:_crossLayer];
|
||||
}
|
||||
|
||||
- (void)addTickLayer {
|
||||
_tickLayer = [self lineDrawingLayer];
|
||||
_tickLayer.strokeColor = [UIColor whiteColor].CGColor;
|
||||
_tickLayer.path = [self tickPath];
|
||||
[self.layer addSublayer:_tickLayer];
|
||||
}
|
||||
|
||||
- (CGPathRef)concealPath:(CGFloat)radius {
|
||||
return [[UIBezierPath bezierPathWithArcCenter:CGPointMake(radius, radius)
|
||||
radius:radius / 2
|
||||
startAngle:M_PI + M_PI_2
|
||||
endAngle:-M_PI_2
|
||||
clockwise:NO] CGPath];
|
||||
}
|
||||
|
||||
- (CGPathRef)tickPath {
|
||||
UIBezierPath *path = [self linePath];
|
||||
[path moveToPoint:(CGPoint){37,65}];
|
||||
[path addLineToPoint:(CGPoint){50,78}];
|
||||
[path addLineToPoint:(CGPoint){87,42}];
|
||||
return path.CGPath;
|
||||
}
|
||||
|
||||
- (CGPathRef)crossPath {
|
||||
UIBezierPath *path = [self linePath];
|
||||
[path moveToPoint:(CGPoint){45,78}];
|
||||
[path addLineToPoint:(CGPoint){82,42}];
|
||||
[path moveToPoint:(CGPoint){45,42}];
|
||||
[path addLineToPoint:(CGPoint){82,78}];
|
||||
return path.CGPath;
|
||||
}
|
||||
|
||||
- (UIBezierPath *)linePath {
|
||||
UIBezierPath *path = [UIBezierPath new];
|
||||
path.lineCapStyle = kCGLineCapRound;
|
||||
path.lineWidth = 5;
|
||||
return path;
|
||||
}
|
||||
|
||||
- (CAShapeLayer *)lineDrawingLayer {
|
||||
CAShapeLayer *shapeLayer = [CAShapeLayer new];
|
||||
shapeLayer.strokeEnd = 0;
|
||||
shapeLayer.lineWidth = 5;
|
||||
shapeLayer.lineCap = kCALineCapRound;
|
||||
shapeLayer.lineJoin = kCALineJoinRound;
|
||||
shapeLayer.frame = self.layer.bounds;
|
||||
shapeLayer.backgroundColor = [UIColor clearColor].CGColor;
|
||||
shapeLayer.fillColor = nil;
|
||||
return shapeLayer;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
Copyright (c) 2015, James Cox. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder(s) nor the names of any contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission. No license is granted to the trademarks of
|
||||
the copyright holders even if such marks are included in this software.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
ORK_CLASS_AVAILABLE
|
||||
@interface ORKReactionTimeViewController : ORKActiveStepViewController
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
Copyright (c) 2015, James Cox. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder(s) nor the names of any contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission. No license is granted to the trademarks of
|
||||
the copyright holders even if such marks are included in this software.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKReactionTimeViewController.h"
|
||||
#import "ORKActiveStepViewController_Internal.h"
|
||||
#import "ORKActiveStepView.h"
|
||||
#import "ORKReactionTimeContentView.h"
|
||||
#import "ORKReactionTimeStep.h"
|
||||
#import <CoreMotion/CMDeviceMotion.h>
|
||||
#import <AudioToolbox/AudioServices.h>
|
||||
|
||||
|
||||
@implementation ORKReactionTimeViewController {
|
||||
ORKReactionTimeContentView *_reactionTimeContentView;
|
||||
NSMutableArray *_results;
|
||||
NSTimer *_stimulusTimer;
|
||||
NSTimer *_timeoutTimer;
|
||||
NSTimeInterval _stimulusTimestamp;
|
||||
BOOL _validResult;
|
||||
BOOL _timedOut;
|
||||
BOOL _shouldIndicateFailure;
|
||||
}
|
||||
|
||||
static const NSTimeInterval OutcomeAnimationDuration = 0.3;
|
||||
|
||||
#pragma mark - UIViewController
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
// Do any additional setup after loading the view.
|
||||
[self configureTitle];
|
||||
_results = [NSMutableArray new];
|
||||
_reactionTimeContentView = [ORKReactionTimeContentView new];
|
||||
self.activeStepView.activeCustomView = _reactionTimeContentView;
|
||||
self.activeStepView.stepViewFillsAvailableSpace = YES;
|
||||
[_reactionTimeContentView setStimulusHidden:YES];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
[super viewDidAppear:animated];
|
||||
[self start];
|
||||
_shouldIndicateFailure = YES;
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
[super viewWillDisappear:animated];
|
||||
_shouldIndicateFailure = NO;
|
||||
}
|
||||
|
||||
#pragma mark - ORKActiveStepViewController
|
||||
|
||||
- (void)start {
|
||||
[super start];
|
||||
[self startStimulusTimer];
|
||||
|
||||
}
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
|
||||
{
|
||||
if(event.type == UIEventSubtypeMotionShake)
|
||||
{
|
||||
if (_validResult) {
|
||||
ORKReactionTimeResult *reactionTimeResult = [[ORKReactionTimeResult alloc] initWithIdentifier:self.step.identifier];
|
||||
reactionTimeResult.timestamp = _stimulusTimestamp;
|
||||
[_results addObject:reactionTimeResult];
|
||||
}
|
||||
[self attemptDidFinish];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
- (ORKStepResult *)result {
|
||||
ORKStepResult *stepResult = [super result];
|
||||
stepResult.results = _results;
|
||||
return stepResult;
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(NSNotification *)notification {
|
||||
[super applicationWillResignActive:notification];
|
||||
_validResult = NO;
|
||||
[_stimulusTimer invalidate];
|
||||
[_timeoutTimer invalidate];
|
||||
}
|
||||
|
||||
- (void)applicationDidBecomeActive:(NSNotification *)notification {
|
||||
[super applicationDidBecomeActive:notification];
|
||||
[self resetAfterDelay:0];
|
||||
}
|
||||
|
||||
#pragma mark - ORKRecorderDelegate
|
||||
|
||||
- (void)recorder:(ORKRecorder *)recorder didCompleteWithResult:(ORKResult *)result {
|
||||
if (_validResult) {
|
||||
ORKReactionTimeResult *reactionTimeResult = [[ORKReactionTimeResult alloc] initWithIdentifier:self.step.identifier];
|
||||
reactionTimeResult.timestamp = _stimulusTimestamp;
|
||||
reactionTimeResult.fileResult = (ORKFileResult *)result;
|
||||
[_results addObject:reactionTimeResult];
|
||||
}
|
||||
[self attemptDidFinish];
|
||||
}
|
||||
|
||||
#pragma mark - ORKDeviceMotionRecorderDelegate
|
||||
|
||||
- (void)deviceMotionRecorderDidUpdateWithMotion:(CMDeviceMotion *)motion {
|
||||
CMAcceleration v = motion.userAcceleration;
|
||||
double vectorMagnitude = sqrt(((v.x * v.x) + (v.y * v.y) + (v.z * v.z)));
|
||||
if (vectorMagnitude > [self reactionTimeStep].thresholdAcceleration) {
|
||||
[self stopRecorders];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - ORKReactionTimeStepViewController
|
||||
|
||||
- (ORKReactionTimeStep *)reactionTimeStep {
|
||||
return (ORKReactionTimeStep *)self.step;
|
||||
}
|
||||
|
||||
- (void)configureTitle {
|
||||
NSString *format = ORKLocalizedString(@"REACTION_TIME_TASK_ATTEMPTS_FORMAT", nil);
|
||||
NSString *text = [NSString stringWithFormat:format, _results.count + 1, [self reactionTimeStep].numberOfAttempts];
|
||||
[self.activeStepView updateTitle:ORKLocalizedString(@"REACTION_TIME_TASK_ACTIVE_STEP_TITLE", nil) text:text];
|
||||
}
|
||||
|
||||
- (void)attemptDidFinish {
|
||||
void (^completion)(void) = ^{
|
||||
if ([_results count] == [self reactionTimeStep].numberOfAttempts) {
|
||||
[self finish];
|
||||
} else {
|
||||
[self resetAfterDelay:2];
|
||||
}
|
||||
};
|
||||
if (_validResult) {
|
||||
[self indicateSuccess:completion];
|
||||
} else {
|
||||
[self indicateFailure:completion];
|
||||
}
|
||||
_validResult = NO;
|
||||
_timedOut = NO;
|
||||
[_stimulusTimer invalidate];
|
||||
[_timeoutTimer invalidate];
|
||||
}
|
||||
|
||||
- (void)indicateSuccess:(void(^)(void))completion {
|
||||
[_reactionTimeContentView startSuccessAnimationWithDuration:OutcomeAnimationDuration completion:completion];
|
||||
AudioServicesPlaySystemSound([self reactionTimeStep].successSound);
|
||||
}
|
||||
|
||||
- (void)indicateFailure:(void(^)(void))completion {
|
||||
if (!_shouldIndicateFailure) {
|
||||
return;
|
||||
}
|
||||
[_reactionTimeContentView startFailureAnimationWithDuration:OutcomeAnimationDuration completion:completion];
|
||||
SystemSoundID sound = _timedOut ? [self reactionTimeStep].timeoutSound : [self reactionTimeStep].failureSound;
|
||||
AudioServicesPlayAlertSound(sound);
|
||||
}
|
||||
|
||||
- (void)resetAfterDelay:(NSTimeInterval)delay {
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
[_reactionTimeContentView resetAfterDelay:delay completion:^{
|
||||
[weakSelf configureTitle];
|
||||
[weakSelf start];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)startStimulusTimer {
|
||||
_stimulusTimer = [NSTimer scheduledTimerWithTimeInterval:[self stimulusInterval] target:self selector:@selector(stimulusTimerDidFire) userInfo:nil repeats:NO];
|
||||
}
|
||||
|
||||
- (void)stimulusTimerDidFire {
|
||||
_stimulusTimestamp = [NSProcessInfo processInfo].systemUptime;
|
||||
[_reactionTimeContentView setStimulusHidden:NO];
|
||||
_validResult = YES;
|
||||
[self startTimeoutTimer];
|
||||
}
|
||||
|
||||
- (void)startTimeoutTimer {
|
||||
NSTimeInterval timeout = [self reactionTimeStep].timeout;
|
||||
if (timeout > 0) {
|
||||
_timeoutTimer = [NSTimer scheduledTimerWithTimeInterval:timeout target:self selector:@selector(timeoutTimerDidFire) userInfo:nil repeats:NO];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)timeoutTimerDidFire {
|
||||
_validResult = NO;
|
||||
_timedOut = YES;
|
||||
[self stopRecorders];
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
// Device motion recorder won't work, so manually trigger didfinish
|
||||
[self attemptDidFinish];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (NSTimeInterval)stimulusInterval {
|
||||
ORKReactionTimeStep *step = [self reactionTimeStep];
|
||||
NSTimeInterval range = step.maximumStimulusInterval - step.minimumStimulusInterval;
|
||||
NSTimeInterval randomFactor = ((NSTimeInterval)rand() / RAND_MAX) * range;
|
||||
return randomFactor + step.minimumStimulusInterval;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
Copyright (c) 2015, Apple Inc. All rights reserved.
|
||||
Copyright (c) 2015, Ricardo Sánchez-Sáez.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
@@ -34,6 +35,7 @@
|
||||
#import <HealthKit/HealthKit.h>
|
||||
#import <ResearchKit/ORKResult.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ORKRecorder;
|
||||
@@ -57,21 +59,35 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
ORK_CLASS_AVAILABLE
|
||||
@interface ORKRecorderConfiguration : NSObject <NSSecureCoding>
|
||||
|
||||
/**
|
||||
The `init` method is unavailable outside the framework on `ORKRecorderConfiguration`,
|
||||
/*
|
||||
The `init` and `new` methods are unavailable outside the framework on `ORKRecorderConfiguration`,
|
||||
because it is an abstract class.
|
||||
|
||||
`ORKRecorderConfiguration` classes should be initialized with custom designated
|
||||
initializers on each subclass.
|
||||
*/
|
||||
+ (instancetype)new NS_UNAVAILABLE;
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/**
|
||||
A short string that uniquely identifies the recorder configuration within the step.
|
||||
|
||||
The identifier is reproduced in the results of a recorder created from this configuration. In fact, the only way to link a result
|
||||
(an `ORKFileResult` object) to the recorder that generated it is to look at the value of
|
||||
`identifier`. To accurately identify recorder results, you need to ensure that recorder identifiers
|
||||
are unique within each step.
|
||||
|
||||
In some cases, it can be useful to link the recorder identifier to a unique identifier in a
|
||||
database; in other cases, it can make sense to make the identifier human
|
||||
readable.
|
||||
*/
|
||||
@property (nonatomic, copy, readonly) NSString *identifier;
|
||||
|
||||
/**
|
||||
Returns a recorder instance using this configuration.
|
||||
|
||||
@param step The step for which this recorder is being created.
|
||||
@param outputDirectory The directory in which all output file data should be written
|
||||
(if producing `ORKFileResult` instances).
|
||||
@param step The step for which this recorder is being created.
|
||||
@param outputDirectory The directory in which all output file data should be written (if producing `ORKFileResult` instances).
|
||||
|
||||
@return A configured recorder instance.
|
||||
*/
|
||||
@@ -117,17 +133,25 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
This method is the designated initializer.
|
||||
|
||||
@param frequency The frequency of accelerometer data collection in samples per second (Hz).
|
||||
@param identifier The unique identifier of the recorder configuration.
|
||||
@param frequency The frequency of accelerometer data collection in samples per second (Hz).
|
||||
|
||||
@return An initialized accelerometer recorder configuration.
|
||||
*/
|
||||
- (instancetype)initWithFrequency:(double)frequency NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier frequency:(double)frequency NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
Returns a new accelerometer recorder configuration initialized from data in the given unarchiver.
|
||||
|
||||
@param aDecoder Coder from which to initialize the accelerometer recorder configuration.
|
||||
|
||||
@return A new accelerometer recorder configuration.
|
||||
*/
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
/**
|
||||
The `ORKAudioRecorderConfiguration` class represents a configuration that records
|
||||
audio data during an active step.
|
||||
@@ -159,10 +183,20 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
For information on the settings available for an audio recorder, see "AV Foundation Audio Settings Constants".
|
||||
|
||||
@param recorderSettings The settings for the recording session.
|
||||
@param identifier The unique identifier of the recorder configuration.
|
||||
@param recorderSettings The settings for the recording session.
|
||||
|
||||
@return An initialized audio recorder configuration.
|
||||
*/
|
||||
- (instancetype)initWithRecorderSettings:(NSDictionary *)recorderSettings NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier recorderSettings:(NSDictionary *)recorderSettings NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
Returns a new audio recorder configuration initialized from data in the given unarchiver.
|
||||
|
||||
@param aDecoder Coder from which to initialize the audio recorder configuration.
|
||||
|
||||
@return A new audio recorder configuration.
|
||||
*/
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
@@ -196,17 +230,25 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
This method is the designated initializer.
|
||||
|
||||
@param frequency Motion data collection frequency in samples per second (Hz).
|
||||
@param identifier The unique identifier of the recorder configuration.
|
||||
@param frequency Motion data collection frequency in samples per second (Hz).
|
||||
|
||||
@return An initialized device motion recorder configuration.
|
||||
*/
|
||||
- (instancetype)initWithFrequency:(double)frequency NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier frequency:(double)frequency NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
Returns a new device motion recorder configuration initialized from data in the given unarchiver.
|
||||
|
||||
@param aDecoder Coder from which to initialize the device motion recorder configuration.
|
||||
|
||||
@return A new device motion recorder configuration.
|
||||
*/
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
/**
|
||||
The `ORKPedometerRecorderConfiguration` class represents a configuration
|
||||
that records pedometer data during an active step.
|
||||
@@ -228,17 +270,30 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
/**
|
||||
Returns an initialized pedometer recorder configuration.
|
||||
|
||||
This method is the designated initializer.
|
||||
|
||||
Note that the recorder instantiates a `CMPedometer` object, so no parameters are required.
|
||||
*/
|
||||
- (instancetype)init NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
The recorder instantiates a `CMPedometer` object, so no additional parameters besides
|
||||
the identifier are required.
|
||||
|
||||
This method is the designated initializer.
|
||||
|
||||
@param identifier The unique identifier of the recorder configuration.
|
||||
|
||||
@return An initialized pedometer recorder configuration.
|
||||
*/
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
Returns a new pedometer recorder configuration initialized from data in the given unarchiver.
|
||||
|
||||
@param aDecoder Coder from which to initialize the pedometer recorder configuration.
|
||||
|
||||
@return A new pedometer recorder configuration.
|
||||
*/
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/**
|
||||
The `ORKLocationRecorderConfiguration` class represents a configuration
|
||||
that records location data during an active step.
|
||||
@@ -255,6 +310,8 @@ ORK_CLASS_AVAILABLE
|
||||
To use a recorder, include its configuration in the `recorderConfigurations` property
|
||||
of an `ORKActiveStep` object, include that step in a task, and present it with
|
||||
a task view controller.
|
||||
|
||||
No additional parameters besides the identifier are required.
|
||||
*/
|
||||
ORK_CLASS_AVAILABLE
|
||||
@interface ORKLocationRecorderConfiguration : ORKRecorderConfiguration
|
||||
@@ -263,10 +320,20 @@ ORK_CLASS_AVAILABLE
|
||||
Returns an initialized location recorder configuration.
|
||||
|
||||
This method is the designated initializer.
|
||||
|
||||
@param identifier The unique identifier of the recorder configuration.
|
||||
|
||||
No parameters are required.
|
||||
@return An initialized location recorder configuration.
|
||||
*/
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
Returns a new location recorder configuration initialized from data in the given unarchiver.
|
||||
|
||||
@param aDecoder Coder from which to initialize the location recorder configuration.
|
||||
|
||||
@return A new location recorder configuration.
|
||||
*/
|
||||
- (instancetype)init NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
@@ -294,12 +361,21 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
This method is the designated initializer.
|
||||
|
||||
@param identifier The unique identifier of the recorder configuration.
|
||||
@param quantityType The quantity type that should be collected during the active task.
|
||||
@param unit The unit for the data that should be collected and serialized.
|
||||
|
||||
@return An initialized health quantity type recorder configuration.
|
||||
*/
|
||||
- (instancetype)initWithHealthQuantityType:(HKQuantityType *)quantityType unit:(HKUnit *)unit NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier healthQuantityType:(HKQuantityType *)quantityType unit:(HKUnit *)unit NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
Returns a new health quantity type recorder configuration initialized from data in the given unarchiver.
|
||||
|
||||
@param aDecoder Coder from which to initialize the health quantity type recorder configuration.
|
||||
|
||||
@return A new health quantity type recorder configuration.
|
||||
*/
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
@@ -307,7 +383,6 @@ ORK_CLASS_AVAILABLE
|
||||
*/
|
||||
@property (nonatomic, readonly, copy) HKQuantityType *quantityType;
|
||||
|
||||
|
||||
/**
|
||||
The unit in which to serialize the data from HealthKit. (read-only)
|
||||
*/
|
||||
@@ -315,6 +390,7 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/**
|
||||
The `ORKRecorderDelegate` protocol defines methods that the delegate of an `ORKRecorder` object should use to handle errors and log the
|
||||
completed results.
|
||||
@@ -346,6 +422,7 @@ need to implement it.
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/**
|
||||
A recorder is the runtime companion to an `ORKRecorderConfiguration` object, and is
|
||||
usually generated by one.
|
||||
@@ -369,12 +446,27 @@ need to implement it.
|
||||
ORK_CLASS_AVAILABLE
|
||||
@interface ORKRecorder : NSObject
|
||||
|
||||
+ (instancetype)new NS_UNAVAILABLE;
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/// @name Configuration
|
||||
|
||||
@property (nonatomic, weak, nullable) id<ORKRecorderDelegate> delegate;
|
||||
|
||||
/**
|
||||
A short string that uniquely identifies the recorder (usually assigned by the recorder configuration).
|
||||
|
||||
The identifier is reproduced in the results of a recorder created from this configuration. In fact, the only way to link a result
|
||||
(an `ORKFileResult` object) to the recorder that generated it is to look at the value of
|
||||
`identifier`. To accurately identify recorder results, you need to ensure that recorder identifiers
|
||||
are unique within each step.
|
||||
|
||||
In some cases, it can be useful to link the recorder identifier to a unique identifier in a
|
||||
database; in other cases, it can make sense to make the identifier human
|
||||
readable.
|
||||
*/
|
||||
@property (nonatomic, copy, readonly) NSString *identifier;
|
||||
|
||||
/**
|
||||
The step that produced this recorder, configured during initialization.
|
||||
*/
|
||||
@@ -425,4 +517,3 @@ ORK_CLASS_AVAILABLE
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
Copyright (c) 2015, Apple Inc. All rights reserved.
|
||||
Copyright (c) 2015, Ricardo Sánchez-Sáez.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
@@ -36,27 +37,36 @@
|
||||
#import "ORKDataLogger.h"
|
||||
#import "ORKDefines_Private.h"
|
||||
|
||||
|
||||
@implementation ORKRecorderConfiguration
|
||||
|
||||
- (instancetype)ork_init
|
||||
{
|
||||
return [super init];
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
if (nil == identifier) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"identifier cannot be nil." userInfo:nil];
|
||||
}
|
||||
_identifier = [identifier copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
return [super init];
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
ORK_DECODE_OBJ_CLASS(aDecoder, identifier, NSString);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder
|
||||
{
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
ORK_ENCODE_OBJ(aCoder, identifier);
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
if ([self class] != [object class]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@@ -64,17 +74,14 @@
|
||||
return 0;
|
||||
}
|
||||
|
||||
+ (BOOL)supportsSecureCoding
|
||||
{
|
||||
+ (BOOL)supportsSecureCoding {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (ORKRecorder *)recorderForStep:(ORKStep *)step outputDirectory:(NSURL *)outputDirectory
|
||||
{
|
||||
- (ORKRecorder *)recorderForStep:(ORKStep *)step outputDirectory:(NSURL *)outputDirectory {
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
- (NSSet *)requestedHealthKitTypesForReading {
|
||||
return nil;
|
||||
}
|
||||
@@ -82,30 +89,26 @@
|
||||
return ORKPermissionNone;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@implementation ORKRecorder
|
||||
{
|
||||
|
||||
@implementation ORKRecorder {
|
||||
UIBackgroundTaskIdentifier _backgroundTask;
|
||||
NSUUID *_recorderUUID;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
- (instancetype)init {
|
||||
@throw [NSException exceptionWithName:NSGenericException reason:@"Use designated initializer" userInfo:nil];
|
||||
}
|
||||
|
||||
- (instancetype)ork_init
|
||||
{
|
||||
return [super init];
|
||||
}
|
||||
|
||||
- (instancetype)initWithStep:(ORKStep *)step outputDirectory:(NSURL *)outputDirectory;
|
||||
{
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier step:(ORKStep *)step outputDirectory:(NSURL *)outputDirectory {
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
if (self) {
|
||||
if (nil == identifier) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"identifier cannot be nil." userInfo:nil];
|
||||
}
|
||||
|
||||
_identifier = [identifier copy];
|
||||
_outputDirectory = outputDirectory;
|
||||
self.step = step;
|
||||
_backgroundTask = NSNotFound;
|
||||
@@ -115,91 +118,70 @@
|
||||
}
|
||||
|
||||
- (void)viewController:(UIViewController *)viewController willStartStepWithView:(UIView *)view {
|
||||
|
||||
}
|
||||
|
||||
- (void)start
|
||||
{
|
||||
if (self.continuesInBackground)
|
||||
{
|
||||
- (void)start {
|
||||
if (self.continuesInBackground) {
|
||||
UIApplication *app = [UIApplication sharedApplication];
|
||||
UIBackgroundTaskIdentifier oldTask = _backgroundTask;
|
||||
_backgroundTask = [app beginBackgroundTaskWithName:[NSString stringWithFormat:@"%@.%p",NSStringFromClass([self class]),self] expirationHandler:^{
|
||||
_backgroundTask = [app beginBackgroundTaskWithName:[NSString stringWithFormat:@"%@.%p",NSStringFromClass([self class]),self]
|
||||
expirationHandler:^{
|
||||
[self stop];
|
||||
}];
|
||||
if (oldTask != NSNotFound)
|
||||
{
|
||||
if (oldTask != NSNotFound) {
|
||||
[app endBackgroundTask:oldTask];
|
||||
}
|
||||
}
|
||||
|
||||
self.startDate = [NSDate date];
|
||||
}
|
||||
|
||||
|
||||
- (void)stop
|
||||
{
|
||||
- (void)stop {
|
||||
[self finishRecordingWithError:nil];
|
||||
[self reset];
|
||||
}
|
||||
|
||||
|
||||
- (void)finishRecordingWithError:(NSError *)error
|
||||
{
|
||||
- (void)finishRecordingWithError:(NSError *)error {
|
||||
// NOTE. This method may be called multiple times (once when someone tries
|
||||
// to finish, and another time with -stop is actually called.
|
||||
|
||||
|
||||
if (error)
|
||||
{
|
||||
if (error) {
|
||||
// ALWAYS report errors to the delegate, even if we think we're finished already
|
||||
|
||||
id<ORKRecorderDelegate> localDelegate = self.delegate;
|
||||
if (localDelegate && [localDelegate respondsToSelector:@selector(recorder:didFailWithError:)])
|
||||
{
|
||||
if (localDelegate && [localDelegate respondsToSelector:@selector(recorder:didFailWithError:)]) {
|
||||
[localDelegate recorder:self didFailWithError:error];
|
||||
}
|
||||
|
||||
[self reset];
|
||||
}
|
||||
|
||||
|
||||
if (_backgroundTask != NSNotFound)
|
||||
{
|
||||
if (_backgroundTask != NSNotFound) {
|
||||
// End the background task asynchronously, so whatever we're doing cleaning up the recorder has a chance to complete.
|
||||
UIBackgroundTaskIdentifier ident = _backgroundTask;
|
||||
UIBackgroundTaskIdentifier identifier = _backgroundTask;
|
||||
_backgroundTask = NSNotFound;
|
||||
|
||||
// Hold the background task for a little extra to give time for the next step to kick in,
|
||||
// if it is an automatic transition.
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[[UIApplication sharedApplication] endBackgroundTask:ident];
|
||||
[[UIApplication sharedApplication] endBackgroundTask:identifier];
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (NSURL *)recordingDirectoryURL
|
||||
{
|
||||
- (NSURL *)recordingDirectoryURL {
|
||||
if (! _outputDirectory) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return [NSURL fileURLWithPath:[_outputDirectory.path stringByAppendingPathComponent:[NSString stringWithFormat:@"recorder-%@",[_recorderUUID UUIDString]]]];
|
||||
}
|
||||
|
||||
- (NSString *)recorderType
|
||||
{
|
||||
- (NSString *)recorderType {
|
||||
return @"recorder";
|
||||
}
|
||||
|
||||
- (NSString *)logName
|
||||
{
|
||||
return [NSString stringWithFormat:@"%@_%@", [self recorderType],self.step.identifier];
|
||||
- (NSString *)logName {
|
||||
return [NSString stringWithFormat:@"%@_%@", [self recorderType],self.identifier];
|
||||
}
|
||||
|
||||
- (ORKDataLogger *)makeJSONDataLoggerWithError:(NSError * __autoreleasing *)error
|
||||
{
|
||||
- (ORKDataLogger *)makeJSONDataLoggerWithError:(NSError * __autoreleasing *)error {
|
||||
NSURL *workingDir = [self recordingDirectoryURL];
|
||||
if (! workingDir) {
|
||||
if (error) {
|
||||
@@ -211,8 +193,8 @@
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString *ident = [self logName];
|
||||
NSString *logName = [ident stringByReplacingOccurrencesOfString:@"-" withString:@"_"];
|
||||
NSString *identifier = [self logName];
|
||||
NSString *logName = [identifier stringByReplacingOccurrencesOfString:@"-" withString:@"_"];
|
||||
|
||||
// Class B data protection for temporary file during active task logging.
|
||||
ORKDataLogger *logger = [[ORKDataLogger alloc] initWithDirectory:workingDir logName:logName formatter:[ORKJSONLogFormatter new] delegate:nil];
|
||||
@@ -221,8 +203,7 @@
|
||||
return logger;
|
||||
}
|
||||
|
||||
- (void)reset
|
||||
{
|
||||
- (void)reset {
|
||||
_recorderUUID = [NSUUID UUID];
|
||||
}
|
||||
|
||||
@@ -235,9 +216,9 @@
|
||||
}
|
||||
|
||||
- (void)applyFileProtection:(ORKFileProtectionMode)fileProtection toFileAtURL:(NSURL *)url {
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSError *error = nil;
|
||||
if (! [fm setAttributes:@{NSFileProtectionKey : ORKFileProtectionFromMode(fileProtection)} ofItemAtPath:[url path] error:&error]) {
|
||||
if (! [fileManager setAttributes:@{NSFileProtectionKey : ORKFileProtectionFromMode(fileProtection)} ofItemAtPath:[url path] error:&error]) {
|
||||
ORK_Log_Debug(@"Error setting %@ on %@: %@", ORKFileProtectionFromMode(fileProtection), url, error);
|
||||
}
|
||||
}
|
||||
@@ -247,7 +228,7 @@
|
||||
id<ORKRecorderDelegate> localDelegate = self.delegate;
|
||||
if (fileUrl && !error) {
|
||||
if (localDelegate && [localDelegate respondsToSelector:@selector(recorder:didCompleteWithResult:)]) {
|
||||
ORKFileResult *result = [[ORKFileResult alloc] initWithIdentifier:(NSString *__nonnull)self.step.identifier];
|
||||
ORKFileResult *result = [[ORKFileResult alloc] initWithIdentifier:self.identifier];
|
||||
result.contentType = [self mimeType];
|
||||
result.fileURL = fileUrl;
|
||||
result.userInfo = [self userInfo];
|
||||
@@ -259,8 +240,7 @@
|
||||
[self reset];
|
||||
}
|
||||
} else {
|
||||
if (! error)
|
||||
{
|
||||
if (! error) {
|
||||
error = [NSError errorWithDomain:NSCocoaErrorDomain
|
||||
code:NSFileReadNoSuchFileError
|
||||
userInfo:@{NSLocalizedDescriptionKey:ORKLocalizedString(@"ERROR_RECORDER_NO_DATA", nil)}];
|
||||
@@ -270,4 +250,3 @@
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -31,22 +31,18 @@
|
||||
|
||||
#import <ResearchKit/ORKRecorder.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ORKDataLogger;
|
||||
|
||||
@interface ORKRecorderConfiguration()
|
||||
|
||||
- (instancetype)ork_init;
|
||||
@interface ORKRecorderConfiguration ()
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@interface ORKRecorder ()
|
||||
|
||||
- (instancetype)ork_init;
|
||||
|
||||
@property (nonatomic, strong, nullable) ORKStep *step;
|
||||
|
||||
@property (nonatomic, strong, nullable) ORKRecorderConfiguration *configuration;
|
||||
|
||||
@@ -28,8 +28,10 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ORKRecorder.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ORKStep;
|
||||
@@ -41,18 +43,45 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
It is currently considered private, and is not used in any of the active tasks.
|
||||
*/
|
||||
ORK_CLASS_AVAILABLE
|
||||
@interface ORKTouchRecorderConfiguration: ORKRecorderConfiguration
|
||||
@interface ORKTouchRecorderConfiguration : ORKRecorderConfiguration
|
||||
|
||||
- (instancetype)init;
|
||||
/**
|
||||
Returns an initialized touch recorder configuration.
|
||||
|
||||
This method is the designated initializer.
|
||||
|
||||
@param identifier The unique identifier of the recorder configuration.
|
||||
|
||||
@return An initialized touch recorder configuration.
|
||||
*/
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
Returns a new touch recorder configuration initialized from data in the given unarchiver.
|
||||
|
||||
@param aDecoder Coder from which to initialize the touch recorder configuration.
|
||||
|
||||
@return A new touch recorder configuration.
|
||||
*/
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface ORKRecorder()
|
||||
|
||||
- (instancetype)initWithStep:(nullable ORKStep *)step
|
||||
outputDirectory:(nullable NSURL *)outputDirectory;
|
||||
@interface ORKRecorder ()
|
||||
|
||||
/**
|
||||
Returns an initialized recorder.
|
||||
|
||||
This method is the designated initializer.
|
||||
|
||||
@param identifier The unique identifier of the recorder.
|
||||
@param step The step for which this recorder is being created.
|
||||
@param outputDirectory The directory in which all output file data should be written (if producing `ORKFileResult` instances).
|
||||
|
||||
@return An initialized recorder.
|
||||
*/
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier step:(nullable ORKStep *)step outputDirectory:(nullable NSURL *)outputDirectory;
|
||||
|
||||
/**
|
||||
A preparation step to provide viewController and view before record starting.
|
||||
@@ -87,7 +116,27 @@ ORK_CLASS_AVAILABLE
|
||||
@end
|
||||
|
||||
|
||||
@interface ORKRecorderConfiguration()
|
||||
@interface ORKRecorderConfiguration ()
|
||||
|
||||
/**
|
||||
Returns an initialized recorder configuration.
|
||||
|
||||
This method is the designated initializer.
|
||||
|
||||
@param identifier The unique identifier of the recorder configuration.
|
||||
|
||||
@return An initialized recorder configuration.
|
||||
*/
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
Returns a new recorder configuration initialized from data in the given unarchiver.
|
||||
|
||||
@param aDecoder Coder from which to initialize the recorder configuration.
|
||||
|
||||
@return A new recorder configuration.
|
||||
*/
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
Returns the permission mask indicating the permissions required for this configuration.
|
||||
@@ -96,7 +145,6 @@ ORK_CLASS_AVAILABLE
|
||||
*/
|
||||
- (ORKPermissionMask)requestedPermissionMask;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
|
||||
@@ -36,7 +37,6 @@
|
||||
|
||||
A game consists of a subset of a permutation of the integers [0 .. gameSize-1],
|
||||
which represent the sequence of targets that should be tapped.
|
||||
|
||||
*/
|
||||
@interface ORKSpatialSpanGame : NSObject
|
||||
|
||||
@@ -64,12 +64,12 @@
|
||||
/**
|
||||
Enumerates the sequence, calling the block once for each element.
|
||||
|
||||
@param handler The block to be called for each element in the sequence. The `handler` block takes the following parameters:
|
||||
@param handler The block to be called for each element in the sequence. The `handler` block takes the following parameters:
|
||||
|
||||
`step` The step in the sequence. The step starts at 0 and increments by one on each call.
|
||||
`tileIndex` The index in [ 0 .. gameSize ] that corresponds to the step's element of the sequence.
|
||||
`isLastStep` A Boolean value that indicates if this is the last step in the sequence.
|
||||
`stop` A Boolean value that indicates if the enumeration should be terminated (pass `NO` to terminate the enumeration).
|
||||
`step` The step in the sequence. The step starts at 0 and increments by one on each call.
|
||||
`tileIndex` The index in [ 0 .. gameSize ] that corresponds to the step's element of the sequence.
|
||||
`isLastStep` A Boolean value that indicates if this is the last step in the sequence.
|
||||
`stop` A Boolean value that indicates if the enumeration should be terminated (pass `NO` to terminate the enumeration).
|
||||
*/
|
||||
|
||||
- (void)enumerateSequenceWithHandler:(void(^)(NSInteger step, NSInteger tileIndex, BOOL isLastStep, BOOL *stop))handler;
|
||||
@@ -77,5 +77,4 @@
|
||||
/// Returns the value of the specified step in the sequence.
|
||||
- (NSInteger)tileIndexForStep:(NSInteger)step;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -28,10 +28,11 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKSpatialSpanGame.h"
|
||||
|
||||
@implementation ORKSpatialSpanGame
|
||||
{
|
||||
|
||||
@implementation ORKSpatialSpanGame {
|
||||
NSInteger *_sequence;
|
||||
}
|
||||
|
||||
@@ -81,13 +82,10 @@
|
||||
if (_sequence == NULL) {
|
||||
self = nil;
|
||||
}
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Step parameter is the step in the sequence; tileIndex is the value of that step of the sequence.
|
||||
- (void)enumerateSequenceWithHandler:(void(^)(NSInteger step, NSInteger tileIndex, BOOL isLastStep, BOOL *stop))handler {
|
||||
BOOL stop = NO;
|
||||
|
||||
@@ -28,9 +28,11 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "ORKSpatialSpanTargetView.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ORKSpatialSpanGame;
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKSpatialSpanGameState.h"
|
||||
#import "ORKSpatialSpanGame.h"
|
||||
|
||||
@@ -37,7 +38,6 @@
|
||||
ORKSpatialSpanTargetState *_states;
|
||||
}
|
||||
|
||||
|
||||
- (instancetype)initWithGame:(ORKSpatialSpanGame *)game {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
@@ -59,7 +59,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)reset {
|
||||
const NSInteger gameSize = [_game gameSize];
|
||||
for (NSInteger tileIndex = 0; tileIndex < gameSize; tileIndex++) {
|
||||
@@ -98,7 +97,6 @@
|
||||
return correct ? ORKSpatialSpanResultCorrect : ORKSpatialSpanResultIncorrect;
|
||||
}
|
||||
|
||||
|
||||
- (NSInteger)currentStepIndex {
|
||||
return [_plays count];
|
||||
}
|
||||
|
||||
@@ -28,9 +28,11 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKCustomStepView_Internal.h"
|
||||
#import "ORKSpatialSpanTargetView.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef struct {
|
||||
@@ -46,6 +48,7 @@ typedef struct {
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface ORKSpatialSpanMemoryGameView : UIView <ORKSpatialSpanTargetViewDelegate>
|
||||
|
||||
@property (nonatomic, weak, nullable) id<ORKSpatialSpanMemoryGameViewDelegate> delegate;
|
||||
@@ -68,7 +71,7 @@ typedef struct {
|
||||
|
||||
@interface ORKSpatialSpanMemoryContentView : ORKActiveStepCustomView
|
||||
|
||||
@property (nonatomic, readonly, strong) ORKSpatialSpanMemoryGameView *gameView;
|
||||
@property (nonatomic, strong, readonly) ORKSpatialSpanMemoryGameView *gameView;
|
||||
|
||||
@property (nonatomic, assign, getter=isFooterHidden) BOOL footerHidden;
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKSpatialSpanMemoryContentView.h"
|
||||
#import "ORKSpatialSpanTargetView.h"
|
||||
#import "ORKActiveStepQuantityView.h"
|
||||
@@ -122,20 +123,20 @@
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
|
||||
CGRect bds = [self bounds];
|
||||
CGFloat gridItemEdgeLength = ORKFloorToViewScale(MIN(bds.size.width / _gridSize.width, bds.size.height / _gridSize.height), self);
|
||||
CGRect bounds = [self bounds];
|
||||
CGFloat gridItemEdgeLength = ORKFloorToViewScale(MIN(bounds.size.width / _gridSize.width, bounds.size.height / _gridSize.height), self);
|
||||
|
||||
gridItemEdgeLength = MIN(gridItemEdgeLength, 114);
|
||||
CGSize gridItemSize = (CGSize){gridItemEdgeLength, gridItemEdgeLength};
|
||||
|
||||
CGPoint centeringOffset = CGPointZero;
|
||||
centeringOffset.x = 0.5*(bds.size.width - (gridItemSize.width * _gridSize.width));
|
||||
centeringOffset.y = 0.5*(bds.size.height - (gridItemSize.height * _gridSize.height));
|
||||
centeringOffset.x = 0.5*(bounds.size.width - (gridItemSize.width * _gridSize.width));
|
||||
centeringOffset.y = 0.5*(bounds.size.height - (gridItemSize.height * _gridSize.height));
|
||||
|
||||
NSInteger tileIndex = 0;
|
||||
for (NSInteger x = 0; x < _gridSize.width; x++) {
|
||||
for (NSInteger y = 0; y < _gridSize.height; y++) {
|
||||
ORKSpatialSpanTargetView *targetView = [_tileViews objectAtIndex:tileIndex];
|
||||
ORKSpatialSpanTargetView *targetView = _tileViews[tileIndex];
|
||||
|
||||
CGPoint origin = (CGPoint){.x = ORKFloorToViewScale(centeringOffset.x + x * gridItemSize.width, self),
|
||||
.y = ORKFloorToViewScale(centeringOffset.y + y * gridItemSize.height, self)};
|
||||
@@ -147,12 +148,12 @@
|
||||
}
|
||||
|
||||
- (void)setState:(ORKSpatialSpanTargetState)state forTileIndex:(NSInteger)tileIndex animated:(BOOL)animated {
|
||||
ORKSpatialSpanTargetView *view = [_tileViews objectAtIndex:tileIndex];
|
||||
ORKSpatialSpanTargetView *view = _tileViews[tileIndex];
|
||||
[view setState:state animated:animated];
|
||||
}
|
||||
|
||||
- (ORKSpatialSpanTargetState)stateForTileIndex:(NSInteger)tileIndex {
|
||||
return [(ORKSpatialSpanTargetView *)[_tileViews objectAtIndex:tileIndex] state];
|
||||
return [(ORKSpatialSpanTargetView *)_tileViews[tileIndex] state];
|
||||
}
|
||||
|
||||
#pragma mark Accessibility
|
||||
@@ -163,6 +164,7 @@
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ORKSpatialSpanMemoryContentView {
|
||||
ORKQuantityPairView *_quantityPairView;
|
||||
ORKNavigationContainerView *_continueView;
|
||||
@@ -219,7 +221,6 @@
|
||||
[self countView].backgroundColor = [[UIColor purpleColor] colorWithAlphaComponent:0.2];
|
||||
#endif
|
||||
|
||||
|
||||
[self setNeedsUpdateConstraints];
|
||||
}
|
||||
return self;
|
||||
@@ -259,7 +260,7 @@
|
||||
|
||||
|
||||
- (void)updateMargins {
|
||||
self.layoutMargins = (UIEdgeInsets){.left=ORKStandardMarginForView(self),.right=ORKStandardMarginForView(self)};
|
||||
self.layoutMargins = (UIEdgeInsets){.left=ORKStandardHorizMarginForView(self), .right=ORKStandardHorizMarginForView(self)};
|
||||
_quantityPairView.layoutMargins = self.layoutMargins;
|
||||
}
|
||||
|
||||
@@ -277,22 +278,57 @@
|
||||
|
||||
NSDictionary *views = NSDictionaryOfVariableBindings(_gameView, _quantityPairView, _continueView);
|
||||
|
||||
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(>=0)-[_gameView][_quantityPairView]|" options:NSLayoutFormatAlignAllCenterX metrics:nil views:views]];
|
||||
NSLayoutConstraint *c1 = [NSLayoutConstraint constraintWithItem:_gameView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:1000];
|
||||
c1.priority = UILayoutPriorityDefaultLow-1;
|
||||
[constraints addObject:c1];
|
||||
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(>=0)-[_gameView][_quantityPairView]|"
|
||||
options:NSLayoutFormatAlignAllCenterX
|
||||
metrics:nil
|
||||
views:views]];
|
||||
NSLayoutConstraint *constraint1 = [NSLayoutConstraint constraintWithItem:_gameView
|
||||
attribute:NSLayoutAttributeHeight
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:nil
|
||||
attribute:NSLayoutAttributeNotAnAttribute
|
||||
multiplier:1.0
|
||||
constant:1000.0];
|
||||
constraint1.priority = UILayoutPriorityDefaultLow-1;
|
||||
[constraints addObject:constraint1];
|
||||
|
||||
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_gameView]-|" options:(NSLayoutFormatOptions)0 metrics:nil views:views]];
|
||||
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_gameView]-|"
|
||||
options:(NSLayoutFormatOptions)0
|
||||
metrics:nil
|
||||
views:views]];
|
||||
|
||||
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_quantityPairView]|" options:(NSLayoutFormatOptions)0 metrics:nil views:views]];
|
||||
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_continueView]|" options:(NSLayoutFormatOptions)0 metrics:nil views:views]];
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_continueView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:_quantityPairView attribute:NSLayoutAttributeBottom multiplier:1 constant:0]];
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_continueView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:_quantityPairView attribute:NSLayoutAttributeTop multiplier:1 constant:0]];
|
||||
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_quantityPairView]|"
|
||||
options:(NSLayoutFormatOptions)0
|
||||
metrics:nil
|
||||
views:views]];
|
||||
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_continueView]|"
|
||||
options:(NSLayoutFormatOptions)0
|
||||
metrics:nil
|
||||
views:views]];
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_continueView
|
||||
attribute:NSLayoutAttributeBottom
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:_quantityPairView
|
||||
attribute:NSLayoutAttributeBottom
|
||||
multiplier:1.0
|
||||
constant:0.0]];
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_continueView
|
||||
attribute:NSLayoutAttributeTop
|
||||
relatedBy:NSLayoutRelationGreaterThanOrEqual
|
||||
toItem:_quantityPairView
|
||||
attribute:NSLayoutAttributeTop
|
||||
multiplier:1.0
|
||||
constant:0.0]];
|
||||
|
||||
NSLayoutConstraint *maxWidthConstraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:10000];
|
||||
NSLayoutConstraint *maxWidthConstraint = [NSLayoutConstraint constraintWithItem:self
|
||||
attribute:NSLayoutAttributeWidth
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:nil
|
||||
attribute:NSLayoutAttributeNotAnAttribute
|
||||
multiplier:1.0
|
||||
constant:ORKScreenMetricMaxDimension];
|
||||
maxWidthConstraint.priority = UILayoutPriorityRequired-1;
|
||||
[constraints addObject:maxWidthConstraint];
|
||||
|
||||
|
||||
[NSLayoutConstraint activateConstraints:constraints];
|
||||
_constraints = constraints;
|
||||
@@ -300,6 +336,4 @@
|
||||
[super updateConstraints];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -28,8 +28,10 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit_Private.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
ORK_CLASS_AVAILABLE
|
||||
|
||||
@@ -28,11 +28,13 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKSpatialSpanMemoryStep.h"
|
||||
#import "ORKSpatialSpanMemoryStepViewController.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import "ORKStep_Private.h"
|
||||
|
||||
|
||||
@implementation ORKSpatialSpanMemoryStep
|
||||
|
||||
+ (Class)stepViewControllerClass {
|
||||
@@ -41,13 +43,14 @@
|
||||
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier {
|
||||
self = [super initWithIdentifier:identifier];
|
||||
self.shouldStartTimerAutomatically = YES;
|
||||
self.shouldContinueOnFinish = YES;
|
||||
if (self) {
|
||||
self.shouldStartTimerAutomatically = YES;
|
||||
self.shouldContinueOnFinish = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
ORKSpatialSpanMemoryStep *step = [super copyWithZone:zone];
|
||||
step.initialSpan = self.initialSpan;
|
||||
step.minimumSpan = self.minimumSpan;
|
||||
@@ -62,7 +65,6 @@
|
||||
}
|
||||
|
||||
- (void)validateParameters {
|
||||
|
||||
[super validateParameters];
|
||||
|
||||
NSInteger const ORKSpatialSpanMemoryTaskMinimumInitialSpan = 2;
|
||||
@@ -72,53 +74,45 @@
|
||||
NSInteger const ORKSpatialSpanMemoryTaskMinimumMaxTests = 1;
|
||||
NSInteger const ORKSpatialSpanMemoryTaskMinimumMaxConsecutiveFailures = 1;
|
||||
|
||||
if ( self.initialSpan < ORKSpatialSpanMemoryTaskMinimumInitialSpan)
|
||||
{
|
||||
if ( self.initialSpan < ORKSpatialSpanMemoryTaskMinimumInitialSpan) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException
|
||||
reason:[NSString stringWithFormat:@"initialSpan can not be less than %@.", @(ORKSpatialSpanMemoryTaskMinimumInitialSpan)]
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
if ( self.minimumSpan > self.initialSpan)
|
||||
{
|
||||
if ( self.minimumSpan > self.initialSpan) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"initialSpan can not be less than minimumSpan." userInfo:nil];
|
||||
}
|
||||
|
||||
if ( self.initialSpan > self.maximumSpan)
|
||||
{
|
||||
if ( self.initialSpan > self.maximumSpan) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"maximumSpan can not be less than initialSpan." userInfo:nil];
|
||||
}
|
||||
|
||||
if ( self.maximumSpan > ORKSpatialSpanMemoryTaskMaximumSpan)
|
||||
{
|
||||
if ( self.maximumSpan > ORKSpatialSpanMemoryTaskMaximumSpan) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException
|
||||
reason:[NSString stringWithFormat:@"maximumSpan can not be more than %@.", @(ORKSpatialSpanMemoryTaskMaximumSpan)]
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
if (self.playSpeed < ORKSpatialSpanMemoryTaskMinimumPlaySpeed)
|
||||
{
|
||||
if (self.playSpeed < ORKSpatialSpanMemoryTaskMinimumPlaySpeed) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException
|
||||
reason:[NSString stringWithFormat:@"playSpeed can not be shorter than %@ seconds.", @(ORKSpatialSpanMemoryTaskMinimumPlaySpeed)]
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
if (self.playSpeed > ORKSpatialSpanMemoryTaskMaximumPlaySpeed)
|
||||
{
|
||||
if (self.playSpeed > ORKSpatialSpanMemoryTaskMaximumPlaySpeed) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException
|
||||
reason:[NSString stringWithFormat:@"playSpeed can not be longer than %@ seconds.", @(ORKSpatialSpanMemoryTaskMaximumPlaySpeed)]
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
if (self.maxTests < ORKSpatialSpanMemoryTaskMinimumMaxTests)
|
||||
{
|
||||
if (self.maxTests < ORKSpatialSpanMemoryTaskMinimumMaxTests) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException
|
||||
reason:[NSString stringWithFormat:@"maxTests can not be less than %@.", @(ORKSpatialSpanMemoryTaskMinimumMaxTests)]
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
if (self.maxConsecutiveFailures < ORKSpatialSpanMemoryTaskMinimumMaxConsecutiveFailures)
|
||||
{
|
||||
if (self.maxConsecutiveFailures < ORKSpatialSpanMemoryTaskMinimumMaxConsecutiveFailures) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException
|
||||
reason:[NSString stringWithFormat:@"maxConsecutiveFailures can not be less than %@.", @(ORKSpatialSpanMemoryTaskMinimumMaxConsecutiveFailures)]
|
||||
userInfo:nil];
|
||||
@@ -129,11 +123,9 @@
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self)
|
||||
{
|
||||
if (self) {
|
||||
ORK_DECODE_INTEGER(aDecoder, initialSpan);
|
||||
ORK_DECODE_INTEGER(aDecoder, minimumSpan);
|
||||
ORK_DECODE_INTEGER(aDecoder, maximumSpan);
|
||||
@@ -147,8 +139,7 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder
|
||||
{
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[super encodeWithCoder:aCoder];
|
||||
ORK_ENCODE_INTEGER(aCoder, initialSpan);
|
||||
ORK_ENCODE_INTEGER(aCoder, minimumSpan);
|
||||
@@ -161,12 +152,10 @@
|
||||
ORK_ENCODE_OBJ(aCoder, customTargetPluralName);
|
||||
}
|
||||
|
||||
+ (BOOL)supportsSecureCoding
|
||||
{
|
||||
+ (BOOL)supportsSecureCoding {
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
BOOL isParentSame = [super isEqual:object];
|
||||
|
||||
@@ -179,14 +168,11 @@
|
||||
(self.maxTests == castObject.maxTests) &&
|
||||
(self.maxConsecutiveFailures == castObject.maxConsecutiveFailures) &&
|
||||
(ORKEqualObjects(self.customTargetPluralName, castObject.customTargetPluralName)) &&
|
||||
(self.requireReversal == castObject.requireReversal)) ;
|
||||
(self.requireReversal == castObject.requireReversal));
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (BOOL)allowsBackNavigation {
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -28,8 +28,10 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit_Private.h>
|
||||
|
||||
|
||||
/**
|
||||
View controller corresponding to `ORKSpatialSpanMemoryStep`.
|
||||
*/
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKSpatialSpanMemoryStepViewController.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import "ORKActiveStep_Internal.h"
|
||||
@@ -45,6 +46,7 @@
|
||||
#import "ORKSpatialSpanMemoryStep.h"
|
||||
#import "ORKActiveStepView.h"
|
||||
|
||||
|
||||
static const NSTimeInterval kMemoryGameActivityTimeout = 20;
|
||||
|
||||
typedef NS_ENUM(NSInteger, ORKSpatialSpanStepState) {
|
||||
@@ -83,6 +85,7 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ORKState;
|
||||
|
||||
+ (ORKState *)stateWithState:(NSInteger)state entryHandler:(_ORKStateHandler)entryHandler exitHandler:(_ORKStateHandler)exitHandler context:(id)context {
|
||||
@@ -94,15 +97,15 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
return s;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface ORKSpatialSpanMemoryStepViewController () <ORKSpatialSpanMemoryGameViewDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@interface ORKSpatialSpanMemoryStepViewController() <ORKSpatialSpanMemoryGameViewDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation ORKSpatialSpanMemoryStepViewController
|
||||
{
|
||||
@implementation ORKSpatialSpanMemoryStepViewController {
|
||||
ORKSpatialSpanMemoryContentView *_contentView;
|
||||
ORKState *_state;
|
||||
NSDictionary *_states;
|
||||
@@ -126,7 +129,6 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
|
||||
NSTimer *_playbackTimer;
|
||||
NSTimer *_activityTimer;
|
||||
|
||||
}
|
||||
|
||||
- (ORKSpatialSpanMemoryStep *)spatialSpanStep {
|
||||
@@ -134,7 +136,6 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
}
|
||||
|
||||
- (instancetype)initWithStep:(ORKStep *)step {
|
||||
|
||||
self = [super initWithStep:step];
|
||||
if (self) {
|
||||
self.suspendIfInactive = YES;
|
||||
@@ -144,7 +145,6 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
|
||||
#pragma mark Overrides
|
||||
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
@@ -154,7 +154,7 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
_contentView.gameView.delegate = self;
|
||||
self.activeStepView.activeCustomView = _contentView;
|
||||
self.activeStepView.stepViewFillsAvailableSpace = NO;
|
||||
self.activeStepView.minimumStepHeaderHeight = ORKGetMetricForScreenType(ORKScreenMetricMinimumStepHeaderHeightForMemoryGame, ORKGetScreenTypeForWindow(self.view.window));
|
||||
self.activeStepView.minimumStepHeaderHeight = ORKGetMetricForWindow(ORKScreenMetricMinimumStepHeaderHeightForMemoryGame, self.view.window);
|
||||
|
||||
[self resetUI];
|
||||
|
||||
@@ -162,7 +162,6 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
[self.activeStepView addGestureRecognizer:tapGestureRecognizer];
|
||||
}
|
||||
|
||||
|
||||
- (void)stepDidChange {
|
||||
[super stepDidChange];
|
||||
|
||||
@@ -179,6 +178,7 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
|
||||
[self transitionToState:ORKSpatialSpanStepStatePlayback];
|
||||
}
|
||||
|
||||
- (void)suspend {
|
||||
[super suspend];
|
||||
switch (_state.state) {
|
||||
@@ -190,26 +190,27 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resume {
|
||||
[super resume];
|
||||
}
|
||||
|
||||
- (void)finish {
|
||||
[super finish];
|
||||
[self transitionToState:ORKSpatialSpanStepStateStopped];
|
||||
}
|
||||
|
||||
|
||||
- (ORKStepResult *)result {
|
||||
ORKStepResult *sResult = [super result];
|
||||
ORKStepResult *stepResult = [super result];
|
||||
|
||||
// "Now" is the end time of the result, which is either actually now,
|
||||
// or the last time we were in the responder chain.
|
||||
NSDate *now = sResult.endDate;
|
||||
NSDate *now = stepResult.endDate;
|
||||
|
||||
NSMutableArray *results = [NSMutableArray arrayWithArray:sResult.results];
|
||||
NSMutableArray *results = [NSMutableArray arrayWithArray:stepResult.results];
|
||||
|
||||
ORKSpatialSpanMemoryResult *memoryResult = [[ORKSpatialSpanMemoryResult alloc] initWithIdentifier:(NSString *__nonnull)self.step.identifier];
|
||||
memoryResult.startDate = sResult.startDate;
|
||||
ORKSpatialSpanMemoryResult *memoryResult = [[ORKSpatialSpanMemoryResult alloc] initWithIdentifier:self.step.identifier];
|
||||
memoryResult.startDate = stepResult.startDate;
|
||||
memoryResult.endDate = now;
|
||||
|
||||
NSMutableArray *records = [NSMutableArray new];
|
||||
@@ -224,8 +225,7 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
|
||||
score += record.score;
|
||||
|
||||
if (record.gameStatus != ORKSpatialSpanMemoryGameStatusSuccess)
|
||||
{
|
||||
if (record.gameStatus != ORKSpatialSpanMemoryGameStatusSuccess) {
|
||||
numberOfFailures++;
|
||||
}
|
||||
}
|
||||
@@ -237,9 +237,9 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
memoryResult.gameRecords = [records copy];
|
||||
|
||||
[results addObject:memoryResult];
|
||||
sResult.results = [results copy];
|
||||
stepResult.results = [results copy];
|
||||
|
||||
return sResult;
|
||||
return stepResult;
|
||||
}
|
||||
|
||||
#pragma mark UpdateGameRecord
|
||||
@@ -249,7 +249,6 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
}
|
||||
|
||||
- (void)createGameRecord {
|
||||
|
||||
if (_gameRecords == nil) {
|
||||
_gameRecords = [NSMutableArray new];
|
||||
}
|
||||
@@ -268,8 +267,7 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
_lastRoundScore = _score;
|
||||
}
|
||||
|
||||
- (void)updateGameRecordTargetRects
|
||||
{
|
||||
- (void)updateGameRecordTargetRects {
|
||||
ORKSpatialSpanMemoryGameRecord *record = [self currentGameRecord];
|
||||
NSArray *tileViews = _contentView.gameView.tileViews;
|
||||
NSMutableArray *targetRects = [NSMutableArray new];
|
||||
@@ -281,21 +279,18 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
NSAssert(tileViews.count == 0 || tileViews.count == record.gameSize, nil);
|
||||
}
|
||||
|
||||
- (void)updateGameRecordOnStartingGamePlay
|
||||
{
|
||||
- (void)updateGameRecordOnStartingGamePlay {
|
||||
_gameStartTime = CACurrentMediaTime();
|
||||
}
|
||||
|
||||
- (void)handleUserTap:(UITapGestureRecognizer *)tapRecognizer
|
||||
{
|
||||
- (void)handleUserTap:(UITapGestureRecognizer *)tapRecognizer {
|
||||
if (_state.state != ORKSpatialSpanStepStateGameplay) {
|
||||
return;
|
||||
}
|
||||
[self updateGameRecordOnTouch:-1 location:[tapRecognizer locationInView: self.view]];
|
||||
}
|
||||
|
||||
- (void)updateGameRecordOnTouch:(NSInteger)targetIndex location:(CGPoint)location
|
||||
{
|
||||
- (void)updateGameRecordOnTouch:(NSInteger)targetIndex location:(CGPoint)location {
|
||||
ORKSpatialSpanMemoryGameTouchSample *sample = [ORKSpatialSpanMemoryGameTouchSample new];
|
||||
|
||||
sample.timestamp = CACurrentMediaTime() - _gameStartTime;
|
||||
@@ -321,35 +316,28 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
[sampleArray addObject:sample];
|
||||
|
||||
record.touchSamples = [sampleArray copy];
|
||||
|
||||
}
|
||||
|
||||
- (void)updateGameRecordOnSuccess
|
||||
{
|
||||
- (void)updateGameRecordOnSuccess {
|
||||
[self currentGameRecord].gameStatus = ORKSpatialSpanMemoryGameStatusSuccess;
|
||||
}
|
||||
|
||||
- (void)updateGameRecordOnFailure
|
||||
{
|
||||
- (void)updateGameRecordOnFailure {
|
||||
[self currentGameRecord].gameStatus = ORKSpatialSpanMemoryGameStatusFailure;
|
||||
}
|
||||
|
||||
- (void)updateGameRecordOnTimeout
|
||||
{
|
||||
- (void)updateGameRecordOnTimeout {
|
||||
[self currentGameRecord].gameStatus = ORKSpatialSpanMemoryGameStatusTimeout;
|
||||
}
|
||||
|
||||
- (void)updateGameRecordScore
|
||||
{
|
||||
- (void)updateGameRecordScore {
|
||||
[self currentGameRecord].score = _score - _lastRoundScore;
|
||||
}
|
||||
|
||||
- (void)updateGameRecordOnPause
|
||||
{
|
||||
- (void)updateGameRecordOnPause {
|
||||
[self currentGameRecord].gameStatus = ORKSpatialSpanMemoryGameStatusUnknown;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark ORKSpatialSpanStepStateInitial
|
||||
|
||||
- (ORKGridSize)gridSizeForSpan:(NSInteger)span {
|
||||
@@ -397,10 +385,8 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
[self createGameRecord];
|
||||
|
||||
[self resetUI];
|
||||
|
||||
}
|
||||
|
||||
|
||||
#pragma mark ORKSpatialSpanStepStatePlayback
|
||||
|
||||
- (void)applyTargetState:(ORKSpatialSpanTargetState)targetState toSequenceIndex:(NSInteger)index duration:(NSTimeInterval)duration {
|
||||
@@ -454,7 +440,6 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
|
||||
[self.activeStepView updateTitle:title text:nil];
|
||||
|
||||
|
||||
[_contentView.gameView resetTilesAnimated:NO];
|
||||
|
||||
_playbackTimer = [NSTimer scheduledTimerWithTimeInterval:step.playSpeed target:self selector:@selector(playbackNextItem) userInfo:nil repeats:YES];
|
||||
@@ -521,7 +506,6 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
_activityTimer = nil;
|
||||
}
|
||||
|
||||
|
||||
- (void)gameView:(ORKSpatialSpanMemoryGameView *)gameView didTapTileWithIndex:(NSInteger)tileIndex recognizer:(UITapGestureRecognizer *)recognizer {
|
||||
if (_state.state != ORKSpatialSpanStepStateGameplay) {
|
||||
return;
|
||||
@@ -533,6 +517,7 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
switch (result) {
|
||||
case ORKSpatialSpanResultIgnore:
|
||||
break;
|
||||
|
||||
case ORKSpatialSpanResultCorrect:
|
||||
[gameView setState:ORKSpatialSpanTargetStateCorrect forTileIndex:tileIndex animated:YES];
|
||||
NSInteger stepIndex = [_currentGameState currentStepIndex];
|
||||
@@ -544,9 +529,8 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
if ([_currentGameState isComplete]) {
|
||||
[self transitionToState:ORKSpatialSpanStepStateSuccess];
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
|
||||
case ORKSpatialSpanResultIncorrect:
|
||||
[gameView setState:ORKSpatialSpanTargetStateIncorrect forTileIndex:tileIndex animated:YES];
|
||||
[self transitionToState:ORKSpatialSpanStepStateFailed];
|
||||
@@ -557,7 +541,6 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
#pragma mark ORKSpatialSpanStepStateSuccess
|
||||
|
||||
- (void)updateGameCountersForSuccess:(BOOL)success {
|
||||
|
||||
ORKSpatialSpanMemoryStep *step = [self spatialSpanStep];
|
||||
if (success) {
|
||||
NSInteger sequenceLength = [_currentGameState.game sequenceLength];
|
||||
@@ -573,7 +556,6 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
}
|
||||
|
||||
- (void)continueAction {
|
||||
|
||||
ORKSpatialSpanMemoryStep *step = [self spatialSpanStep];
|
||||
if (_gamesCounter < step.maxTests && _consecutiveGamesFailed < step.maxConsecutiveFailures) {
|
||||
// Generate a new game
|
||||
@@ -585,7 +567,6 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
}
|
||||
|
||||
- (void)showSuccess {
|
||||
|
||||
[self updateGameRecordOnSuccess];
|
||||
|
||||
[self updateGameCountersForSuccess:YES];
|
||||
@@ -593,7 +574,6 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
[self.activeStepView updateTitle:ORKLocalizedString(@"MEMORY_GAME_COMPLETE_TITLE", nil) text:ORKLocalizedString(@"MEMORY_GAME_COMPLETE_MESSAGE", nil)];
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.75 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
@@ -603,16 +583,13 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
|
||||
#pragma mark ORKSpatialSpanStepStateFailed
|
||||
|
||||
|
||||
- (void)tryAgainAction {
|
||||
// Restart with a new, shorter game
|
||||
[self transitionToState:ORKSpatialSpanStepStateRestart];
|
||||
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil);
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)finishIfCompletedGames {
|
||||
|
||||
ORKSpatialSpanMemoryStep *step = [self spatialSpanStep];
|
||||
if (_consecutiveGamesFailed >= step.maxConsecutiveFailures || _gamesCounter >= step.maxTests) {
|
||||
[self transitionToState:ORKSpatialSpanStepStateComplete];
|
||||
@@ -622,7 +599,6 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
}
|
||||
|
||||
- (void)showFailed {
|
||||
|
||||
[self updateGameRecordOnFailure];
|
||||
|
||||
[self updateGameCountersForSuccess:NO];
|
||||
@@ -631,14 +607,12 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
}
|
||||
[self.activeStepView updateTitle:ORKLocalizedString(@"MEMORY_GAME_FAILED_TITLE", nil) text:ORKLocalizedString(@"MEMORY_GAME_FAILED_MESSAGE", nil)];
|
||||
|
||||
|
||||
_contentView.buttonItem = [[UIBarButtonItem alloc] initWithTitle:ORKLocalizedString(@"BUTTON_NEXT", nil) style:UIBarButtonItemStylePlain target:self action:@selector(tryAgainAction)];
|
||||
}
|
||||
|
||||
#pragma mark ORKSpatialSpanStepStateTimeout
|
||||
|
||||
- (void)showTimeout {
|
||||
|
||||
[self updateGameRecordOnTimeout];
|
||||
|
||||
[self updateGameCountersForSuccess:NO];
|
||||
@@ -686,37 +660,36 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
_contentView.buttonItem = [[UIBarButtonItem alloc] initWithTitle:ORKLocalizedString(@"BUTTON_NEXT", nil) style:UIBarButtonItemStylePlain target:self action:@selector(continueAction)];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark State machine
|
||||
|
||||
- (void)initializeStates {
|
||||
NSMutableDictionary *states = [NSMutableDictionary dictionary];
|
||||
|
||||
states[@(ORKSpatialSpanStepStateInitial)] = [ORKState stateWithState:ORKSpatialSpanStepStateInitial
|
||||
entryHandler:^(ORKState *from, ORKState *to, ORKSpatialSpanMemoryStepViewController *this) {
|
||||
[this resetGameAndUI];
|
||||
}
|
||||
exitHandler:nil context:self];
|
||||
entryHandler:^(ORKState *from, ORKState *to, ORKSpatialSpanMemoryStepViewController *this) {
|
||||
[this resetGameAndUI];
|
||||
}
|
||||
exitHandler:nil context:self];
|
||||
|
||||
states[@(ORKSpatialSpanStepStatePlayback)] = [ORKState stateWithState:ORKSpatialSpanStepStatePlayback
|
||||
entryHandler:^(ORKState *from, ORKState *to, ORKSpatialSpanMemoryStepViewController *this) {
|
||||
[this startPlayback];
|
||||
}
|
||||
exitHandler:^(ORKState *from, ORKState *to, ORKSpatialSpanMemoryStepViewController *this) {
|
||||
[this finishPlayback];
|
||||
} context:self];
|
||||
entryHandler:^(ORKState *from, ORKState *to, ORKSpatialSpanMemoryStepViewController *this) {
|
||||
[this startPlayback];
|
||||
}
|
||||
exitHandler:^(ORKState *from, ORKState *to, ORKSpatialSpanMemoryStepViewController *this) {
|
||||
[this finishPlayback];
|
||||
} context:self];
|
||||
|
||||
states[@(ORKSpatialSpanStepStateGameplay)] = [ORKState stateWithState:ORKSpatialSpanStepStateGameplay
|
||||
entryHandler:^(ORKState *from, ORKState *to, ORKSpatialSpanMemoryStepViewController *this) {
|
||||
[this startGameplay];
|
||||
}
|
||||
exitHandler:^(ORKState *from, ORKState *to, ORKSpatialSpanMemoryStepViewController *this) {
|
||||
[this finishGameplay];
|
||||
} context:self];
|
||||
entryHandler:^(ORKState *from, ORKState *to, ORKSpatialSpanMemoryStepViewController *this) {
|
||||
[this startGameplay];
|
||||
}
|
||||
exitHandler:^(ORKState *from, ORKState *to, ORKSpatialSpanMemoryStepViewController *this) {
|
||||
[this finishGameplay];
|
||||
} context:self];
|
||||
|
||||
states[@(ORKSpatialSpanStepStateSuccess)] = [ORKState stateWithState:ORKSpatialSpanStepStateSuccess
|
||||
entryHandler:^(ORKState *from, ORKState *to, ORKSpatialSpanMemoryStepViewController *this) {
|
||||
[this showSuccess];
|
||||
entryHandler:^(ORKState *from, ORKState *to, ORKSpatialSpanMemoryStepViewController *this) {
|
||||
[this showSuccess];
|
||||
}
|
||||
exitHandler:nil context:self];
|
||||
|
||||
@@ -727,31 +700,31 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
exitHandler:nil context:self];
|
||||
|
||||
states[@(ORKSpatialSpanStepStateTimeout)] = [ORKState stateWithState:ORKSpatialSpanStepStateTimeout
|
||||
entryHandler:^(ORKState *from, ORKState *to, ORKSpatialSpanMemoryStepViewController *this) {
|
||||
[this showTimeout];
|
||||
}
|
||||
exitHandler:nil context:self];
|
||||
entryHandler:^(ORKState *from, ORKState *to, ORKSpatialSpanMemoryStepViewController *this) {
|
||||
[this showTimeout];
|
||||
}
|
||||
exitHandler:nil context:self];
|
||||
|
||||
states[@(ORKSpatialSpanStepStateRestart)] = [ORKState stateWithState:ORKSpatialSpanStepStateRestart
|
||||
entryHandler:^(ORKState *from, ORKState *to, ORKSpatialSpanMemoryStepViewController *this) {
|
||||
[this doRestart];
|
||||
}
|
||||
exitHandler:nil context:self];
|
||||
entryHandler:^(ORKState *from, ORKState *to, ORKSpatialSpanMemoryStepViewController *this) {
|
||||
[this doRestart];
|
||||
}
|
||||
exitHandler:nil context:self];
|
||||
|
||||
states[@(ORKSpatialSpanStepStateComplete)] = [ORKState stateWithState:ORKSpatialSpanStepStateComplete
|
||||
entryHandler:^(ORKState *from, ORKState *to, ORKSpatialSpanMemoryStepViewController *this) {
|
||||
[this showComplete];
|
||||
} exitHandler:nil context:self];
|
||||
entryHandler:^(ORKState *from, ORKState *to, ORKSpatialSpanMemoryStepViewController *this) {
|
||||
[this showComplete];
|
||||
} exitHandler:nil context:self];
|
||||
|
||||
states[@(ORKSpatialSpanStepStateStopped)] = [ORKState stateWithState:ORKSpatialSpanStepStateStopped
|
||||
entryHandler:nil
|
||||
exitHandler:nil
|
||||
context:self];
|
||||
entryHandler:nil
|
||||
exitHandler:nil
|
||||
context:self];
|
||||
|
||||
states[@(ORKSpatialSpanStepStatePaused)] = [ORKState stateWithState:ORKSpatialSpanStepStatePaused
|
||||
entryHandler:^(ORKState *from, ORKState *to, ORKSpatialSpanMemoryStepViewController *this) {
|
||||
[this showPausedFromState:from];
|
||||
} exitHandler:nil context:self];
|
||||
entryHandler:^(ORKState *from, ORKState *to, ORKSpatialSpanMemoryStepViewController *this) {
|
||||
[this showPausedFromState:from];
|
||||
} exitHandler:nil context:self];
|
||||
|
||||
_states = states;
|
||||
|
||||
@@ -771,6 +744,4 @@ typedef void (^_ORKStateHandler)(ORKState *fromState, ORKState *_toState, id con
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -28,9 +28,11 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSInteger, ORKSpatialSpanTargetState) {
|
||||
@@ -48,6 +50,7 @@ typedef NS_ENUM(NSInteger, ORKSpatialSpanTargetState) {
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface ORKSpatialSpanTargetView : UIView
|
||||
|
||||
@property (nonatomic, weak, nullable) id<ORKSpatialSpanTargetViewDelegate> delegate;
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKSpatialSpanTargetView.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import "ORKSkin.h"
|
||||
@@ -35,10 +36,10 @@
|
||||
#import "ORKAccessibility.h"
|
||||
#import "ORKDefines_Private.h"
|
||||
|
||||
|
||||
static const UIEdgeInsets _ORKFlowerMargins = (UIEdgeInsets){12,12,12,12};
|
||||
static const CGSize ORKFlowerBezierPathSize = (CGSize){90,90};
|
||||
static UIBezierPath *ORKFlowerBezierPath() {
|
||||
|
||||
UIBezierPath *bezierPath = UIBezierPath.bezierPath;
|
||||
[bezierPath moveToPoint: CGPointMake(58.8, 45)];
|
||||
[bezierPath addCurveToPoint: CGPointMake(51.9, 33.2) controlPoint1: CGPointMake(107.8, 41.8) controlPoint2: CGPointMake(79.3, -7.2)];
|
||||
@@ -61,7 +62,6 @@ static UIBezierPath *ORKFlowerBezierPath() {
|
||||
|
||||
static const CGSize ORKCheckBezierPathSize = (CGSize){28,28};
|
||||
static UIBezierPath *ORKCheckBezierPath() {
|
||||
|
||||
UIBezierPath *bezierPath = UIBezierPath.bezierPath;
|
||||
[bezierPath moveToPoint: CGPointMake(11.6, 19)];
|
||||
[bezierPath addCurveToPoint: CGPointMake(11.1, 18.8) controlPoint1: CGPointMake(11.4, 19) controlPoint2: CGPointMake(11.2, 18.9)];
|
||||
@@ -82,7 +82,6 @@ static UIBezierPath *ORKCheckBezierPath() {
|
||||
|
||||
static const CGSize ORKErrorBezierPathSize = (CGSize){28,28};
|
||||
static UIBezierPath *ORKErrorBezierPath() {
|
||||
|
||||
UIBezierPath *bezier3Path = UIBezierPath.bezierPath;
|
||||
[bezier3Path moveToPoint: CGPointMake(15.1, 14)];
|
||||
[bezier3Path addLineToPoint: CGPointMake(18.8, 10.3)];
|
||||
@@ -114,17 +113,17 @@ static UIBezierPath *ORKErrorBezierPath() {
|
||||
|
||||
@property (nonatomic, readonly) UIEdgeInsets canvasMargins;
|
||||
@property (nonatomic, readonly) CGSize canvasSize;
|
||||
@property (nonatomic, readonly, strong) UIBezierPath *path;
|
||||
@property (nonatomic, readonly, strong) UIColor *color;
|
||||
@property (nonatomic, strong, readonly) UIBezierPath *path;
|
||||
@property (nonatomic, strong, readonly) UIColor *color;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ORKPathView
|
||||
|
||||
- (instancetype)initWithBezierPath:(UIBezierPath *)path canvasSize:(CGSize)canvasSize canvasMargins:(UIEdgeInsets)margins color:(UIColor *)color
|
||||
{
|
||||
CGRect r = (CGRect){CGPointZero, canvasSize};
|
||||
CGRect outsetRect = UIEdgeInsetsInsetRect(r, (UIEdgeInsets){.top=-margins.top,.left=-margins.left,.right=-margins.right,.bottom=-margins.bottom});
|
||||
- (instancetype)initWithBezierPath:(UIBezierPath *)path canvasSize:(CGSize)canvasSize canvasMargins:(UIEdgeInsets)margins color:(UIColor *)color {
|
||||
CGRect canvasRect = (CGRect){CGPointZero, canvasSize};
|
||||
CGRect outsetRect = UIEdgeInsetsInsetRect(canvasRect, (UIEdgeInsets){.top=-margins.top, .left=-margins.left, .right=-margins.right, .bottom=-margins.bottom});
|
||||
self = [super initWithFrame:outsetRect];
|
||||
if (self) {
|
||||
_canvasMargins = margins;
|
||||
@@ -145,16 +144,16 @@ static UIBezierPath *ORKErrorBezierPath() {
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)rect {
|
||||
CGRect bds = [self bounds];
|
||||
CGRect bounds = [self bounds];
|
||||
|
||||
CGContextRef ctx = UIGraphicsGetCurrentContext();
|
||||
[[UIColor clearColor] setFill];
|
||||
CGContextFillRect(ctx, bds);
|
||||
CGContextFillRect(ctx, bounds);
|
||||
|
||||
CGFloat baseWidth = _canvasSize.width + _canvasMargins.left + _canvasMargins.right;
|
||||
CGFloat baseHeight = _canvasSize.height + _canvasMargins.top + _canvasMargins.bottom;
|
||||
|
||||
CGFloat aspectRatio = MIN( bds.size.width / baseWidth, bds.size.height / baseHeight);
|
||||
CGFloat aspectRatio = MIN( bounds.size.width / baseWidth, bounds.size.height / baseHeight);
|
||||
|
||||
CGContextSaveGState(ctx);
|
||||
|
||||
@@ -165,14 +164,12 @@ static UIBezierPath *ORKErrorBezierPath() {
|
||||
[_path fill];
|
||||
|
||||
CGContextRestoreGState(ctx);
|
||||
|
||||
}
|
||||
|
||||
- (void)tintColorDidChange {
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -245,7 +242,6 @@ static UIBezierPath *ORKErrorBezierPath() {
|
||||
}
|
||||
|
||||
- (void)setState:(ORKSpatialSpanTargetState)state {
|
||||
|
||||
[self setState:state animated:NO];
|
||||
}
|
||||
|
||||
@@ -278,12 +274,14 @@ static UIBezierPath *ORKErrorBezierPath() {
|
||||
newCircleAlpha = 0.0;
|
||||
newCircleTransform = CGAffineTransformMakeScale(0.2, 0.2);
|
||||
break;
|
||||
|
||||
case ORKSpatialSpanTargetStateActive:
|
||||
_flowerView.tintColor = [self tintColor];
|
||||
newAlpha = 1.0;
|
||||
newCircleTransform = CGAffineTransformMakeScale(0.2, 0.2);
|
||||
newCircleAlpha = 0.0;
|
||||
break;
|
||||
|
||||
case ORKSpatialSpanTargetStateIncorrect:
|
||||
_flowerView.tintColor = [UIColor ork_redColor];
|
||||
newTransform = CGAffineTransformMakeScale(0.9*_flowerScaleFactor, 0.9*_flowerScaleFactor);
|
||||
@@ -295,6 +293,7 @@ static UIBezierPath *ORKErrorBezierPath() {
|
||||
errorHidden = NO;
|
||||
useSpring = NO;
|
||||
break;
|
||||
|
||||
case ORKSpatialSpanTargetStateCorrect:
|
||||
_flowerView.tintColor = [self tintColor];
|
||||
newTransform = CGAffineTransformMakeScale(1.1*_flowerScaleFactor, 1.1*_flowerScaleFactor);
|
||||
@@ -318,7 +317,6 @@ static UIBezierPath *ORKErrorBezierPath() {
|
||||
_checkView.hidden = checkHidden;
|
||||
_flowerView.transform = CGAffineTransformMakeScale(_flowerScaleFactor, _flowerScaleFactor);
|
||||
|
||||
|
||||
[UIView animateWithDuration:(animated?duration:0) delay:0 usingSpringWithDamping:useSpring?0.5:1 initialSpringVelocity:0 options:(UIViewAnimationOptions)UIViewAnimationOptionBeginFromCurrentState animations:^{
|
||||
_errorView.alpha = newCircleAlpha;
|
||||
_checkView.alpha = newCircleAlpha;
|
||||
@@ -326,19 +324,18 @@ static UIBezierPath *ORKErrorBezierPath() {
|
||||
_checkView.transform = newCircleTransform;
|
||||
self.alpha = newAlpha;
|
||||
_flowerView.transform = newTransform;
|
||||
|
||||
} completion:NULL];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
CGRect bds = self.bounds;
|
||||
_flowerView.center = (CGPoint){CGRectGetMidX(bds), CGRectGetMidY(bds)};
|
||||
_flowerView.bounds = bds;
|
||||
CGRect bounds = self.bounds;
|
||||
_flowerView.center = (CGPoint){CGRectGetMidX(bounds), CGRectGetMidY(bounds)};
|
||||
_flowerView.bounds = bounds;
|
||||
_flowerView.transform = CGAffineTransformMakeScale(_flowerScaleFactor, _flowerScaleFactor);
|
||||
|
||||
CGFloat designWidth = ORKFlowerBezierPathSize.width + _ORKFlowerMargins.left + _ORKFlowerMargins.right;
|
||||
CGFloat scaleFactor = bds.size.width / designWidth;
|
||||
CGFloat scaleFactor = bounds.size.width / designWidth;
|
||||
CGAffineTransform tfm = CGAffineTransformMakeScale(scaleFactor, scaleFactor);
|
||||
|
||||
CGRect checkRect = CGRectApplyAffineTransform((CGRect){CGPointZero,ORKCheckBezierPathSize}, tfm);
|
||||
|
||||
@@ -28,9 +28,11 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKCustomStepView_Internal.h"
|
||||
#import "ORKRoundTappingButton.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ORKTappingContentView : ORKActiveStepCustomView
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKTappingContentView.h"
|
||||
#import "ORKActiveStepTimer.h"
|
||||
#import "ORKResult.h"
|
||||
@@ -36,6 +37,10 @@
|
||||
#import "ORKTapCountLabel.h"
|
||||
#import "ORKHelpers.h"
|
||||
|
||||
|
||||
// #define LAYOUT_DEBUG 1
|
||||
|
||||
|
||||
@interface ORKTappingContentView ()
|
||||
|
||||
@property (nonatomic, strong) ORKSubheadlineLabel *tapCaptionLabel;
|
||||
@@ -44,6 +49,7 @@
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ORKTappingContentView {
|
||||
|
||||
NSArray *_constraints;
|
||||
@@ -54,7 +60,6 @@
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
|
||||
_screenType = ORKScreenTypeiPhone4;
|
||||
_tapCaptionLabel = [ORKSubheadlineLabel new];
|
||||
_tapCaptionLabel.textAlignment = NSTextAlignmentCenter;
|
||||
@@ -94,6 +99,13 @@
|
||||
[self setNeedsUpdateConstraints];
|
||||
|
||||
_tapCountLabel.accessibilityTraits |= UIAccessibilityTraitUpdatesFrequently;
|
||||
|
||||
#if LAYOUT_DEBUG
|
||||
self.backgroundColor = [[UIColor yellowColor] colorWithAlphaComponent:0.5];
|
||||
self.tapCaptionLabel.backgroundColor = [UIColor orangeColor];
|
||||
self.tapCountLabel.backgroundColor = [UIColor greenColor];
|
||||
_buttonContainer.backgroundColor = [[UIColor blueColor] colorWithAlphaComponent:0.25];
|
||||
#endif
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -142,12 +154,23 @@
|
||||
ORKScreenType screenType = _screenType;
|
||||
const CGFloat HeaderBaselineToCaptionTop = ORKGetMetricForScreenType(ORKScreenMetricCaptionBaselineToTappingLabelTop, screenType);
|
||||
const CGFloat AssumedHeaderBaselineToStepViewTop = ORKGetMetricForScreenType(ORKScreenMetricLearnMoreBaselineToStepViewTop, screenType);
|
||||
CGFloat margin = ORKStandardMarginForView(self);
|
||||
CGFloat margin = ORKStandardHorizMarginForView(self);
|
||||
self.layoutMargins = (UIEdgeInsets) { .left=margin*2, .right=margin*2 };
|
||||
|
||||
static const CGFloat CaptionBaselineToTapCountBaseline = 56;
|
||||
static const CGFloat TapButtonBottomToBottom = 36;
|
||||
|
||||
// On the iPhone, _progressView is positioned outside the bounds of this view, to be in-between the header and this view.
|
||||
// On the iPad, we want to stretch this out a bit so it feels less compressed.
|
||||
CGFloat progressViewOffset, topCaptionLabelOffset;
|
||||
if (screenType == ORKScreenTypeiPad) {
|
||||
progressViewOffset = 0;
|
||||
topCaptionLabelOffset = AssumedHeaderBaselineToStepViewTop;
|
||||
} else {
|
||||
progressViewOffset = (HeaderBaselineToCaptionTop/3) - AssumedHeaderBaselineToStepViewTop;
|
||||
topCaptionLabelOffset = HeaderBaselineToCaptionTop - AssumedHeaderBaselineToStepViewTop;
|
||||
}
|
||||
|
||||
NSMutableArray *constraints = [NSMutableArray array];
|
||||
|
||||
NSDictionary *views = NSDictionaryOfVariableBindings(_buttonContainer, _tapCaptionLabel, _tapCountLabel, _progressView, _tapButton1, _tapButton2);
|
||||
@@ -156,14 +179,14 @@
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self
|
||||
attribute:NSLayoutAttributeTop
|
||||
multiplier:1 constant:(HeaderBaselineToCaptionTop/3) - AssumedHeaderBaselineToStepViewTop]];
|
||||
multiplier:1 constant:progressViewOffset]];
|
||||
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_tapCaptionLabel
|
||||
attribute:NSLayoutAttributeTop
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self
|
||||
attribute:NSLayoutAttributeTop
|
||||
multiplier:1 constant:(HeaderBaselineToCaptionTop - AssumedHeaderBaselineToStepViewTop)]];
|
||||
multiplier:1 constant:topCaptionLabelOffset]];
|
||||
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_tapCountLabel
|
||||
attribute:NSLayoutAttributeFirstBaseline
|
||||
@@ -232,7 +255,6 @@
|
||||
multiplier:1 constant:0]];
|
||||
|
||||
|
||||
|
||||
_constraints = constraints;
|
||||
[self addConstraints:_constraints];
|
||||
|
||||
|
||||
@@ -28,8 +28,10 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit_Private.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
ORK_CLASS_AVAILABLE
|
||||
@@ -37,4 +39,4 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -28,17 +28,26 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKTappingIntervalStep.h"
|
||||
#import "ORKTappingIntervalStepViewController.h"
|
||||
|
||||
|
||||
@implementation ORKTappingIntervalStep
|
||||
|
||||
+ (Class)stepViewControllerClass {
|
||||
return [ORKTappingIntervalStepViewController class];
|
||||
}
|
||||
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier {
|
||||
self = [super initWithIdentifier:identifier];
|
||||
if (self) {
|
||||
self.shouldShowDefaultTimer = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)validateParameters {
|
||||
|
||||
[super validateParameters];
|
||||
|
||||
NSTimeInterval const ORKTwoFingerTappingMinimumDuration = 5.0;
|
||||
@@ -46,9 +55,6 @@
|
||||
if ( self.stepDuration < ORKTwoFingerTappingMinimumDuration) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"duration can not be shorter than %@ seconds.", @(ORKTwoFingerTappingMinimumDuration)] userInfo:nil];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -28,8 +28,10 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit_Private.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
ORK_CLASS_AVAILABLE
|
||||
@@ -37,4 +39,4 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKTappingIntervalStepViewController.h"
|
||||
#import "ORKTappingContentView.h"
|
||||
#import "ORKActiveStepViewController_internal.h"
|
||||
@@ -38,14 +39,15 @@
|
||||
#import "ORKHelpers.h"
|
||||
#import "ORKActiveStepView.h"
|
||||
|
||||
|
||||
@interface ORKTappingIntervalStepViewController () <UIGestureRecognizerDelegate>
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray *samples;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ORKTappingIntervalStepViewController {
|
||||
|
||||
ORKTappingContentView *_tappingContentView;
|
||||
NSTimeInterval _tappingStart;
|
||||
BOOL _expired;
|
||||
@@ -59,9 +61,7 @@
|
||||
UIGestureRecognizer *_touchDownRecognizer;
|
||||
}
|
||||
|
||||
|
||||
- (instancetype)initWithStep:(ORKStep *)step {
|
||||
|
||||
self = [super initWithStep:step];
|
||||
if (self) {
|
||||
self.suspendIfInactive = YES;
|
||||
@@ -69,8 +69,7 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initializeInternalButtonItems
|
||||
{
|
||||
- (void)initializeInternalButtonItems {
|
||||
[super initializeInternalButtonItems];
|
||||
|
||||
// Don't show next button
|
||||
@@ -98,10 +97,8 @@
|
||||
|
||||
[_tappingContentView.tapButton1 addTarget:self action:@selector(buttonPressed:forEvent:) forControlEvents:UIControlEventTouchDown];
|
||||
[_tappingContentView.tapButton2 addTarget:self action:@selector(buttonPressed:forEvent:) forControlEvents:UIControlEventTouchDown];
|
||||
|
||||
}
|
||||
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
[super viewDidAppear:animated];
|
||||
|
||||
@@ -111,7 +108,6 @@
|
||||
}
|
||||
|
||||
- (ORKStepResult *)result {
|
||||
|
||||
ORKStepResult *sResult = [super result];
|
||||
|
||||
// "Now" is the end time of the result, which is either actually now,
|
||||
@@ -120,7 +116,7 @@
|
||||
|
||||
NSMutableArray *results = [NSMutableArray arrayWithArray:sResult.results];
|
||||
|
||||
ORKTappingIntervalResult *tappingResult = [[ORKTappingIntervalResult alloc] initWithIdentifier:(NSString *__nonnull)self.step.identifier];
|
||||
ORKTappingIntervalResult *tappingResult = [[ORKTappingIntervalResult alloc] initWithIdentifier:self.step.identifier];
|
||||
tappingResult.startDate = sResult.startDate;
|
||||
tappingResult.endDate = now;
|
||||
tappingResult.buttonRect1 = _buttonRect1;
|
||||
@@ -136,28 +132,26 @@
|
||||
}
|
||||
|
||||
- (void)receiveTouch:(UITouch *)touch onButton:(ORKTappingButtonIdentifier)buttonIdentifier {
|
||||
|
||||
if (_expired || self.samples == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSTimeInterval ti = CACurrentMediaTime();
|
||||
NSTimeInterval mediaTime = CACurrentMediaTime();
|
||||
|
||||
if (_tappingStart == 0) {
|
||||
_tappingStart = ti;
|
||||
_tappingStart = mediaTime;
|
||||
}
|
||||
|
||||
|
||||
CGPoint location = [touch locationInView:self.view];
|
||||
|
||||
// Add new sample
|
||||
ti = ti-_tappingStart;
|
||||
|
||||
mediaTime = mediaTime-_tappingStart;
|
||||
|
||||
ORKTappingSample *sample = [[ORKTappingSample alloc] init];
|
||||
sample.buttonIdentifier = buttonIdentifier;
|
||||
sample.location = location;
|
||||
sample.timestamp = ti;
|
||||
sample.timestamp = mediaTime;
|
||||
|
||||
[self.samples addObject:sample];
|
||||
|
||||
@@ -176,7 +170,6 @@
|
||||
[self goForward];
|
||||
}
|
||||
|
||||
|
||||
- (void)countDownTimerFired:(ORKActiveStepTimer *)timer finished:(BOOL)finished {
|
||||
CGFloat progress = finished ? 1 : (timer.runtime / timer.duration);
|
||||
[_tappingContentView setProgress:progress animated:YES];
|
||||
@@ -201,7 +194,7 @@
|
||||
|
||||
NSInteger index = (button == _tappingContentView.tapButton1) ? ORKTappingButtonIdentifierLeft : ORKTappingButtonIdentifierRight;
|
||||
|
||||
[self receiveTouch:[[event touchesForView:button] anyObject] onButton:index] ;
|
||||
[self receiveTouch:[[event touchesForView:button] anyObject] onButton:index];
|
||||
}
|
||||
|
||||
#pragma mark UIGestureRecognizerDelegate
|
||||
@@ -219,4 +212,3 @@
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Copyright (c) 2015, Shazino SAS. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder(s) nor the names of any contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission. No license is granted to the trademarks of
|
||||
the copyright holders even if such marks are included in this software.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "ORKCustomStepView_Internal.h"
|
||||
#import "ORKRoundTappingButton.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ORKToneAudiometryContentView : ORKActiveStepCustomView
|
||||
|
||||
- (void)setProgress:(CGFloat)progress
|
||||
caption:(NSString *)caption
|
||||
animated:(BOOL)animated;
|
||||
|
||||
@property (nonatomic, strong, readonly) ORKRoundTappingButton *tapButton;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
Copyright (c) 2015, Shazino SAS. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder(s) nor the names of any contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission. No license is granted to the trademarks of
|
||||
the copyright holders even if such marks are included in this software.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "ORKToneAudiometryContentView.h"
|
||||
#import "ORKSkin.h"
|
||||
#import "ORKUnitLabel.h"
|
||||
#import "ORKHelpers.h"
|
||||
|
||||
@interface ORKToneAudiometryContentView ()
|
||||
|
||||
@property (nonatomic, strong) ORKUnitLabel *captionLabel;
|
||||
@property (nonatomic, strong) UIProgressView *progressView;
|
||||
|
||||
- (void)setupConstraints;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ORKToneAudiometryContentView {
|
||||
ORKScreenType _screenType;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
|
||||
_screenType = ORKGetScreenTypeForWindow(self.window);
|
||||
_captionLabel = [ORKUnitLabel new];
|
||||
_captionLabel.textAlignment = NSTextAlignmentCenter;
|
||||
_captionLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
||||
_progressView = [UIProgressView new];
|
||||
_progressView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
_progressView.progressTintColor = [self tintColor];
|
||||
[_progressView setAlpha:0];
|
||||
|
||||
_tapButton = [[ORKRoundTappingButton alloc] init];
|
||||
_tapButton.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[_tapButton setTitle:ORKLocalizedString(@"TAP_BUTTON_TITLE", nil) forState:UIControlStateNormal];
|
||||
|
||||
[self addSubview:_captionLabel];
|
||||
[self addSubview:_progressView];
|
||||
[self addSubview:_tapButton];
|
||||
|
||||
self.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
||||
_captionLabel.text = nil;
|
||||
|
||||
[self setupConstraints];
|
||||
[self setNeedsUpdateConstraints];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)tintColorDidChange {
|
||||
[super tintColorDidChange];
|
||||
self.progressView.progressTintColor = [self tintColor];
|
||||
}
|
||||
|
||||
- (void)setProgress:(CGFloat)progress
|
||||
caption:(NSString *)caption
|
||||
animated:(BOOL)animated {
|
||||
self.captionLabel.text = caption;
|
||||
|
||||
[self.progressView setProgress:progress animated:animated];
|
||||
[UIView animateWithDuration:animated ? 0.2 : 0 animations:^{
|
||||
[self.progressView setAlpha:(progress == 0) ? 0 : 1];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)finishStep:(ORKActiveStepViewController *)viewController {
|
||||
[super finishStep:viewController];
|
||||
self.tapButton.enabled = NO;
|
||||
}
|
||||
|
||||
- (void)setupConstraints {
|
||||
ORKScreenType screenType = _screenType;
|
||||
const CGFloat HeaderBaselineToCaptionTop = ORKGetMetricForScreenType(ORKScreenMetricCaptionBaselineToTappingLabelTop, screenType);
|
||||
const CGFloat AssumedHeaderBaselineToStepViewTop = ORKGetMetricForScreenType(ORKScreenMetricLearnMoreBaselineToStepViewTop, screenType);
|
||||
CGFloat margin = ORKStandardHorizMarginForView(self);
|
||||
self.layoutMargins = (UIEdgeInsets) { .left=margin*2, .right=margin*2 };
|
||||
|
||||
static const CGFloat TapButtonBottomToBottom = 36;
|
||||
|
||||
NSMutableArray *constraints = [NSMutableArray array];
|
||||
|
||||
NSDictionary *views = NSDictionaryOfVariableBindings(_progressView, _captionLabel, _tapButton);
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_progressView
|
||||
attribute:NSLayoutAttributeTop
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self
|
||||
attribute:NSLayoutAttributeTop
|
||||
multiplier:1 constant:(HeaderBaselineToCaptionTop/3) - AssumedHeaderBaselineToStepViewTop]];
|
||||
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_captionLabel
|
||||
attribute:NSLayoutAttributeTop
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self
|
||||
attribute:NSLayoutAttributeTop
|
||||
multiplier:1 constant:(HeaderBaselineToCaptionTop - AssumedHeaderBaselineToStepViewTop)]];
|
||||
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:self
|
||||
attribute:NSLayoutAttributeBottom
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:_tapButton
|
||||
attribute:NSLayoutAttributeBottom
|
||||
multiplier:1 constant:TapButtonBottomToBottom]];
|
||||
|
||||
[constraints addObjectsFromArray:
|
||||
[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_captionLabel]-(>=10)-[_tapButton]"
|
||||
options:NSLayoutFormatAlignAllCenterX
|
||||
metrics:nil views:views]];
|
||||
|
||||
[constraints addObjectsFromArray:
|
||||
[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_progressView]-|"
|
||||
options:(NSLayoutFormatOptions)0
|
||||
metrics:nil views:views]];
|
||||
NSLayoutConstraint *wideProgress = [NSLayoutConstraint constraintWithItem:_progressView
|
||||
attribute:NSLayoutAttributeWidth
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:nil
|
||||
attribute:NSLayoutAttributeNotAnAttribute
|
||||
multiplier:1
|
||||
constant:2000];
|
||||
wideProgress.priority = UILayoutPriorityRequired-1;
|
||||
[constraints addObject:wideProgress];
|
||||
|
||||
[constraints addObjectsFromArray:
|
||||
[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_captionLabel]-|"
|
||||
options:(NSLayoutFormatOptions)0
|
||||
metrics:nil views:views]];
|
||||
|
||||
[constraints addObject:[NSLayoutConstraint constraintWithItem:_tapButton
|
||||
attribute:NSLayoutAttributeCenterX
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self
|
||||
attribute:NSLayoutAttributeCenterX
|
||||
multiplier:1 constant:0]];
|
||||
|
||||
[self addConstraints:constraints];
|
||||
|
||||
[NSLayoutConstraint activateConstraints:constraints];
|
||||
}
|
||||
|
||||
@end
|
||||
+2
-5
@@ -28,15 +28,12 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface UIApplication (ORKFirstResponderAdditions)
|
||||
|
||||
- (nullable UIResponder *)ork_currentFirstResponder;
|
||||
ORK_CLASS_AVAILABLE
|
||||
@interface ORKToneAudiometryPracticeStep : ORKActiveStep
|
||||
|
||||
@end
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user