Compare commits
501 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c1493b9c5c | |||
| d47e835e8e | |||
| 4b456ad2bb | |||
| 125ff93795 | |||
| 2a1bbeefe8 | |||
| ddc023fd4c | |||
| 160bb111f0 | |||
| 30742a5043 | |||
| ead0650dfb | |||
| 4f3d0f33df | |||
| 608d954197 | |||
| 74326baef4 | |||
| 5b1abe95c5 | |||
| fe9c9e1fc5 | |||
| b8b7b80f09 | |||
| 1bcc12bd80 | |||
| eb9464b6ce | |||
| c476ad838e | |||
| c167e4d84c | |||
| 77eb12636a | |||
| 27f4197374 | |||
| 34f473d8e5 | |||
| 42b9907579 | |||
| 67a04562d6 | |||
| f86668e8cb | |||
| 33ca348856 | |||
| bccef20ce5 | |||
| 0f92862e52 | |||
| c7a8cdd2c0 | |||
| 75142028b0 | |||
| d618d45635 | |||
| 7a33c6b4a7 | |||
| add20301a1 | |||
| fbc4dde858 | |||
| 18b6557cf3 | |||
| c195cf1a16 | |||
| 6679fb0922 | |||
| 740051812a | |||
| 5c602d7850 | |||
| 0b4b2ae6e5 | |||
| 0efee740e4 | |||
| 856d544fad | |||
| c257751d1d | |||
| 81e879e8f6 | |||
| ddeed599df | |||
| 65f525ff38 | |||
| 60bfacf507 | |||
| 0eae7e87f7 | |||
| 26597eb334 | |||
| 44c5535a8d | |||
| d06f070e83 | |||
| 633d31be97 | |||
| 6936eb34ac | |||
| 40891abb63 | |||
| f9b8e39876 | |||
| e14d327e3b | |||
| b9bcd6713c | |||
| bc2801bb9e | |||
| ba78feb4dd | |||
| 56a8bc1db3 | |||
| e86ef34a75 | |||
| 912a8112fb | |||
| 27644a292c | |||
| 3f2cb32b23 | |||
| d84944ac6a | |||
| ebfbfcb900 | |||
| 3bd27d6ebe | |||
| e11440b036 | |||
| fdedb11fea | |||
| 72a665b961 | |||
| cfe04441ad | |||
| 7fe3e227ad | |||
| 4d8f4f3bb1 | |||
| e29e7f2b73 | |||
| f2e133676b | |||
| 0c898b4abb | |||
| c1fa1ea154 | |||
| f659531d77 | |||
| 6e22f94cf5 | |||
| 88c7c87f28 | |||
| 6a9da73a8a | |||
| 0f0448bff7 | |||
| fd2c914b68 | |||
| 5122948de0 | |||
| 19d3624658 | |||
| 4f68c2bf4e | |||
| 9e1ed9e070 | |||
| 9fb7562ce5 | |||
| b1b25f9ac4 | |||
| a5b58c4c8e | |||
| 66c7b86484 | |||
| 7b850951ef | |||
| bd5f5bb231 | |||
| 2526450391 | |||
| 289a45c3dd | |||
| 931c7d7728 | |||
| 6d52269995 | |||
| f69401cae9 | |||
| 3d57bf65b8 | |||
| e7b75b5f88 | |||
| 590bb25559 | |||
| f7345070a9 | |||
| 558b5391a3 | |||
| 4ea6fc7d46 | |||
| 2289b01f7c | |||
| 4899548438 | |||
| bcc85b9a2c | |||
| 613d3a676c | |||
| e5fa2374ab | |||
| 859bbb4e17 | |||
| 9fe59ca7a8 | |||
| b3d72ce28a | |||
| e421214098 | |||
| 53e19548c4 | |||
| f3fdb66cf8 | |||
| ead682b402 | |||
| aeb3849fd7 | |||
| b9af6508ac | |||
| 2ff97e4558 | |||
| 26da4d8e34 | |||
| 6ee2b47dc1 | |||
| 84f806ee11 | |||
| a24dc1ac56 | |||
| c3618bb104 | |||
| 119b5f19d2 | |||
| 5195ed44f1 | |||
| 755db8b700 | |||
| 3d95111ace | |||
| 859fc28d8a | |||
| ebc4ccacc0 | |||
| 1c3aab92c1 | |||
| 70093149b5 | |||
| d595a0b199 | |||
| 764b5f8691 | |||
| 395eb936d5 | |||
| dcab7194c1 | |||
| 9c271d87f6 | |||
| d9ebc6d253 | |||
| 2e555356a6 | |||
| 69c830b8cd | |||
| a5aa647ed0 | |||
| d7d923e609 | |||
| 0cbb21d71d | |||
| f5fe7a47f3 | |||
| cbea809430 | |||
| a65d5da62f | |||
| 49f2914063 | |||
| f50340c7e1 | |||
| dfcc02d75f | |||
| 6ad71820d8 | |||
| 275cc3ae17 | |||
| 5f643fdcde | |||
| e444c32eb5 | |||
| 7c7dfc4861 | |||
| 9b9e941299 | |||
| 27a0755414 | |||
| 0dfb3455f7 | |||
| 47d6b5e0f9 | |||
| dbb0fcf2b7 | |||
| 170ca3e0b6 | |||
| 2c868e3bb4 | |||
| 1abd8bf548 | |||
| 5db0f6422e | |||
| 47f1a3bfd1 | |||
| 231ed3f90c | |||
| dd465a00e7 | |||
| 05325d6070 | |||
| ee446da653 | |||
| b39c2ea10d | |||
| 106b2d969f | |||
| cd02eefff7 | |||
| 55b97af36a | |||
| fa50f74e9c | |||
| 903e1cbffe | |||
| 4cda98e896 | |||
| 525d5b2350 | |||
| 191c17930d | |||
| cfe39f8201 | |||
| 4b35370e80 | |||
| 2404b20433 | |||
| a20f953467 | |||
| f41480f5bb | |||
| ddef74e9cc | |||
| a50f7cbc2f | |||
| 5715ea13c5 | |||
| 41575b830c | |||
| c6907b1e9e | |||
| 286f49bb5d | |||
| 59ac9c3213 | |||
| 0cfce94dd6 | |||
| cb4ff44859 | |||
| 0375b70146 | |||
| e9e2434d5f | |||
| 72fb6d5138 | |||
| 60aa464b02 | |||
| 8474d39cdf | |||
| 3f258d2c6c | |||
| aad49eac86 | |||
| 4b610ee085 | |||
| 78d59f7ed8 | |||
| a147b2e6e3 | |||
| 78bc96956c | |||
| 0e9ce26b82 | |||
| e2f284a4cb | |||
| 90af9211eb | |||
| 1107bd9dba | |||
| 4bcc83ecd3 | |||
| aa86bf3bed | |||
| 5de1888375 | |||
| 01eab7bd0c | |||
| 1da1d632b6 | |||
| 072d19c84b | |||
| 11effe846e | |||
| c185fce465 | |||
| 4863368e23 | |||
| 1ce947395d | |||
| c15b98e41a | |||
| 9341e85030 | |||
| 1934f143a5 | |||
| 9b1efb8641 | |||
| c1f30947be | |||
| dc22fe8191 | |||
| 8e2313118f | |||
| 354cee7e7a | |||
| 7cfb8960c9 | |||
| cabaecb5d1 | |||
| 99bd214f23 | |||
| ea7c941424 | |||
| 75e880e1d9 | |||
| a788e35d25 | |||
| e9d7b847da | |||
| b0f5e2cbb7 | |||
| 2c4ef0f73e | |||
| 4ada9cf5ff | |||
| 7b23d8b21e | |||
| d2f26a5ab9 | |||
| c170a8c38a | |||
| 934aa3283b | |||
| 639f75eaa6 | |||
| 5f969da0d7 | |||
| 080c69bd8b | |||
| 42c12ca73f | |||
| 025581ad55 | |||
| 65aee797ce | |||
| f7ea0167b1 | |||
| a5a8531fdf | |||
| e60db320ed | |||
| 8a6895d762 | |||
| 2ac579a610 | |||
| 4b9ae8e404 | |||
| f7d1591c19 | |||
| 9b539d7faf | |||
| e515278b4c | |||
| e3b2d91a8b | |||
| da41b8a43b | |||
| 86273181ac | |||
| 930d94f804 | |||
| d783c0b2b6 | |||
| 92cc2c0ef9 | |||
| 275b7b5ff9 | |||
| 642f87576a | |||
| 4735bc5d47 | |||
| 0e29f50f77 | |||
| daac3505b4 | |||
| 89838d63bd | |||
| 1337dbf851 | |||
| 0c60532068 | |||
| ea0fb64319 | |||
| 14bb563d26 | |||
| 09b7adc981 | |||
| 412f30652f | |||
| 9ef8b4a980 | |||
| ce365bbe10 | |||
| 778bcf71e0 | |||
| bf94333d32 | |||
| 9f5caad71d | |||
| 14fce4445f | |||
| 1984b46efb | |||
| 02a822baef | |||
| cdaa4d4c91 | |||
| 6e4a4560d8 | |||
| 657bcab2f7 | |||
| b8a999995f | |||
| 28ee60beb7 | |||
| e305e3c4b2 | |||
| 90f5a2cc62 | |||
| 75c75901a3 | |||
| c5396e8ae6 | |||
| 942a092bd1 | |||
| 1cb09d68d3 | |||
| fd11ee1e89 | |||
| 878acecad6 | |||
| 5d1fe51b42 | |||
| 077dfcc4ab | |||
| f24c371541 | |||
| 7a0101c12b | |||
| 887f8dd6bd | |||
| f6c8bc939b | |||
| b6cc2f3add | |||
| 24929ca8b1 | |||
| 8c270dca89 | |||
| 129ee6baef | |||
| a2e4b5aa1c | |||
| 75047cabb2 | |||
| 8a3fea6488 | |||
| 57cbebae64 | |||
| b13a470348 | |||
| 2ea828e29e | |||
| 7efb9ecb36 | |||
| ea777873af | |||
| fc7c55c3ab | |||
| df05877256 | |||
| 9025045a4c | |||
| 0571b6e960 | |||
| d1fbc178a1 | |||
| 6a2f18af4d | |||
| cc352493f9 | |||
| 43a4379c8e | |||
| 5c3db570f7 | |||
| 0980410b95 | |||
| 2e5d784608 | |||
| 288d710e51 | |||
| cc46125c69 | |||
| ca94aa7468 | |||
| d0293a0ee7 | |||
| b8c80c70c7 | |||
| 40e328e29e | |||
| 3f89fba2cf | |||
| bece29b351 | |||
| 4d1601a787 | |||
| 80d7685ab3 | |||
| 6e1334a2fc | |||
| 3c550b54b4 | |||
| 7ca7079522 | |||
| e4af7fe7fe | |||
| 8c3b5ce81f | |||
| 783ad4c6b4 | |||
| b85182b372 | |||
| 6e075cbfe3 | |||
| 410affbf5d | |||
| 182960a263 | |||
| 0971ef71f4 | |||
| 92ca8f86a5 | |||
| 24c46898e8 | |||
| 065a02eff1 | |||
| 2db99d6152 | |||
| f6cb8b09f0 | |||
| f9d49594fe | |||
| 62a31b087a | |||
| 70fd57488d | |||
| 1f457e1d54 | |||
| 729247cfcd | |||
| 218b8af66a | |||
| 069c388cb3 | |||
| e199db13b4 | |||
| a96c8b9540 | |||
| 865275d3aa | |||
| a1d29fe225 | |||
| 67bcc93947 | |||
| 8e8fabf79f | |||
| 292727ad91 | |||
| 2f1e58e234 | |||
| 86226e9ba8 | |||
| d40e8193d5 | |||
| 525f2c6efb | |||
| ad52da287c | |||
| 8d0fd95ad0 | |||
| 46c78c08a4 | |||
| 54927408bf | |||
| f325310d77 | |||
| dd4e4fdbce | |||
| 063cde7656 | |||
| fb1ef39165 | |||
| c570771b4d | |||
| 00ff2f3402 | |||
| 407de8ed11 | |||
| 6474ebfff7 | |||
| 340ba7210d | |||
| 8c86b8d80d | |||
| 21c89256fa | |||
| 62ae57bb11 | |||
| fbc4191081 | |||
| 674e06cebc | |||
| ade7f99010 | |||
| 94fbd3a6d1 | |||
| f24a74006a | |||
| 040b9d2220 | |||
| 2e1749a14d | |||
| 204d5dbf40 | |||
| 908b9cb713 | |||
| ff2c5ad055 | |||
| 66fa232ca7 | |||
| 75ad7512b8 | |||
| 8b521477dd | |||
| d60c81b004 | |||
| a44f2db8fd | |||
| 58d377d9ab | |||
| e56bf7d217 | |||
| 28ff1415dd | |||
| 8cbe836bb2 | |||
| 2289abf03a | |||
| 44ee48fa54 | |||
| b704be75b5 | |||
| 4048e34530 | |||
| b34fb01350 | |||
| 1b266c3652 | |||
| 8ab052577d | |||
| 1e34cf6d35 | |||
| d39a72e535 | |||
| 135e2209c8 | |||
| b865da3a75 | |||
| 40111a1431 | |||
| cc45d92266 | |||
| 1619428bb0 | |||
| 3ae773049a | |||
| 76eb4f97d9 | |||
| 12326634db | |||
| 36b90b813c | |||
| 22b769b786 | |||
| 0afc2027d7 | |||
| a78878c712 | |||
| f6fd29087b | |||
| 4517cd445e | |||
| d151e11004 | |||
| ec9b1c9108 | |||
| d7bf7a6ec3 | |||
| 4342caae71 | |||
| f7d7355c49 | |||
| 610ce5aefd | |||
| bd52a14196 | |||
| 423a89c879 | |||
| e444086399 | |||
| 0ed1df3945 | |||
| d71ab9538c | |||
| 7caaf525e3 | |||
| c3102d6183 | |||
| 73495244be | |||
| 85dd559217 | |||
| 81c8c10ebc | |||
| 63a7e30049 | |||
| 8fa839a66b | |||
| f8a77a1b1e | |||
| ddc0016bd1 | |||
| 8b81a8c5f1 | |||
| ff673f0ad4 | |||
| 116a8e08ad | |||
| 09e717c69f | |||
| 9b1bdd390c | |||
| b113ef0ee3 | |||
| 8e49cbc733 | |||
| 37cfadf81a | |||
| fb778eefcf | |||
| f384f9137d | |||
| 2f19a3e089 | |||
| b112945365 | |||
| 58c12a025b | |||
| 2aa09084c8 | |||
| 4d16e39f73 | |||
| 59d2ed7a39 | |||
| e5a4d01039 | |||
| e7aac14571 | |||
| 746b0866b0 | |||
| 11a29435cd | |||
| 00886afe69 | |||
| fdd608fee4 | |||
| b630dff27e | |||
| c41fac8023 | |||
| 2819b7714d | |||
| 81ec1f0056 | |||
| 36f710739d | |||
| 04f5a9e9f6 | |||
| aa2e83e2b5 | |||
| 79deaaa4b2 | |||
| a581d58243 | |||
| 718a4d3ed7 | |||
| 9241848bc3 | |||
| 913be47d6d | |||
| 88a94e106b | |||
| 2469bfe5a3 | |||
| 6ec5df751d | |||
| 7ef55e55e7 | |||
| 41d6a415ad | |||
| a3b2d68b7a | |||
| 87560025c8 | |||
| dc3c6e011c | |||
| 0180500607 | |||
| 42e6c97ee2 | |||
| ce6c79a60f | |||
| 86bcdaa3ee | |||
| 6d2c1400b1 | |||
| 8fa8dbae9e | |||
| d26712e5a1 | |||
| 0eba3970b5 | |||
| 20e9cb24d4 | |||
| 29f883b04b | |||
| 870a9f6ac5 | |||
| 30a346e94d | |||
| 7361442a04 | |||
| 398ae8029f | |||
| 1c0ac116d1 | |||
| 1ecf1d7027 |
@@ -1,8 +1,8 @@
|
||||
ResearchKit Framework
|
||||
===========
|
||||
|
||||
The ResearchKit™ framework is an open source software framework that makes it easy to
|
||||
create apps for medical research or for other research projects.
|
||||
The *ResearchKit™ framework* is an open source software framework that makes it easy to create apps
|
||||
for medical research or for other research projects.
|
||||
|
||||
* [Getting Started](#gettingstarted)
|
||||
* Documentation:
|
||||
@@ -16,36 +16,48 @@ create apps for medical research or for other research projects.
|
||||
Getting More Information
|
||||
========================
|
||||
|
||||
* Join the [ResearchKit Forum](https://forums.developer.apple.com/community/researchkit) for discussing uses of the ResearchKit framework and related projects.
|
||||
* Join the [*ResearchKit* Forum](https://forums.developer.apple.com/community/researchkit) for discussing uses of the *ResearchKit framework and* related projects.
|
||||
|
||||
Use Cases
|
||||
===========
|
||||
|
||||
A task in the ResearchKit framework contains a set of steps to present to a
|
||||
user. Everything, whether it’s a survey, the consent process, or active tasks,
|
||||
is represented as a task that can be presented with a task view controller.
|
||||
A task in the *ResearchKit framework* contains a set of steps to present to a user. Everything,
|
||||
whether it’s a *survey*, the *consent process*, or *active tasks*, is represented as a task that can
|
||||
be presented with a task view controller.
|
||||
|
||||
Surveys
|
||||
-------
|
||||
|
||||
The ResearchKit framework provides a pre-built user interface for surveys, which can be
|
||||
presented modally on an iPhone, iPod Touch, or iPad. See *[Creating Surveys](http://researchkit.org/docs/docs/Survey/CreatingSurveys.html)* for more information.
|
||||
The *ResearchKit framework* provides a pre-built user interface for surveys, which can be presented
|
||||
modally on an *iPhone*, *iPod Touch*, or *iPad*. See
|
||||
*[Creating Surveys](http://researchkit.org/docs/docs/Survey/CreatingSurveys.html)* for more
|
||||
information.
|
||||
|
||||
|
||||
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. See *[Obtaining Consent](http://researchkit.org/docs/docs/InformedConsent/InformedConsent.html)* for more information.
|
||||
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.
|
||||
See *[Obtaining Consent](http://researchkit.org/docs/docs/InformedConsent/InformedConsent.html)* for
|
||||
more information.
|
||||
|
||||
|
||||
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. See *[Active Tasks](http://researchkit.org/docs/docs/ActiveTasks/ActiveTasks.html)* for more information.
|
||||
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. See
|
||||
*[Active Tasks](http://researchkit.org/docs/docs/ActiveTasks/ActiveTasks.html)* for more
|
||||
information.
|
||||
|
||||
Charts
|
||||
------------
|
||||
*ResearchKit* includes a *Charts module*. It features three chart types: a *pie chart* (`ORKPieChartView`), a *line graph chart* (`ORKLineGraphChartView`), and a *discrete graph chart* (`ORKDiscreteGraphChartView`).
|
||||
|
||||
The views in the *Charts module* can be used independently of the rest of *ResearchKit*. They don't automatically connect with any other part of *ResearchKit*: the developer has to supply the data to be displayed through the views' `dataSources`, which allows for maximum flexibility.
|
||||
|
||||
|
||||
Getting Started<a name="gettingstarted"></a>
|
||||
@@ -55,16 +67,15 @@ Getting Started<a name="gettingstarted"></a>
|
||||
Requirements
|
||||
------------
|
||||
|
||||
The primary ResearchKit framework codebase supports iOS and requires Xcode 7.0
|
||||
or newer.
|
||||
The ResearchKit framework has a Base SDK version of 8.0, meaning that apps
|
||||
using the ResearchKit framework can run on devices with iOS 8.0 or newer.
|
||||
The primary *ResearchKit framework* codebase supports *iOS* and requires *Xcode 8.0* or newer. The
|
||||
*ResearchKit framework* has a *Base SDK* version of *8.0*, meaning that apps using the *ResearchKit
|
||||
framework* can run on devices with *iOS 8.0* or newer.
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
The latest 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
|
||||
@@ -79,48 +90,49 @@ git clone https://github.com/ResearchKit/ResearchKit.git
|
||||
Building
|
||||
--------
|
||||
|
||||
Build the ResearchKit framework by opening `ResearchKit.xcodeproj` and running the
|
||||
`ResearchKit` framework target. Optionally, run the unit tests too.
|
||||
Build the *ResearchKit framework* by opening `ResearchKit.xcodeproj` and running the `ResearchKit`
|
||||
framework target. Optionally, run the unit tests too.
|
||||
|
||||
|
||||
Adding the ResearchKit framework to your App
|
||||
------------------------------
|
||||
|
||||
This walk-through shows how to embed the ResearchKit framework in your app as a
|
||||
dynamic framework, and present a simple task view controller.
|
||||
This walk-through shows how to embed the *ResearchKit framework* in your app as a dynamic framework,
|
||||
and present a simple task view controller.
|
||||
|
||||
###1. Add the ResearchKit framework to Your Project
|
||||
### 1. Add the ResearchKit framework to Your Project
|
||||
|
||||
To get started, drag `ResearchKit.xcodeproj` from your checkout into
|
||||
your iOS app project in Xcode:
|
||||
To get started, drag `ResearchKit.xcodeproj` from your checkout into your *iOS* app project
|
||||
in *Xcode*:
|
||||
|
||||
<center>
|
||||
<figure>
|
||||
<img src="../../wiki/AddingResearchKitXcode.png" alt="Adding the ResearchKit framework to your project" align="middle"/>
|
||||
<img src="../../wiki/AddingResearchKitXcode.png" alt="Adding the ResearchKit framework to your
|
||||
project" align="middle"/>
|
||||
</figure>
|
||||
</center>
|
||||
|
||||
Then, embed the ResearchKit framework as a dynamic framework in your app, by adding
|
||||
it to the Embedded Binaries section of the General pane for your
|
||||
target as shown in the figure below.
|
||||
Then, embed the *ResearchKit framework* as a dynamic framework in your app, by adding it to the
|
||||
*Embedded Binaries* section of the *General* pane for your target as shown in the figure below.
|
||||
|
||||
<center>
|
||||
<figure>
|
||||
<img src="../../wiki/AddedBinaries.png" width="100%" alt="Adding the ResearchKit framework to Embedded Binaries" align="middle"/>
|
||||
<img src="../../wiki/AddedBinaries.png" width="100%" alt="Adding the ResearchKit framework to
|
||||
Embedded Binaries" align="middle"/>
|
||||
<figcaption><center>Adding the ResearchKit framework to Embedded Binaries</center></figcaption>
|
||||
</figure>
|
||||
</center>
|
||||
|
||||
Note: You can also import ResearchKit into your project using a [dependency manager](./docs-standalone/dependency-management.md) such as CocoaPods or Carthage.
|
||||
Note: You can also import *ResearchKit* into your project using a
|
||||
[dependency manager](./docs-standalone/dependency-management.md) such as *CocoaPods* or *Carthage*.
|
||||
|
||||
###2. Create a Step
|
||||
### 2. Create a Step
|
||||
|
||||
In this walk-through, we will use the ResearchKit framework to modally present a
|
||||
simple single-step task showing a single instruction.
|
||||
In this walk-through, we will use the *ResearchKit framework* to modally present a simple
|
||||
single-step task showing a single instruction.
|
||||
|
||||
Create a step for your task by adding some code, perhaps in
|
||||
`viewDidAppear:` of an existing view controller. To keep things
|
||||
simple, we'll use an instruction step (`ORKInstructionStep`) and name
|
||||
Create a step for your task by adding some code, perhaps in `viewDidAppear:` of an existing view
|
||||
controller. To keep things simple, we'll use an instruction step (`ORKInstructionStep`) and name
|
||||
the step `myStep`.
|
||||
|
||||
*Objective-C*
|
||||
@@ -138,12 +150,11 @@ let myStep = ORKInstructionStep(identifier: "intro")
|
||||
myStep.title = "Welcome to ResearchKit"
|
||||
```
|
||||
|
||||
###3. Create a Task
|
||||
### 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
|
||||
selection of later steps does not depend on the results of earlier
|
||||
ones. Name your task `task` and initialize it with `myStep`.
|
||||
Use the ordered task class (`ORKOrderedTask`) to create a task that 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*
|
||||
|
||||
@@ -158,11 +169,10 @@ ORKOrderedTask *task =
|
||||
let task = ORKOrderedTask(identifier: "task", steps: [myStep])
|
||||
```
|
||||
|
||||
###4. Present the Task
|
||||
### 4. Present the Task
|
||||
|
||||
Create a task view controller (`ORKTaskViewController`) and initialize
|
||||
it with your `task`. A task view controller manages a task and collects the
|
||||
results of each step. In this case, your task view
|
||||
Create a task view controller (`ORKTaskViewController`) and initialize 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*
|
||||
@@ -182,9 +192,9 @@ 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:
|
||||
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*
|
||||
|
||||
@@ -216,8 +226,7 @@ func taskViewController(taskViewController: ORKTaskViewController,
|
||||
```
|
||||
|
||||
|
||||
If you now run your app, you should see your first ResearchKit framework
|
||||
instruction step:
|
||||
If you now run your app, you should see your first *ResearchKit framework* instruction step:
|
||||
|
||||
<center>
|
||||
<figure>
|
||||
@@ -230,19 +239,18 @@ instruction step:
|
||||
What else can the ResearchKit framework do?
|
||||
-----------------------------
|
||||
|
||||
The ResearchKit [`ORKCatalog`](samples/ORKCatalog) sample app is a
|
||||
good place to start. Find the project in ResearchKit's
|
||||
[`samples`](samples) directory. This project includes a list of all
|
||||
the types of steps supported by the ResearchKit framework in one tab, and displays a
|
||||
browser for the results of the last completed task in the other tab.
|
||||
The *ResearchKit* [`ORKCatalog`](samples/ORKCatalog) sample app is a good place to start. Find the
|
||||
project in ResearchKit's [`samples`](samples) directory. This project includes a list of all the
|
||||
types of steps supported by the *ResearchKit framework* in the first tab, and displays a browser for the
|
||||
results of the last completed task in the second tab. The third tab shows some examples from the *Charts module*.
|
||||
|
||||
|
||||
|
||||
License<a name="license"></a>
|
||||
=======
|
||||
|
||||
The source in the ResearchKit repository is made available under the
|
||||
following license unless another license is explicitly identified:
|
||||
The source in the *ResearchKit* repository is made available under the following license unless
|
||||
another license is explicitly identified:
|
||||
|
||||
```
|
||||
Copyright (c) 2015, Apple Inc. All rights reserved.
|
||||
|
||||
+115
-47
@@ -1,5 +1,73 @@
|
||||
# ResearchKit Release Notes
|
||||
|
||||
## ResearchKit 1.4 Release Notes
|
||||
|
||||
*ResearchKit 1.4* supports *iOS* and requires *Xcode 8.0* or newer. The minimum supported *Base SDK* is *8.0*.
|
||||
|
||||
In addition to general stabiltiy and performance improvements, *ResearchKit 1.4* includes the following new features and enhancements.
|
||||
|
||||
- **New Active Task**
|
||||
|
||||
- **Hand Tremor Task**
|
||||
|
||||
*Contributed by [Shannon Young](https://github.com/syoung-smallwisdom).*
|
||||
|
||||
The *Hand Tremor Task* asks the participant to hold the device with their most affected hand in various positions while accelerometer and motion data is captured.
|
||||
|
||||
- **Walk Back and Forth Task**
|
||||
|
||||
*Contributed by [Shannon Young](https://github.com/syoung-smallwisdom).*
|
||||
|
||||
The *Walk Back and Forth Task* addresses the concern of researchers/participants who have difficulty locating an unobstructed path for 20 steps.
|
||||
|
||||
Instructs users to walk and turn in a full circle, allowing the tests to be conducted in a smaller space.
|
||||
|
||||
- **New Steps**
|
||||
|
||||
- **Video Capture Step**
|
||||
|
||||
*Contributed by [Apple Inc](https://github.com/researchkit).*
|
||||
|
||||
The *Video Capture Step* provides a step to be used to record video.
|
||||
|
||||
The step can be used as part of a survey to capture video respones as well.
|
||||
|
||||
- **Review Step**
|
||||
|
||||
*Contributed by [Oliver Schäfer](https://github.com/oliverschaefer).*
|
||||
|
||||
The *Review Step* allows a participant to review and modify their answers to a survey.
|
||||
|
||||
The step can be used in the middle of a survey, at the end of a survey, or a standalone module.
|
||||
|
||||
- **Signature Step**
|
||||
|
||||
*Contributed by [Oliver Schäfer](https://github.com/oliverschaefer).*
|
||||
|
||||
The *Signature Step* provides an interface for a participant to sign their name.
|
||||
|
||||
The step can be used for handwriting detection or simply to sign a document.
|
||||
|
||||
- **Table Step**
|
||||
|
||||
*Contributed by [Shannon Young](https://github.com/syoung-smallwisdom).*
|
||||
|
||||
The *Table Step* provides a way to neatly display data in a table.
|
||||
|
||||
- **Other Improvements**
|
||||
|
||||
- **Data Collection Module**
|
||||
|
||||
*Contributed by [Apple Inc](https://github.com/researchkit).*
|
||||
|
||||
The *Data Collection Module* makes it even easier to aggregate data from HealthKit and device sensors.
|
||||
|
||||
- **Tapping Test**
|
||||
|
||||
*Contributed by [Michał Zaborowski](https://github.com/m1entus).*
|
||||
|
||||
The *Tapping Test* is updated to include tap duration as part of the result.
|
||||
|
||||
|
||||
## ResearchKit 1.3 Release Notes
|
||||
|
||||
@@ -7,88 +75,88 @@
|
||||
|
||||
In addition to general stability and performance improvements, *ResearchKit 1.3* includes the following new features and enhancements.
|
||||
|
||||
- **New Active Tasks**
|
||||
- **New Active Task**
|
||||
|
||||
- **9-Hole Peg Test**
|
||||
|
||||
|
||||
*Contributed by [Julien Therier](https://github.com/julientherier).*
|
||||
|
||||
The *[9-Hole Peg Test] task* is used to test upper extremity functionality.
|
||||
|
||||
The test involves putting a variable variable number of pegs in a hole, and then removing them.
|
||||
|
||||
The *9-Hole Peg Test task* is used to test upper extremity functionality.
|
||||
|
||||
The test involves putting a variable number of pegs in a hole and subsequently removing them.
|
||||
|
||||
The test is documented in the scientific literature to measure the *[MSFC score in Multiple Sclerosis](http://www.nationalmssociety.org/For-Professionals/Researchers/Resources-for-Researchers/Clinical-Study-Measures/9-Hole-Peg-Test-(9-HPT))* or *[Parkinson's Disease](http://www.ncbi.nlm.nih.gov/pubmed/22020457)*.
|
||||
|
||||
- **Sample App**
|
||||
|
||||
*Contributed by [Apple Inc](https://github.com/researchkit).*
|
||||
|
||||
The *[Sample App]* serves as a template application that combines different modules from the ResearchKit framework.
|
||||
The *Sample App* (`ORKSample` project on *ResearchKit*'s workspace) serves as a template application that combines different modules from the *ResearchKit framework*.
|
||||
|
||||
- **Account Module**
|
||||
|
||||
*Contributed by [Apple Inc](https://github.com/researchkit).*
|
||||
|
||||
The *[Account Module]* provides steps to facilitate account creation and login.
|
||||
|
||||
The *Account Module* provides steps to facilitate account creation and login.
|
||||
|
||||
The module includes the following steps:
|
||||
|
||||
1. Registration to create a new account.
|
||||
2. Verification to verify email.
|
||||
3. Login to allow registered users to login.
|
||||
|
||||
|
||||
1. *Registration*, used to allow the participant to create a new account.
|
||||
2. *Verification*, used to confirm if the participant has verified the provided email address.
|
||||
3. *Login*, used to allow registered users to login.
|
||||
|
||||
- **Passcode with Touch ID**
|
||||
|
||||
*Contributed by [Apple Inc](https://github.com/researchkit).*
|
||||
|
||||
The *[Passcode with Touch ID] module* provides the ability to secure any ResearchKit application with a pin entry.
|
||||
|
||||
This module includes a *Keychain Wrapper* that stores the passcode on the device, as well as the option to use Touch ID on compatible devices. The passcode module supports 4-pin and 6-pin entries.
|
||||
|
||||
The passcode module can be used in the following scenarios:
|
||||
|
||||
1. Passcode creation step which can be used as part of onboarding to create a passcode and store it in the keychain.
|
||||
2. Passcode authentication view controller which can be presented modally when appropriate.
|
||||
3. Passcode modification view controller which allows the participant to change their passcode.
|
||||
|
||||
The *Passcode with Touch ID module* provides the ability to secure any *ResearchKit* application with a numeric passcode.
|
||||
|
||||
This module includes a *Keychain Wrapper* that stores the passcode on the device, as well as the option to use *Touch ID* on compatible devices. The passcode module supports 4-digit and 6-digit numeric codes.
|
||||
|
||||
The passcode module provides the following components:
|
||||
|
||||
1. *Passcode creation step*, which can be used as part of onboarding to create a passcode and store it in the keychain.
|
||||
2. *Passcode authentication view controller*, which can be modally presented when appropriate.
|
||||
3. *Passcode modification view controller*, which allows the participant to change their passcode.
|
||||
|
||||
- **Other Improvements**
|
||||
|
||||
- **Optional Form Items**
|
||||
|
||||
*Contributed by [Ricardo Sánchez-Sáez](https://github.com/rsanchezsaez).*
|
||||
|
||||
Implements the `ORKFormItem` `optional` property.
|
||||
|
||||
The *Continue/Done* button of form steps enables only if:
|
||||
|
||||
- At least one form item has an answer.
|
||||
- All answered form items are valid.
|
||||
- All the non-optional form items have answers.
|
||||
Adds the `optional` property to `ORKFormItem`.
|
||||
|
||||
The *Continue/Done* button of form steps is enabled when all of the following conditions are met:
|
||||
|
||||
- At least one form item has an answer.
|
||||
- All the non-optional form items have answers.
|
||||
- All answered form items have valid answers.
|
||||
|
||||
- **Location Question**
|
||||
|
||||
|
||||
*Contributed by [Quintiles](https://github.com/QuintilesRK).*
|
||||
|
||||
A *Location Question* can be used to request details about the participant's current location or a specific address.
|
||||
|
||||
The question uses *MapKit* to provides a visual representation for the specified address.
|
||||
|
||||
|
||||
A *Location Question* can be used to request details about the participant's current location or about a specific address.
|
||||
|
||||
The question uses *MapKit* to provide a visual representation for the specified address.
|
||||
|
||||
- **Wait Step**
|
||||
|
||||
|
||||
*Contributed by [Quintiles](https://github.com/QuintilesRK).*
|
||||
|
||||
|
||||
The *Wait Step* provides a step to be used in-between steps when additional data processing is required.
|
||||
|
||||
|
||||
The step supports both indeterminate and determinate progress views, as well as the ability to show text status updates.
|
||||
|
||||
|
||||
- **Validated Text Answer Format**
|
||||
|
||||
|
||||
*Contributed by [Quintiles](https://github.com/QuintilesRK).*
|
||||
|
||||
|
||||
The *Validated Text Answer Format* enhances the existing *Text Answer Format* by providing input validation using a regular expression.
|
||||
|
||||
|
||||
A valid *NSRegularExpression* object and an *error message* string are required to properly use this answer format.
|
||||
|
||||
|
||||
|
||||
## ResearchKit 1.2 Release Notes
|
||||
|
||||
@@ -170,7 +238,7 @@ In addition to general stability and performance improvements, *ResearchKit 1.1*
|
||||
A new type of *conditional ordered task* (`ORKNavigableOrderedTask`) has been implemented.
|
||||
|
||||
The developer can use the `ORKStepNavigationRule` subclasses to dynamically navigate between the task steps:
|
||||
- `ORKPredicateStepNavigationRule` allows to make conditional jumps by matching previous results (either those of the the ongoing task, or those of any previously stored task result tree). You typically use the class methods in the `ORKResultPredicate` class to match answers in the most commonly used result types.
|
||||
- `ORKPredicateStepNavigationRule` allows to make conditional jumps by matching previous results (either those of the ongoing task, or those of any previously stored task result tree). You typically use the class methods in the `ORKResultPredicate` class to match answers in the most commonly used result types.
|
||||
- `ORKDirectStepNavigationRule` provides support for unconditional jumps.
|
||||
|
||||
- **New Active Tasks**
|
||||
@@ -216,4 +284,4 @@ In addition to general stability and performance improvements, *ResearchKit 1.1*
|
||||
|
||||
*Contributed by [Apple Inc.](https://github.com/researchkit) and [Ricardo Sánchez-Sáez](https://github.com/rsanchezsaez).*
|
||||
|
||||
*iPhone landscape orientation support* has been implemented.
|
||||
*iPhone landscape orientation support* has been implemented.
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'ResearchKit'
|
||||
s.version = '1.3.1'
|
||||
s.version = ‘1.4.1’
|
||||
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/'
|
||||
@@ -8,7 +8,7 @@ Pod::Spec.new do |s|
|
||||
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.source_files = 'ResearchKit/**/*.{h,m,swift}'
|
||||
s.resources = 'ResearchKit/**/*.{fsh,vsh}', 'ResearchKit/Animations/**/*.m4v', 'ResearchKit/Artwork.xcassets', 'ResearchKit/Localized/*.lproj'
|
||||
s.platform = :ios, '8.0'
|
||||
s.requires_arc = true
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0700"
|
||||
LastUpgradeVersion = "0800"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0700"
|
||||
LastUpgradeVersion = "0800"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -30,6 +30,6 @@
|
||||
|
||||
|
||||
// Shared header for accessibility functionality.
|
||||
#import "UIView+ORKAccessibility.h"
|
||||
#import "ORKAccessibilityFunctions.h"
|
||||
#import "ORKLineGraphAccessibilityElement.h"
|
||||
#import "UIView+ORKAccessibility.h"
|
||||
|
||||
@@ -30,9 +30,11 @@
|
||||
|
||||
|
||||
#import "ORKDefines.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import "ORKHelpers_Internal.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ORKScaleSlider;
|
||||
|
||||
// Used to properly format values from the ORKScaleSlider.
|
||||
@@ -43,7 +45,7 @@ ORK_EXTERN NSString *ORKAccessibilityFormatContinuousScaleSliderValue(CGFloat va
|
||||
ORK_EXTERN void ORKAccessibilityPerformBlockAfterDelay(NSTimeInterval delay, void(^block)(void));
|
||||
|
||||
// Convenience for posting an accessibility notification after a delay.
|
||||
ORK_INLINE void ORKAccessibilityPostNotificationAfterDelay(UIAccessibilityNotifications notification, id argument, NSTimeInterval delay) {
|
||||
ORK_INLINE void ORKAccessibilityPostNotificationAfterDelay(UIAccessibilityNotifications notification, _Nullable id argument, NSTimeInterval delay) {
|
||||
ORKAccessibilityPerformBlockAfterDelay(delay, ^{
|
||||
UIAccessibilityPostNotification(notification, argument);
|
||||
});
|
||||
@@ -52,3 +54,5 @@ ORK_INLINE void ORKAccessibilityPostNotificationAfterDelay(UIAccessibilityNotifi
|
||||
// Creates a string suitable for Voice Over by joining the variables with ", " and avoiding nil and empty strings.
|
||||
#define ORKAccessibilityStringForVariables(...) _ORKAccessibilityStringForVariables(ORK_NARG(__VA_ARGS__), ##__VA_ARGS__)
|
||||
ORK_EXTERN NSString *_ORKAccessibilityStringForVariables(NSInteger numParameters, NSString *baseString, ...);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -29,11 +29,14 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "ORKAccessibilityFunctions.h"
|
||||
#import "ORKAnswerFormat_Internal.h"
|
||||
@import UIKit;
|
||||
|
||||
#import "ORKScaleSlider.h"
|
||||
#import "ORKScaleSliderView.h"
|
||||
|
||||
#import "ORKAnswerFormat_Internal.h"
|
||||
|
||||
#import "ORKAccessibilityFunctions.h"
|
||||
#import "UIView+ORKAccessibility.h"
|
||||
|
||||
|
||||
|
||||
@@ -28,10 +28,16 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@import UIKit;
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ORKLineGraphAccessibilityElement : UIAccessibilityElement
|
||||
|
||||
- (nonnull instancetype)initWithAccessibilityContainer:(nonnull UIView *)container index:(NSInteger)index maxIndex:(NSInteger)maxIndex;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -30,11 +30,15 @@
|
||||
|
||||
#import "ORKLineGraphAccessibilityElement.h"
|
||||
|
||||
|
||||
@interface ORKLineGraphAccessibilityElement()
|
||||
|
||||
@property (assign, nonatomic) NSInteger index;
|
||||
@property (assign, nonatomic) NSInteger maxIndex;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ORKLineGraphAccessibilityElement
|
||||
|
||||
- (nonnull instancetype)initWithAccessibilityContainer:(nonnull UIView *)container index:(NSInteger)index maxIndex:(NSInteger)maxIndex {
|
||||
|
||||
@@ -29,8 +29,7 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
@import UIKit;
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -29,8 +29,7 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
@import CoreLocation;
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
|
||||
|
||||
#import "CLLocation+ORKJSONDictionary.h"
|
||||
#import "ORKHelpers.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
|
||||
|
||||
@implementation CLLocation (ORKJSONDictionary)
|
||||
@@ -45,10 +46,11 @@
|
||||
NSDate *timestamp = self.timestamp;
|
||||
CLFloor *floor = self.floor;
|
||||
|
||||
NSMutableDictionary *dictionary = [@{@"timestamp" : ORKStringFromDateISO8601(timestamp)} mutableCopy];
|
||||
NSMutableDictionary *dictionary = [@{@"timestamp": ORKStringFromDateISO8601(timestamp)} mutableCopy];
|
||||
|
||||
if (horizAccuracy >= 0) {
|
||||
dictionary[@"coordinate"] = @{ @"latitude" : [NSDecimalNumber numberWithDouble:coord.latitude], @"longitude" : [NSDecimalNumber numberWithDouble:coord.longitude]};
|
||||
dictionary[@"coordinate"] = @{ @"latitude": [NSDecimalNumber numberWithDouble:coord.latitude],
|
||||
@"longitude": [NSDecimalNumber numberWithDouble:coord.longitude]};
|
||||
dictionary[@"horizontalAccuracy"] = [NSDecimalNumber numberWithDouble:horizAccuracy];
|
||||
}
|
||||
if (vertAccuracy >= 0) {
|
||||
|
||||
@@ -29,8 +29,7 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <CoreMotion/CoreMotion.h>
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
@import CoreMotion;
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -36,9 +36,9 @@
|
||||
|
||||
- (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]
|
||||
@"x": [NSDecimalNumber numberWithDouble:self.acceleration.x],
|
||||
@"y": [NSDecimalNumber numberWithDouble:self.acceleration.y],
|
||||
@"z": [NSDecimalNumber numberWithDouble:self.acceleration.z]
|
||||
};
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
@@ -29,8 +29,7 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <CoreMotion/CoreMotion.h>
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
@import CoreMotion;
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -42,34 +42,34 @@
|
||||
CMCalibratedMagneticField field = self.magneticField;
|
||||
|
||||
NSDictionary *dictionary = @{@"timestamp": [NSDecimalNumber numberWithDouble:self.timestamp],
|
||||
@"attitude" : @{
|
||||
@"x" : [NSDecimalNumber numberWithDouble:attitude.x],
|
||||
@"y" : [NSDecimalNumber numberWithDouble:attitude.y],
|
||||
@"z" : [NSDecimalNumber numberWithDouble:attitude.z],
|
||||
@"w" : [NSDecimalNumber numberWithDouble:attitude.w]
|
||||
},
|
||||
@"rotationRate" : @{
|
||||
@"x" : [NSDecimalNumber numberWithDouble:rotationRate.x],
|
||||
@"y" : [NSDecimalNumber numberWithDouble:rotationRate.y],
|
||||
@"z" : [NSDecimalNumber numberWithDouble:rotationRate.z]
|
||||
},
|
||||
@"gravity" : @{
|
||||
@"x" : [NSDecimalNumber numberWithDouble:gravity.x],
|
||||
@"y" : [NSDecimalNumber numberWithDouble:gravity.y],
|
||||
@"z" : [NSDecimalNumber numberWithDouble:gravity.z]
|
||||
},
|
||||
@"userAcceleration" : @{
|
||||
@"x" : [NSDecimalNumber numberWithDouble:userAccel.x],
|
||||
@"y" : [NSDecimalNumber numberWithDouble:userAccel.y],
|
||||
@"z" : [NSDecimalNumber numberWithDouble:userAccel.z]
|
||||
},
|
||||
@"magneticField" : @{
|
||||
@"x" : [NSDecimalNumber numberWithDouble:field.field.x],
|
||||
@"y" : [NSDecimalNumber numberWithDouble:field.field.y],
|
||||
@"z" : [NSDecimalNumber numberWithDouble:field.field.z],
|
||||
@"accuracy" : [NSDecimalNumber numberWithDouble:field.accuracy]
|
||||
}
|
||||
};
|
||||
@"attitude": @{
|
||||
@"x": [NSDecimalNumber numberWithDouble:attitude.x],
|
||||
@"y": [NSDecimalNumber numberWithDouble:attitude.y],
|
||||
@"z": [NSDecimalNumber numberWithDouble:attitude.z],
|
||||
@"w": [NSDecimalNumber numberWithDouble:attitude.w]
|
||||
},
|
||||
@"rotationRate": @{
|
||||
@"x": [NSDecimalNumber numberWithDouble:rotationRate.x],
|
||||
@"y": [NSDecimalNumber numberWithDouble:rotationRate.y],
|
||||
@"z": [NSDecimalNumber numberWithDouble:rotationRate.z]
|
||||
},
|
||||
@"gravity": @{
|
||||
@"x": [NSDecimalNumber numberWithDouble:gravity.x],
|
||||
@"y": [NSDecimalNumber numberWithDouble:gravity.y],
|
||||
@"z": [NSDecimalNumber numberWithDouble:gravity.z]
|
||||
},
|
||||
@"userAcceleration": @{
|
||||
@"x": [NSDecimalNumber numberWithDouble:userAccel.x],
|
||||
@"y": [NSDecimalNumber numberWithDouble:userAccel.y],
|
||||
@"z": [NSDecimalNumber numberWithDouble:userAccel.z]
|
||||
},
|
||||
@"magneticField": @{
|
||||
@"x": [NSDecimalNumber numberWithDouble:field.field.x],
|
||||
@"y": [NSDecimalNumber numberWithDouble:field.field.y],
|
||||
@"z": [NSDecimalNumber numberWithDouble:field.field.z],
|
||||
@"accuracy": [NSDecimalNumber numberWithDouble:field.accuracy]
|
||||
}
|
||||
};
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,8 +29,7 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <CoreMotion/CoreMotion.h>
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
@import CoreMotion;
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
|
||||
|
||||
#import "CMMotionActivity+ORKJSONDictionary.h"
|
||||
#import "ORKHelpers.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
|
||||
|
||||
static NSString *const ActivityUnknown = @"unknown";
|
||||
@@ -42,9 +43,9 @@ static NSString *const StartDateKey = @"startDate";
|
||||
static NSString *const EndDateKey = @"endDate";
|
||||
|
||||
static NSString *stringFromActivityConfidence(CMMotionActivityConfidence confidence) {
|
||||
NSDictionary *confidences = @{@(CMMotionActivityConfidenceHigh) : @"high",
|
||||
@(CMMotionActivityConfidenceMedium) : @"medium",
|
||||
@(CMMotionActivityConfidenceLow) : @"low"};
|
||||
NSDictionary *confidences = @{@(CMMotionActivityConfidenceHigh): @"high",
|
||||
@(CMMotionActivityConfidenceMedium): @"medium",
|
||||
@(CMMotionActivityConfidenceLow): @"low"};
|
||||
return confidences[@(confidence)];
|
||||
}
|
||||
|
||||
@@ -74,9 +75,9 @@ static NSString *const ConfidenceKey = @"confidence";
|
||||
@implementation CMMotionActivity (ORKJSONDictionary)
|
||||
|
||||
- (NSDictionary *)ork_JSONDictionary {
|
||||
return @{ConfidenceKey : stringFromActivityConfidence(self.confidence),
|
||||
ActivityKey : activityArray(self),
|
||||
StartDateKey : ORKStringFromDateISO8601(self.startDate)};
|
||||
return @{ConfidenceKey: stringFromActivityConfidence(self.confidence),
|
||||
ActivityKey: activityArray(self),
|
||||
StartDateKey: ORKStringFromDateISO8601(self.startDate)};
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -29,8 +29,7 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <CoreMotion/CoreMotion.h>
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
@import CoreMotion;
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -30,8 +30,10 @@
|
||||
|
||||
|
||||
#import "CMPedometerData+ORKJSONDictionary.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import <CoreMotion/CoreMotion.h>
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
|
||||
@import CoreMotion;
|
||||
|
||||
|
||||
@implementation CMPedometerData (ORKJSONDictionary)
|
||||
|
||||
@@ -29,8 +29,7 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <HealthKit/HealthKit.h>
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
@import HealthKit;
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
|
||||
|
||||
#import "HKSample+ORKJSONDictionary.h"
|
||||
#import "ORKHelpers.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
|
||||
|
||||
static NSString *const HKSampleIdentifierKey = @"type"; // For compatibility with Health XML export
|
||||
@@ -89,7 +90,7 @@ static NSString *const HKCorrelatedObjectsKey = @"objects";
|
||||
}
|
||||
|
||||
if (options & ORKSampleIncludeSource) {
|
||||
HKSource *source = [self source];
|
||||
HKSource *source = [[self sourceRevision] source];
|
||||
if (source.name) {
|
||||
mutableDictionary[HKSourceKey] = source.name;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
*/
|
||||
|
||||
|
||||
@import UIKit;
|
||||
#import <ResearchKit/ORKRecorder.h>
|
||||
|
||||
|
||||
|
||||
@@ -30,12 +30,15 @@
|
||||
|
||||
|
||||
#import "ORKAccelerometerRecorder.h"
|
||||
|
||||
#import "ORKDataLogger.h"
|
||||
#import "CMAccelerometerData+ORKJSONDictionary.h"
|
||||
#import <CoreMotion/CoreMotion.h>
|
||||
|
||||
#import "ORKRecorder_Internal.h"
|
||||
#import "ORKRecorder_Private.h"
|
||||
#import "ORKHelpers.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
#import "CMAccelerometerData+ORKJSONDictionary.h"
|
||||
|
||||
@import CoreMotion;
|
||||
|
||||
|
||||
@interface ORKAccelerometerRecorder () {
|
||||
@@ -87,10 +90,10 @@
|
||||
self.motionManager = [self createMotionManager];
|
||||
|
||||
if (!_logger) {
|
||||
NSError *err = nil;
|
||||
_logger = [self makeJSONDataLoggerWithError:&err];
|
||||
NSError *error = nil;
|
||||
_logger = [self makeJSONDataLoggerWithError:&error];
|
||||
if (!_logger) {
|
||||
[self finishRecordingWithError:err];
|
||||
[self finishRecordingWithError:error];
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -98,7 +101,7 @@
|
||||
if (!self.motionManager || !self.motionManager.accelerometerAvailable) {
|
||||
NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain
|
||||
code:NSFeatureUnsupportedError
|
||||
userInfo:@{@"recorder" : self}];
|
||||
userInfo:@{@"recorder": self}];
|
||||
[self finishRecordingWithError:error];
|
||||
return;
|
||||
}
|
||||
@@ -124,7 +127,7 @@
|
||||
}
|
||||
|
||||
- (NSDictionary *)userInfo {
|
||||
return @{ @"frequency" : @(self.frequency) };
|
||||
return @{ @"frequency": @(self.frequency) };
|
||||
}
|
||||
|
||||
- (void)stop {
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
@import UIKit;
|
||||
@import HealthKit;
|
||||
#import <ResearchKit/ORKStep.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <HealthKit/HealthKit.h>
|
||||
|
||||
|
||||
@class ORKRecorderConfiguration;
|
||||
|
||||
@@ -103,6 +103,17 @@ automatically navigates forward when the timer expires.
|
||||
*/
|
||||
@property (nonatomic) BOOL shouldSpeakCountDown;
|
||||
|
||||
/**
|
||||
A Boolean value indicating whether to speak the halfway point in the count down of the
|
||||
duration of a timed step.
|
||||
|
||||
When the value of this property is `YES`, `AVSpeechSynthesizer` is used to synthesize the countdown. Note that this property is ignored if VoiceOver is enabled.
|
||||
|
||||
The default value of this property is `NO`.
|
||||
*/
|
||||
@property (nonatomic) BOOL shouldSpeakRemainingTimeAtHalfway;
|
||||
|
||||
|
||||
/**
|
||||
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.
|
||||
@@ -171,6 +182,14 @@ The default value of this property is `NO`.
|
||||
*/
|
||||
@property (nonatomic, copy, nullable) NSString *spokenInstruction;
|
||||
|
||||
/**
|
||||
Localized text that represents an instructional voice prompt for when the step finishes.
|
||||
|
||||
Instructional speech begins when the step finishes. If VoiceOver is active,
|
||||
the instruction is spoken by VoiceOver.
|
||||
*/
|
||||
@property (nonatomic, copy, nullable) NSString *finishedSpokenInstruction;
|
||||
|
||||
/**
|
||||
An image to be displayed below the instructions for the step.
|
||||
|
||||
|
||||
@@ -30,12 +30,15 @@
|
||||
|
||||
|
||||
#import "ORKActiveStep.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import "ORKStep_Private.h"
|
||||
#import "ORKActiveStep_Internal.h"
|
||||
|
||||
#import "ORKActiveStepViewController.h"
|
||||
#import "ORKRecorder_Private.h"
|
||||
|
||||
#import "ORKStep_Private.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
|
||||
|
||||
@implementation ORKActiveStep
|
||||
|
||||
@@ -62,7 +65,9 @@
|
||||
}
|
||||
|
||||
- (BOOL)hasVoice {
|
||||
return (_spokenInstruction != nil && _spokenInstruction.length > 0);
|
||||
BOOL hasSpokenInstruction = (_spokenInstruction != nil && _spokenInstruction.length > 0);
|
||||
BOOL hasFinishedSpokenInstruction = (_finishedSpokenInstruction != nil && _finishedSpokenInstruction.length > 0);
|
||||
return (hasSpokenInstruction || hasFinishedSpokenInstruction);
|
||||
}
|
||||
|
||||
- (BOOL)isRestorable {
|
||||
@@ -86,6 +91,7 @@
|
||||
step.stepDuration = self.stepDuration;
|
||||
step.shouldStartTimerAutomatically = self.shouldStartTimerAutomatically;
|
||||
step.shouldSpeakCountDown = self.shouldSpeakCountDown;
|
||||
step.shouldSpeakRemainingTimeAtHalfway = self.shouldSpeakRemainingTimeAtHalfway;
|
||||
step.shouldShowDefaultTimer = self.shouldShowDefaultTimer;
|
||||
step.shouldPlaySoundOnStart = self.shouldPlaySoundOnStart;
|
||||
step.shouldPlaySoundOnFinish = self.shouldPlaySoundOnFinish;
|
||||
@@ -94,6 +100,7 @@
|
||||
step.shouldUseNextAsSkipButton = self.shouldUseNextAsSkipButton;
|
||||
step.shouldContinueOnFinish = self.shouldContinueOnFinish;
|
||||
step.spokenInstruction = self.spokenInstruction;
|
||||
step.finishedSpokenInstruction = self.finishedSpokenInstruction;
|
||||
step.recorderConfigurations = [self.recorderConfigurations copy];
|
||||
step.image = self.image;
|
||||
return step;
|
||||
@@ -105,6 +112,7 @@
|
||||
ORK_DECODE_DOUBLE(aDecoder, stepDuration);
|
||||
ORK_DECODE_BOOL(aDecoder, shouldStartTimerAutomatically);
|
||||
ORK_DECODE_BOOL(aDecoder, shouldSpeakCountDown);
|
||||
ORK_DECODE_BOOL(aDecoder, shouldSpeakRemainingTimeAtHalfway);
|
||||
ORK_DECODE_BOOL(aDecoder, shouldShowDefaultTimer);
|
||||
ORK_DECODE_BOOL(aDecoder, shouldPlaySoundOnStart);
|
||||
ORK_DECODE_BOOL(aDecoder, shouldPlaySoundOnFinish);
|
||||
@@ -113,6 +121,7 @@
|
||||
ORK_DECODE_BOOL(aDecoder, shouldUseNextAsSkipButton);
|
||||
ORK_DECODE_BOOL(aDecoder, shouldContinueOnFinish);
|
||||
ORK_DECODE_OBJ_CLASS(aDecoder, spokenInstruction, NSString);
|
||||
ORK_DECODE_OBJ_CLASS(aDecoder, finishedSpokenInstruction, NSString);
|
||||
ORK_DECODE_IMAGE(aDecoder, image);
|
||||
ORK_DECODE_OBJ_ARRAY(aDecoder, recorderConfigurations, ORKRecorderConfiguration);
|
||||
}
|
||||
@@ -124,6 +133,7 @@
|
||||
ORK_ENCODE_DOUBLE(aCoder, stepDuration);
|
||||
ORK_ENCODE_BOOL(aCoder, shouldStartTimerAutomatically);
|
||||
ORK_ENCODE_BOOL(aCoder, shouldSpeakCountDown);
|
||||
ORK_ENCODE_BOOL(aCoder, shouldSpeakRemainingTimeAtHalfway);
|
||||
ORK_ENCODE_BOOL(aCoder, shouldShowDefaultTimer);
|
||||
ORK_ENCODE_BOOL(aCoder, shouldPlaySoundOnStart);
|
||||
ORK_ENCODE_BOOL(aCoder, shouldPlaySoundOnFinish);
|
||||
@@ -133,6 +143,7 @@
|
||||
ORK_ENCODE_BOOL(aCoder, shouldContinueOnFinish);
|
||||
ORK_ENCODE_IMAGE(aCoder, image);
|
||||
ORK_ENCODE_OBJ(aCoder, spokenInstruction);
|
||||
ORK_ENCODE_OBJ(aCoder, finishedSpokenInstruction);
|
||||
ORK_ENCODE_OBJ(aCoder, recorderConfigurations);
|
||||
}
|
||||
|
||||
@@ -142,12 +153,14 @@
|
||||
__typeof(self) castObject = object;
|
||||
return (isParentSame &&
|
||||
ORKEqualObjects(self.spokenInstruction, castObject.spokenInstruction) &&
|
||||
ORKEqualObjects(self.finishedSpokenInstruction, castObject.finishedSpokenInstruction) &&
|
||||
ORKEqualObjects(self.recorderConfigurations, castObject.recorderConfigurations) &&
|
||||
ORKEqualObjects(self.image, castObject.image) &&
|
||||
(self.stepDuration == castObject.stepDuration) &&
|
||||
(self.shouldShowDefaultTimer == castObject.shouldShowDefaultTimer) &&
|
||||
(self.shouldStartTimerAutomatically == castObject.shouldStartTimerAutomatically) &&
|
||||
(self.shouldSpeakCountDown == castObject.shouldSpeakCountDown) &&
|
||||
(self.shouldSpeakRemainingTimeAtHalfway == castObject.shouldSpeakRemainingTimeAtHalfway) &&
|
||||
(self.shouldPlaySoundOnStart == castObject.shouldPlaySoundOnStart) &&
|
||||
(self.shouldPlaySoundOnFinish == castObject.shouldPlaySoundOnFinish) &&
|
||||
(self.shouldVibrateOnStart == castObject.shouldVibrateOnStart) &&
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit_Private.h>
|
||||
@import UIKit;
|
||||
#import "ORKLabel.h"
|
||||
|
||||
|
||||
|
||||
@@ -30,10 +30,12 @@
|
||||
|
||||
|
||||
#import "ORKActiveStepQuantityView.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import "ORKSkin.h"
|
||||
#import "ORKTintedImageView.h"
|
||||
|
||||
#import "ORKSubheadlineLabel.h"
|
||||
#import "ORKTintedImageView.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
#import "ORKSkin.h"
|
||||
|
||||
|
||||
@implementation ORKQuantityLabel
|
||||
|
||||
@@ -29,8 +29,7 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
@import Foundation;
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -30,9 +30,12 @@
|
||||
|
||||
|
||||
#import "ORKActiveStepTimer.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
|
||||
@import UIKit;
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_time.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
|
||||
static NSTimeInterval timeIntervalFromMachTime(uint64_t delta) {
|
||||
@@ -197,9 +200,9 @@ static NSTimeInterval timeIntervalFromMachTime(uint64_t delta) {
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
__weak typeof(self) weakSelf = self;
|
||||
ORKWeakTypeOf(self) weakSelf = self;
|
||||
dispatch_source_set_event_handler(_timer, ^{
|
||||
typeof(self) strongSelf = weakSelf;
|
||||
ORKStrongTypeOf(self) strongSelf = weakSelf;
|
||||
[strongSelf hiqueue_event];
|
||||
});
|
||||
|
||||
|
||||
@@ -29,18 +29,20 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <ResearchKit/ResearchKit_Private.h>
|
||||
@import UIKit;
|
||||
#import "ORKCustomStepView_Internal.h"
|
||||
#import "ORKCountdownLabel.h"
|
||||
#import "ORKTextButton.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ORKActiveStep;
|
||||
@class ORKCountdownLabel;
|
||||
@class ORKTextButton;
|
||||
|
||||
@interface ORKActiveStepTimerView : ORKActiveStepCustomView
|
||||
|
||||
@property (nonatomic, strong, nullable) ORKCountdownLabel *countDownLabel;
|
||||
|
||||
@property (nonatomic, strong, nullable) ORKTextButton *startTimerButton;
|
||||
|
||||
@property (nonatomic, strong, nullable) ORKActiveStep *step;
|
||||
|
||||
@@ -30,17 +30,22 @@
|
||||
|
||||
|
||||
#import "ORKActiveStepTimerView.h"
|
||||
|
||||
#import "ORKActiveStepTimer.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import "ORKSkin.h"
|
||||
#import "ORKActiveStepViewController_Internal.h"
|
||||
#import "ORKVoiceEngine.h"
|
||||
#import "ORKCountdownLabel.h"
|
||||
#import "ORKSurveyAnswerCellForText.h"
|
||||
#import "ORKSurveyAnswerCellForNumber.h"
|
||||
#import "ORKActiveStep_Internal.h"
|
||||
#import "ORKTextButton.h"
|
||||
#import "ORKVoiceEngine.h"
|
||||
|
||||
#import "ORKActiveStepViewController_Internal.h"
|
||||
#import "ORKStepViewController_Internal.h"
|
||||
|
||||
#import "ORKActiveStep_Internal.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
#import "ORKSkin.h"
|
||||
|
||||
|
||||
@implementation ORKActiveStepTimerView {
|
||||
BOOL _started;
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
*/
|
||||
|
||||
|
||||
@import UIKit;
|
||||
#import <ResearchKit/ORKStepViewController.h>
|
||||
#import <ResearchKit/ORKRecorder.h>
|
||||
|
||||
|
||||
@@ -30,23 +30,27 @@
|
||||
|
||||
|
||||
#import "ORKActiveStepViewController.h"
|
||||
#import "ORKVoiceEngine.h"
|
||||
#import "ORKSkin.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import "ORKActiveStep.h"
|
||||
#import "ORKTask.h"
|
||||
#import "ORKTaskViewController.h"
|
||||
#import "ORKVerticalContainerView.h"
|
||||
#import "ORKStepViewController_Internal.h"
|
||||
#import "ORKActiveStepViewController_Internal.h"
|
||||
#import "ORKActiveStep_Internal.h"
|
||||
#import "ORKRecorder_Internal.h"
|
||||
#import "ORKTaskViewController_Internal.h"
|
||||
#import "ORKActiveStepTimerView.h"
|
||||
|
||||
#import "ORKActiveStepTimer.h"
|
||||
#import "ORKAccessibility.h"
|
||||
#import "ORKStepHeaderView_Internal.h"
|
||||
#import "ORKActiveStepTimerView.h"
|
||||
#import "ORKActiveStepView.h"
|
||||
#import "ORKNavigationContainerView.h"
|
||||
#import "ORKStepHeaderView_Internal.h"
|
||||
#import "ORKVerticalContainerView.h"
|
||||
#import "ORKVoiceEngine.h"
|
||||
|
||||
#import "ORKActiveStepViewController_Internal.h"
|
||||
#import "ORKStepViewController_Internal.h"
|
||||
#import "ORKTaskViewController_Internal.h"
|
||||
#import "ORKRecorder_Internal.h"
|
||||
|
||||
#import "ORKActiveStep_Internal.h"
|
||||
#import "ORKResult.h"
|
||||
#import "ORKTask.h"
|
||||
|
||||
#import "ORKAccessibility.h"
|
||||
#import "ORKHelpers_Internal.h"
|
||||
#import "ORKSkin.h"
|
||||
|
||||
|
||||
@interface ORKActiveStepViewController () {
|
||||
@@ -57,6 +61,7 @@
|
||||
|
||||
SystemSoundID _alertSound;
|
||||
NSURL *_alertSoundURL;
|
||||
BOOL _hasSpokenHalfwayCountdown;
|
||||
}
|
||||
|
||||
@property (nonatomic, strong) NSArray *recorders;
|
||||
@@ -362,6 +367,9 @@
|
||||
if (self.activeStep.shouldVibrateOnFinish) {
|
||||
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
|
||||
}
|
||||
if (self.activeStep.hasVoice && self.activeStep.finishedSpokenInstruction) {
|
||||
[[ORKVoiceEngine sharedVoiceEngine] speakText:self.activeStep.finishedSpokenInstruction];
|
||||
}
|
||||
if (!self.activeStep.startsFinished) {
|
||||
if (self.activeStep.shouldContinueOnFinish) {
|
||||
[self goForward];
|
||||
@@ -390,12 +398,12 @@
|
||||
NSTimeInterval stepDuration = self.activeStep.stepDuration;
|
||||
|
||||
if (stepDuration > 0) {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
ORKWeakTypeOf(self) weakSelf = self;
|
||||
_activeStepTimer = [[ORKActiveStepTimer alloc] initWithDuration:stepDuration
|
||||
interval:_timerUpdateInterval
|
||||
runtime:0
|
||||
handler:^(ORKActiveStepTimer *timer, BOOL finished) {
|
||||
typeof(self) strongSelf = weakSelf;
|
||||
ORKStrongTypeOf(self) strongSelf = weakSelf;
|
||||
[strongSelf countDownTimerFired:timer finished:finished];
|
||||
}];
|
||||
[_activeStepTimer resume];
|
||||
@@ -410,6 +418,7 @@
|
||||
ORKActiveStepCustomView *customView = _activeStepView.activeCustomView;
|
||||
[customView updateDisplay:self];
|
||||
|
||||
|
||||
ORKVoiceEngine *voice = [ORKVoiceEngine sharedVoiceEngine];
|
||||
|
||||
if (!finished && self.activeStep.shouldSpeakCountDown) {
|
||||
@@ -423,6 +432,13 @@
|
||||
[voice speakInt:countDownValue];
|
||||
}
|
||||
}
|
||||
|
||||
BOOL isHalfway = !_hasSpokenHalfwayCountdown && timer.runtime > timer.duration / 2.0;
|
||||
if (!finished && self.activeStep.shouldSpeakRemainingTimeAtHalfway && !UIAccessibilityIsVoiceOverRunning() && isHalfway) {
|
||||
_hasSpokenHalfwayCountdown = YES;
|
||||
NSString *text = [NSString stringWithFormat:ORKLocalizedString(@"COUNTDOWN_SPOKEN_REMAINING_%@", nil), @(countDownValue)];
|
||||
[voice speakText:text];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)timerActive {
|
||||
@@ -450,7 +466,7 @@
|
||||
|
||||
- (void)recorder:(ORKRecorder *)recorder didFailWithError:(NSError *)error {
|
||||
if (error) {
|
||||
STRONGTYPE(self.delegate) strongDelegate = self.delegate;
|
||||
ORKStrongTypeOf(self.delegate) strongDelegate = self.delegate;
|
||||
if ([strongDelegate respondsToSelector:@selector(stepViewController:recorder:didFailWithError:)]) {
|
||||
[strongDelegate stepViewController:self recorder:recorder didFailWithError:error];
|
||||
}
|
||||
|
||||
@@ -30,11 +30,11 @@
|
||||
|
||||
|
||||
#import "ORKActiveStepViewController.h"
|
||||
#import "ORKActiveStepTimer.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ORKActiveStepTimer;
|
||||
@class ORKActiveStepView;
|
||||
|
||||
@interface ORKActiveStepViewController ()
|
||||
|
||||
@@ -29,14 +29,14 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ORKActiveStep.h>
|
||||
#import "ORKActiveStep.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ORKActiveStep ()
|
||||
|
||||
/**
|
||||
Convenience methods.
|
||||
*/
|
||||
// Convenience methods.
|
||||
- (BOOL)startsFinished;
|
||||
- (BOOL)hasCountDown;
|
||||
- (BOOL)hasTitle;
|
||||
@@ -44,3 +44,5 @@
|
||||
- (BOOL)hasVoice;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
@import UIKit;
|
||||
#import "ORKCustomStepView_Internal.h"
|
||||
|
||||
|
||||
|
||||
@@ -30,11 +30,13 @@
|
||||
|
||||
|
||||
#import "ORKAudioContentView.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import "ORKSkin.h"
|
||||
#import "ORKLabel.h"
|
||||
|
||||
#import "ORKHeadlineLabel.h"
|
||||
#import "ORKLabel.h"
|
||||
|
||||
#import "ORKAccessibility.h"
|
||||
#import "ORKHelpers_Internal.h"
|
||||
#import "ORKSkin.h"
|
||||
|
||||
|
||||
// The central blue region.
|
||||
@@ -328,17 +330,16 @@ static const CGFloat ValueLineMargin = 1.5;
|
||||
}
|
||||
|
||||
- (void)updateTimerLabel {
|
||||
static NSDateComponentsFormatter *_formatter = nil;
|
||||
static NSDateComponentsFormatter *formatter = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
NSDateComponentsFormatter *formatter = [NSDateComponentsFormatter new];
|
||||
formatter.unitsStyle = NSDateComponentsFormatterUnitsStylePositional;
|
||||
formatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorPad;
|
||||
formatter.allowedUnits = NSCalendarUnitMinute | NSCalendarUnitSecond;
|
||||
_formatter = formatter;
|
||||
});
|
||||
|
||||
NSString *string = [_formatter stringFromTimeInterval:MAX(round(_timeLeft),0)];
|
||||
NSString *string = [formatter stringFromTimeInterval:MAX(round(_timeLeft),0)];
|
||||
_timerLabel.text = string;
|
||||
_timerLabel.hidden = (string == nil);
|
||||
}
|
||||
@@ -351,6 +352,10 @@ static const CGFloat ValueLineMargin = 1.5;
|
||||
- (void)updateAlertLabelHidden {
|
||||
NSNumber *sample = _samples.lastObject;
|
||||
BOOL show = (!_finished && (sample.doubleValue > _alertThreshold)) || _failed;
|
||||
|
||||
if (_alertLabel.hidden && show) {
|
||||
UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, _alertLabel.text);
|
||||
}
|
||||
_alertLabel.hidden = !show;
|
||||
}
|
||||
|
||||
@@ -384,11 +389,9 @@ static const CGFloat ValueLineMargin = 1.5;
|
||||
}
|
||||
|
||||
- (NSString *)accessibilityLabel {
|
||||
if (_alertLabel.isHidden) {
|
||||
return _timerLabel.accessibilityLabel;
|
||||
}
|
||||
|
||||
return ORKAccessibilityStringForVariables(_timerLabel.accessibilityLabel, _alertLabel.accessibilityLabel);
|
||||
NSString *timerAxString = _timerLabel.isHidden ? nil : _timerLabel.accessibilityLabel;
|
||||
NSString *alertAxString = _alertLabel.isHidden ? nil : _alertLabel.accessibilityLabel;
|
||||
return ORKAccessibilityStringForVariables(ORKLocalizedString(@"AX_AUDIO_BAR_GRAPH", nil), timerAxString, alertAxString);
|
||||
}
|
||||
|
||||
- (UIAccessibilityTraits)accessibilityTraits {
|
||||
|
||||
@@ -49,8 +49,11 @@
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#import <ResearchKit/ORKRecorder.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@import UIKit;
|
||||
@import AVFoundation;
|
||||
#import "ORKTypes.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@@ -49,15 +49,15 @@
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
|
||||
#import "ORKAudioGenerator.h"
|
||||
|
||||
|
||||
@import AudioToolbox;
|
||||
|
||||
@interface ORKAudioGenerator () {
|
||||
@public
|
||||
@public
|
||||
AudioComponentInstance _toneUnit;
|
||||
|
||||
@public
|
||||
double _frequency;
|
||||
double _theta;
|
||||
ORKAudioChannel _activeChannel;
|
||||
@@ -148,15 +148,15 @@ OSStatus ORKAudioGeneratorRenderTone(void *inRefCon,
|
||||
|
||||
- (void)applicationDidBecomeActive:(NSNotification *)notification {
|
||||
if (_toneUnit) {
|
||||
__unused OSErr err = AudioOutputUnitStart(_toneUnit);
|
||||
NSAssert1(err == noErr, @"Error starting unit: %hd", err);
|
||||
__unused OSErr error = AudioOutputUnitStart(_toneUnit);
|
||||
NSAssert1(error == noErr, @"Error starting unit: %hd", error);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(NSNotification *)notification {
|
||||
if (_toneUnit) {
|
||||
__unused OSErr err = AudioOutputUnitStop(_toneUnit);
|
||||
NSAssert1(err == noErr, @"Error stopping unit: %hd", err);
|
||||
__unused OSErr error = AudioOutputUnitStop(_toneUnit);
|
||||
NSAssert1(error == noErr, @"Error stopping unit: %hd", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,12 +194,12 @@ OSStatus ORKAudioGeneratorRenderTone(void *inRefCon,
|
||||
[self createToneUnit];
|
||||
|
||||
// Stop changing parameters on the unit
|
||||
OSErr err = AudioUnitInitialize(_toneUnit);
|
||||
NSAssert1(err == noErr, @"Error initializing unit: %hd", err);
|
||||
OSErr error = AudioUnitInitialize(_toneUnit);
|
||||
NSAssert1(error == noErr, @"Error initializing unit: %hd", error);
|
||||
|
||||
// Start playback
|
||||
err = AudioOutputUnitStart(_toneUnit);
|
||||
NSAssert1(err == noErr, @"Error starting unit: %hd", err);
|
||||
error = AudioOutputUnitStart(_toneUnit);
|
||||
NSAssert1(error == noErr, @"Error starting unit: %hd", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,20 +240,20 @@ OSStatus ORKAudioGeneratorRenderTone(void *inRefCon,
|
||||
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);
|
||||
OSErr error = AudioComponentInstanceNew(defaultOutput, &_toneUnit);
|
||||
NSAssert1(_toneUnit, @"Error creating unit: %hd", error);
|
||||
|
||||
// Set our tone rendering function on the unit
|
||||
AURenderCallbackStruct input;
|
||||
input.inputProc = ORKAudioGeneratorRenderTone;
|
||||
input.inputProcRefCon = (__bridge void *)(self);
|
||||
err = AudioUnitSetProperty(_toneUnit,
|
||||
error = AudioUnitSetProperty(_toneUnit,
|
||||
kAudioUnitProperty_SetRenderCallback,
|
||||
kAudioUnitScope_Input,
|
||||
0,
|
||||
&input,
|
||||
sizeof(input));
|
||||
NSAssert1(err == noErr, @"Error setting callback: %hd", err);
|
||||
NSAssert1(error == noErr, @"Error setting callback: %hd", error);
|
||||
|
||||
// Set the format to 32 bit, single channel, floating point, linear PCM
|
||||
const int four_bytes_per_float = 4;
|
||||
@@ -267,13 +267,13 @@ OSStatus ORKAudioGeneratorRenderTone(void *inRefCon,
|
||||
streamFormat.mBytesPerFrame = four_bytes_per_float;
|
||||
streamFormat.mChannelsPerFrame = 2;
|
||||
streamFormat.mBitsPerChannel = four_bytes_per_float * eight_bits_per_byte;
|
||||
err = AudioUnitSetProperty (_toneUnit,
|
||||
error = AudioUnitSetProperty (_toneUnit,
|
||||
kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Input,
|
||||
0,
|
||||
&streamFormat,
|
||||
sizeof(AudioStreamBasicDescription));
|
||||
NSAssert1(err == noErr, @"Error setting stream format: %hd", err);
|
||||
NSAssert1(error == noErr, @"Error setting stream format: %hd", error);
|
||||
}
|
||||
|
||||
- (void)handleInterruption:(id)sender {
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
Copyright (c) 2016, Sage Bionetworks
|
||||
Copyright (c) 2016, Apple Inc.
|
||||
|
||||
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/ORKStepNavigationRule.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
ORK_CLASS_AVAILABLE
|
||||
@interface ORKAudioLevelNavigationRule : ORKStepNavigationRule
|
||||
|
||||
/**
|
||||
Returns an initialized direct-step navigation rule using the specified destination step identifier.
|
||||
|
||||
@param audioLevelStepIdentifier The identifier of the step with the audio file to check.
|
||||
@param destinationStepIdentifier The identifier of the destination step if audio test passes.
|
||||
@param recordingSettings Use key AVNumberOfChannelsKey to sepcify the number of recording channels.
|
||||
@return A audio level step navigation rule.
|
||||
*/
|
||||
- (instancetype)initWithAudioLevelStepIdentifier:(NSString *)audioLevelStepIdentifier
|
||||
destinationStepIdentifier:(NSString *)destinationStepIdentifier
|
||||
recordingSettings:(NSDictionary *)recordingSettings NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
Returns a new direct-step navigation rule initialized from data in a given unarchiver.
|
||||
|
||||
@param aDecoder The coder from which to initialize the step navigation rule.
|
||||
|
||||
@return A new direct-step navigation rule.
|
||||
*/
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@property (nonatomic, copy, readonly) NSString *audioLevelStepIdentifier;
|
||||
@property (nonatomic, copy, readonly) NSString *destinationStepIdentifier;
|
||||
@property (nonatomic, copy, readonly) NSDictionary *recordingSettings;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
Copyright (c) 2016, Sage Bionetworks
|
||||
Copyright (c) 2016, Apple Inc.
|
||||
|
||||
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 "ORKAudioLevelNavigationRule.h"
|
||||
|
||||
#import "ORKResult.h"
|
||||
#import "ORKResultPredicate.h"
|
||||
#import "ORKStepNavigationRule_Internal.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
|
||||
Float32 const VolumeThreshold = 0.45;
|
||||
UInt16 const LinearPCMBitDepth = 16;
|
||||
Float32 const MaxAmplitude = 32767.0;
|
||||
Float32 const VolumeClamp = 60.0;
|
||||
|
||||
|
||||
@interface ORKAudioLevelNavigationRule ()
|
||||
|
||||
@property (nonatomic, copy, readwrite) NSString *audioLevelStepIdentifier;
|
||||
@property (nonatomic, copy, readwrite) NSString *destinationStepIdentifier;
|
||||
@property (nonatomic, copy, readwrite) NSDictionary *recordingSettings;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ORKAudioLevelNavigationRule
|
||||
|
||||
- (instancetype)initWithAudioLevelStepIdentifier:(NSString *)audioLevelStepIdentifier
|
||||
destinationStepIdentifier:(NSString *)destinationStepIdentifier
|
||||
recordingSettings:(NSDictionary *)recordingSettings
|
||||
{
|
||||
ORKThrowInvalidArgumentExceptionIfNil(audioLevelStepIdentifier);
|
||||
ORKThrowInvalidArgumentExceptionIfNil(destinationStepIdentifier);
|
||||
ORKThrowInvalidArgumentExceptionIfNil(recordingSettings);
|
||||
self = [super init_ork];
|
||||
if (self) {
|
||||
_audioLevelStepIdentifier = [audioLevelStepIdentifier copy];
|
||||
_destinationStepIdentifier = [destinationStepIdentifier copy];
|
||||
_recordingSettings = [recordingSettings copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark NSSecureCoding
|
||||
|
||||
+ (BOOL)supportsSecureCoding {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self) {
|
||||
ORK_DECODE_OBJ_CLASS(aDecoder, audioLevelStepIdentifier, NSString);
|
||||
ORK_DECODE_OBJ_CLASS(aDecoder, destinationStepIdentifier, NSString);
|
||||
ORK_DECODE_OBJ_CLASS(aDecoder, recordingSettings, NSDictionary);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[super encodeWithCoder:aCoder];
|
||||
ORK_ENCODE_OBJ(aCoder, audioLevelStepIdentifier);
|
||||
ORK_ENCODE_OBJ(aCoder, destinationStepIdentifier);
|
||||
ORK_ENCODE_OBJ(aCoder, recordingSettings);
|
||||
}
|
||||
|
||||
#pragma mark NSCopying
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
typeof(self) rule = [[[self class] allocWithZone:zone] initWithAudioLevelStepIdentifier:self.audioLevelStepIdentifier destinationStepIdentifier:self.destinationStepIdentifier recordingSettings:self.recordingSettings];
|
||||
return rule;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
BOOL isParentSame = [super isEqual:object];
|
||||
__typeof(self) castObject = object;
|
||||
return (isParentSame
|
||||
&& ORKEqualObjects(self.audioLevelStepIdentifier, castObject.audioLevelStepIdentifier)
|
||||
&& ORKEqualObjects(self.destinationStepIdentifier, castObject.destinationStepIdentifier)
|
||||
&& ORKEqualObjects(self.recordingSettings, castObject.recordingSettings));
|
||||
}
|
||||
|
||||
- (NSUInteger)hash {
|
||||
return _audioLevelStepIdentifier.hash ^ _destinationStepIdentifier.hash ^ _recordingSettings.hash;
|
||||
}
|
||||
|
||||
#pragma mark - Required overrides
|
||||
|
||||
- (NSString *)identifierForDestinationStepWithTaskResult:(ORKTaskResult *)taskResult {
|
||||
|
||||
// Get the result file
|
||||
ORKStepResult *stepResult = (ORKStepResult *)[taskResult resultForIdentifier:self.audioLevelStepIdentifier];
|
||||
ORKFileResult *audioLevelResult = (ORKFileResult *)[stepResult.results firstObject];
|
||||
|
||||
// Check the volume
|
||||
if ((audioLevelResult.fileURL != nil) && [self checkAudioLevelFromSoundFile:audioLevelResult.fileURL]) {
|
||||
// Returning nil will drop through to the next step (which should be the the step that has the instructions
|
||||
// for moving to a quieter room).
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self.destinationStepIdentifier;
|
||||
}
|
||||
|
||||
- (BOOL)checkAudioLevelFromSoundFile:(NSURL *)fileURL {
|
||||
// Setup reader
|
||||
AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:fileURL options:nil];
|
||||
if (urlAsset.tracks.count == 0) {
|
||||
NSLog(@"No tracks found for urlAsset: %@", fileURL);
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSError *error = nil;
|
||||
AVAssetReader *reader = [[AVAssetReader alloc] initWithAsset:urlAsset error:&error];
|
||||
AVAssetTrack *track = [urlAsset.tracks objectAtIndex:0];
|
||||
NSDictionary *outputSettings = @{AVFormatIDKey: @(kAudioFormatLinearPCM),
|
||||
AVLinearPCMBitDepthKey: @(LinearPCMBitDepth),
|
||||
AVLinearPCMIsBigEndianKey: @(NO),
|
||||
AVLinearPCMIsFloatKey: @(NO),
|
||||
AVLinearPCMIsNonInterleaved: @(NO)};
|
||||
AVAssetReaderTrackOutput *output = [[AVAssetReaderTrackOutput alloc] initWithTrack:track outputSettings:outputSettings];
|
||||
[reader addOutput:output];
|
||||
|
||||
// Setup initial values - Assume 2 channels if not in recording settings
|
||||
const UInt32 channelCount = (UInt32)[self.recordingSettings[AVNumberOfChannelsKey] unsignedIntegerValue] ? : 2;
|
||||
const UInt32 bytesPerSample = 2 * channelCount;
|
||||
|
||||
// setup criteria block - Use a high-pass filter and a rolling average of the amplitude
|
||||
// normalized to be < 1
|
||||
__block Float32 rollingAvg = 0;
|
||||
__block UInt64 totalCount = 0;
|
||||
void (^processVolume)(Float32) = ^(Float32 amplitude) {
|
||||
if (amplitude != 0) {
|
||||
Float32 dB = 20 * log10(ABS(amplitude) / MaxAmplitude);
|
||||
float clampedValue = MAX(dB / VolumeClamp, -1) + 1;
|
||||
totalCount++;
|
||||
rollingAvg = (rollingAvg * (totalCount - 1) + clampedValue) / totalCount;
|
||||
}
|
||||
};
|
||||
|
||||
// While there are samples to read and the number of samples above the decibel threshold
|
||||
// is less than the total number of allowed samples over the limit, keep going
|
||||
[reader startReading];
|
||||
while (reader.status == AVAssetReaderStatusReading) {
|
||||
|
||||
AVAssetReaderTrackOutput *trackOutput = (AVAssetReaderTrackOutput *)[reader.outputs objectAtIndex:0];
|
||||
CMSampleBufferRef sampleBufferRef = [trackOutput copyNextSampleBuffer];
|
||||
|
||||
if (sampleBufferRef) {
|
||||
CMBlockBufferRef blockBufferRef = CMSampleBufferGetDataBuffer(sampleBufferRef);
|
||||
size_t length = CMBlockBufferGetDataLength(blockBufferRef);
|
||||
|
||||
NSMutableData *data = [NSMutableData dataWithLength:length];
|
||||
CMBlockBufferCopyDataBytes(blockBufferRef, 0, length, data.mutableBytes);
|
||||
|
||||
SInt16 *samples = (SInt16 *) data.mutableBytes;
|
||||
UInt64 sampleCount = length / bytesPerSample;
|
||||
for (UInt32 i = 0; i < sampleCount ; i++) {
|
||||
Float32 left = (Float32) *samples++;
|
||||
processVolume(left);
|
||||
if (channelCount == 2) {
|
||||
Float32 right = (Float32) *samples++;
|
||||
processVolume(right);
|
||||
}
|
||||
}
|
||||
|
||||
CMSampleBufferInvalidate(sampleBufferRef);
|
||||
CFRelease(sampleBufferRef);
|
||||
}
|
||||
}
|
||||
|
||||
return rollingAvg > VolumeThreshold;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -29,8 +29,9 @@
|
||||
*/
|
||||
|
||||
|
||||
@import UIKit;
|
||||
@import AVFoundation;
|
||||
#import <ResearchKit/ORKRecorder.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -30,10 +30,10 @@
|
||||
|
||||
|
||||
#import "ORKAudioRecorder.h"
|
||||
#import "ORKHelpers.h"
|
||||
|
||||
#import "ORKRecorder_Internal.h"
|
||||
#import "ORKRecorder_Private.h"
|
||||
#import "ORKDefines_Private.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
|
||||
|
||||
@interface ORKAudioRecorder ()
|
||||
@@ -42,6 +42,8 @@
|
||||
|
||||
@property (nonatomic, copy) NSDictionary *recorderSettings;
|
||||
|
||||
@property (nonatomic, copy) NSString *savedSessionCategory;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -79,6 +81,16 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)restoreSavedAudioSessionCategory {
|
||||
if (_savedSessionCategory) {
|
||||
NSError *error;
|
||||
if (![[AVAudioSession sharedInstance] setCategory:_savedSessionCategory error:&error]) {
|
||||
ORK_Log_Error(@"Failed to restore the audio session category: %@", [error localizedDescription]);
|
||||
}
|
||||
_savedSessionCategory = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)start {
|
||||
if (self.outputDirectory == nil) {
|
||||
@throw [NSException exceptionWithName:NSDestinationInvalidException reason:@"audioRecorder requires an output directory" userInfo:nil];
|
||||
@@ -95,6 +107,7 @@
|
||||
|
||||
|
||||
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
|
||||
_savedSessionCategory = audioSession.category;
|
||||
if (![audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&error]) {
|
||||
[self finishRecordingWithError:error];
|
||||
return;
|
||||
@@ -191,12 +204,13 @@
|
||||
|
||||
[self applyFileProtection:ORKFileProtectionComplete toFileAtURL:[self recordingFileURL]];
|
||||
#endif
|
||||
[self restoreSavedAudioSessionCategory];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)finishRecordingWithError:(NSError *)error {
|
||||
[self doStopRecording];
|
||||
|
||||
|
||||
[super finishRecordingWithError:error];
|
||||
}
|
||||
|
||||
@@ -231,7 +245,7 @@
|
||||
return [[self recordingDirectoryURL] URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", [self logName], [self extension]]];
|
||||
}
|
||||
|
||||
- (BOOL)recreateFileWithError:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)recreateFileWithError:(NSError **)error {
|
||||
NSURL *url = [self recordingFileURL];
|
||||
if (!url) {
|
||||
if (error) {
|
||||
@@ -253,7 +267,7 @@
|
||||
}
|
||||
|
||||
[fileManager createFileAtPath:[url path] contents:nil attributes:nil];
|
||||
[fileManager setAttributes:@{NSFileProtectionKey : ORKFileProtectionFromMode(ORKFileProtectionCompleteUnlessOpen)} ofItemAtPath:[url path] error:error];
|
||||
[fileManager setAttributes:@{NSFileProtectionKey: ORKFileProtectionFromMode(ORKFileProtectionCompleteUnlessOpen)} ofItemAtPath:[url path] error:error];
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
*/
|
||||
|
||||
|
||||
@import Foundation;
|
||||
#import <ResearchKit/ORKActiveStep.h>
|
||||
|
||||
|
||||
|
||||
@@ -30,10 +30,13 @@
|
||||
|
||||
|
||||
#import "ORKAudioStep.h"
|
||||
|
||||
#import "ORKAudioStepViewController.h"
|
||||
#import "ORKHelpers.h"
|
||||
|
||||
#import "ORKStep_Private.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
|
||||
|
||||
@implementation ORKAudioStep
|
||||
|
||||
|
||||
@@ -29,7 +29,9 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit_Private.h>
|
||||
@import Foundation;
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
#import <ResearchKit/ORKActiveStepViewController.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
@@ -37,6 +39,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
ORK_CLASS_AVAILABLE
|
||||
@interface ORKAudioStepViewController : ORKActiveStepViewController
|
||||
|
||||
@property (nonatomic, assign) CGFloat alertThreshold;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -30,17 +30,22 @@
|
||||
|
||||
|
||||
#import "ORKAudioStepViewController.h"
|
||||
#import "ORKAudioContentView.h"
|
||||
#import "ORKActiveStepViewController_Internal.h"
|
||||
#import "ORKVerticalContainerView.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
#import "ORKActiveStepTimer.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import "ORKStep_Private.h"
|
||||
#import "ORKAudioStep.h"
|
||||
#import "ORKAudioRecorder.h"
|
||||
#import "ORKActiveStepView.h"
|
||||
#import "ORKAudioContentView.h"
|
||||
#import "ORKCustomStepView_Internal.h"
|
||||
#import "ORKVerticalContainerView.h"
|
||||
|
||||
#import "ORKActiveStepViewController_Internal.h"
|
||||
#import "ORKAudioRecorder.h"
|
||||
|
||||
#import "ORKAudioStep.h"
|
||||
#import "ORKStep_Private.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
|
||||
@import AVFoundation;
|
||||
|
||||
|
||||
@interface ORKAudioStepViewController ()
|
||||
@@ -66,11 +71,23 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setAlertThreshold:(CGFloat)alertThreshold {
|
||||
_alertThreshold = alertThreshold;
|
||||
if (self.isViewLoaded && alertThreshold > 0) {
|
||||
_audioContentView.alertThreshold = alertThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
// Do any additional setup after loading the view.
|
||||
_audioContentView = [ORKAudioContentView new];
|
||||
_audioContentView.timeLeft = self.audioStep.stepDuration;
|
||||
|
||||
if (self.alertThreshold > 0) {
|
||||
_audioContentView.alertThreshold = self.alertThreshold;
|
||||
}
|
||||
|
||||
self.activeStepView.activeCustomView = _audioContentView;
|
||||
}
|
||||
|
||||
@@ -115,9 +132,9 @@
|
||||
- (void)startNewTimerIfNeeded {
|
||||
if (!_timer) {
|
||||
NSTimeInterval duration = self.audioStep.stepDuration;
|
||||
__weak typeof(self) weakSelf = self;
|
||||
ORKWeakTypeOf(self) weakSelf = self;
|
||||
_timer = [[ORKActiveStepTimer alloc] initWithDuration:duration interval:duration / 100 runtime:0 handler:^(ORKActiveStepTimer *timer, BOOL finished) {
|
||||
typeof(self) strongSelf = weakSelf;
|
||||
ORKStrongTypeOf(self) strongSelf = weakSelf;
|
||||
[strongSelf doSample];
|
||||
if (finished) {
|
||||
[strongSelf finish];
|
||||
|
||||
@@ -29,10 +29,13 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
@import Foundation;
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
#import <ResearchKit/ORKActiveStep.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
The `ORKCountdownStep` class represents a step that displays a label and a
|
||||
countdown for a time equal to its duration.
|
||||
@@ -46,3 +49,5 @@ ORK_CLASS_AVAILABLE
|
||||
@interface ORKCountdownStep : ORKActiveStep
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
|
||||
#import "ORKCountdownStep.h"
|
||||
|
||||
#import "ORKCountdownStepViewController.h"
|
||||
|
||||
|
||||
|
||||
@@ -29,9 +29,13 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit_Private.h>
|
||||
@import Foundation;
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
#import <ResearchKit/ORKActiveStepViewController.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
The `ORKCountdownStepViewController` class represents the step view controller that corresponds to an `ORKCountdownStep`.
|
||||
|
||||
@@ -43,3 +47,5 @@ ORK_CLASS_AVAILABLE
|
||||
@interface ORKCountdownStepViewController : ORKActiveStepViewController
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -30,17 +30,22 @@
|
||||
|
||||
|
||||
#import "ORKCountdownStepViewController.h"
|
||||
#import "ORKCustomStepView_Internal.h"
|
||||
#import "ORKActiveStepViewController_internal.h"
|
||||
#import "ORKVerticalContainerView.h"
|
||||
#import "ORKStepViewController_Internal.h"
|
||||
|
||||
#import "ORKActiveStepTimer.h"
|
||||
#import "ORKResult.h"
|
||||
#import "ORKActiveStepView.h"
|
||||
#import "ORKCustomStepView_Internal.h"
|
||||
#import "ORKLabel.h"
|
||||
#import "ORKSubheadlineLabel.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import "ORKVerticalContainerView.h"
|
||||
|
||||
#import "ORKActiveStepViewController_Internal.h"
|
||||
#import "ORKStepViewController_Internal.h"
|
||||
|
||||
#import "ORKActiveStep.h"
|
||||
#import "ORKResult.h"
|
||||
|
||||
#import "ORKAccessibility.h"
|
||||
#import "ORKActiveStepView.h"
|
||||
#import "ORKHelpers_Internal.h"
|
||||
|
||||
|
||||
@interface ORKCountDownViewLabel : ORKLabel
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
@import Foundation;
|
||||
#import <ResearchKit/ORKTypes.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
@@ -65,6 +65,18 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@end
|
||||
|
||||
|
||||
@protocol ORKDataLoggerExtendedDelegate <ORKDataLoggerDelegate>
|
||||
|
||||
@optional
|
||||
/**
|
||||
Tells the delegate that the maximum current log file lifetime changed.
|
||||
@param dataLogger Source of this event.
|
||||
*/
|
||||
- (void)dataLoggerThresholdsDidChange:(ORKDataLogger *)dataLogger;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@class ORKLogFormatter;
|
||||
|
||||
/**
|
||||
@@ -101,6 +113,7 @@ ORK_CLASS_AVAILABLE
|
||||
*/
|
||||
+ (ORKDataLogger *)JSONDataLoggerWithDirectory:(NSURL *)url logName:(NSString *)logName delegate:(nullable id<ORKDataLoggerDelegate>)delegate;
|
||||
|
||||
+ (instancetype)new NS_UNAVAILABLE;
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/**
|
||||
@@ -166,7 +179,7 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@return `YES` if the enumeration was successful; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)enumerateLogs:(void (^)(NSURL *logFileUrl, BOOL *stop))block error:(NSError * __autoreleasing *)error;
|
||||
- (BOOL)enumerateLogs:(void (^)(NSURL *logFileUrl, BOOL *stop))block error:(NSError * _Nullable *)error;
|
||||
|
||||
/**
|
||||
Enumerates the URLs of completed log files not yet marked uploaded,
|
||||
@@ -181,7 +194,7 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@return `YES` if the enumeration was successful; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)enumerateLogsNeedingUpload:(void (^)(NSURL *logFileUrl, BOOL *stop))block error:(NSError * __autoreleasing *)error;
|
||||
- (BOOL)enumerateLogsNeedingUpload:(void (^)(NSURL *logFileUrl, BOOL *stop))block error:(NSError * _Nullable *)error;
|
||||
|
||||
/**
|
||||
Enumerates the URLs of completed log files not already marked uploaded,
|
||||
@@ -196,7 +209,7 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@return `YES` if the enumeration was successful; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)enumerateLogsAlreadyUploaded:(void (^)(NSURL *logFileUrl, BOOL *stop))block error:(NSError * __autoreleasing *)error;
|
||||
- (BOOL)enumerateLogsAlreadyUploaded:(void (^)(NSURL *logFileUrl, BOOL *stop))block error:(NSError * _Nullable *)error;
|
||||
|
||||
/**
|
||||
Appends an object to the log file, which is formatted with `logFormatter`.
|
||||
@@ -207,12 +220,12 @@ ORK_CLASS_AVAILABLE
|
||||
log data is made. If an attempt is made to log data and there is no access due
|
||||
to file protection, the log is immediately rolled over and a new file created.
|
||||
|
||||
@param object Should be an object of a class that is accepted by the logFormatter.
|
||||
@param error Error output, if the append fails.
|
||||
@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;
|
||||
- (BOOL)append:(id)object error:(NSError * _Nullable *)error;
|
||||
|
||||
/**
|
||||
Appends multiple objects to the log file.
|
||||
@@ -220,12 +233,12 @@ ORK_CLASS_AVAILABLE
|
||||
This method formats and appends all the objects at once. Using this method may have efficiency
|
||||
and atomicity gains for error handling, compared to making multiple calls to `append:error`.
|
||||
|
||||
@param objects An array of objects of a class that is accepted by the logFormatter.
|
||||
@param error Error output, if the append fails.
|
||||
@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;
|
||||
- (BOOL)appendObjects:(NSArray *)objects error:(NSError * _Nullable *)error;
|
||||
|
||||
/**
|
||||
Checks whether a file has been marked as uploaded.
|
||||
@@ -251,7 +264,7 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@return `YES` if adding or removing the attribute succeeded; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)markFileUploaded:(BOOL)uploaded atURL:(NSURL *)url error:(NSError * _Nullable __autoreleasing *)error;
|
||||
- (BOOL)markFileUploaded:(BOOL)uploaded atURL:(NSURL *)url error:(NSError * _Nullable *)error;
|
||||
|
||||
/**
|
||||
Removes files if they are marked uploaded.
|
||||
@@ -265,7 +278,7 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@return `YES` if removing the files succeeded; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)removeUploadedFiles:(NSArray<NSURL *> *)fileURLs withError:(NSError * _Nullable __autoreleasing *)error;
|
||||
- (BOOL)removeUploadedFiles:(NSArray<NSURL *> *)fileURLs withError:(NSError * _Nullable *)error;
|
||||
|
||||
/**
|
||||
Removes all files managed by this logger (files that have the `logName` prefix).
|
||||
@@ -274,7 +287,7 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@return `YES` if removing the files succeeded.; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)removeAllFilesWithError:(NSError *_Nullable __autoreleasing *)error;
|
||||
- (BOOL)removeAllFilesWithError:(NSError * _Nullable *)error;
|
||||
|
||||
@end
|
||||
|
||||
@@ -320,7 +333,7 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@return `YES` if the write succeeds; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)beginLogWithFileHandle:(NSFileHandle *)fileHandle error:(NSError * _Nullable __autoreleasing *)error;
|
||||
- (BOOL)beginLogWithFileHandle:(NSFileHandle *)fileHandle error:(NSError * _Nullable *)error;
|
||||
|
||||
/**
|
||||
Appends the specified object to the log file.
|
||||
@@ -331,7 +344,7 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@return `YES` if the write succeeds; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)appendObject:(id)object fileHandle:(NSFileHandle *)fileHandle error:(NSError * _Nullable __autoreleasing *)error;
|
||||
- (BOOL)appendObject:(id)object fileHandle:(NSFileHandle *)fileHandle error:(NSError * _Nullable *)error;
|
||||
|
||||
/**
|
||||
Appends the specified objects to the log file.
|
||||
@@ -342,7 +355,7 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@return `YES` if the write succeeds; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)appendObjects:(NSArray *)objects fileHandle:(NSFileHandle *)fileHandle error:(NSError * _Nullable __autoreleasing *)error;
|
||||
- (BOOL)appendObjects:(NSArray *)objects fileHandle:(NSFileHandle *)fileHandle error:(NSError * _Nullable *)error;
|
||||
|
||||
@end
|
||||
|
||||
@@ -425,6 +438,7 @@ ORK_CLASS_AVAILABLE
|
||||
ORK_CLASS_AVAILABLE
|
||||
@interface ORKDataLoggerManager : NSObject <ORKDataLoggerDelegate>
|
||||
|
||||
+ (instancetype)new NS_UNAVAILABLE;
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/**
|
||||
@@ -505,7 +519,7 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@return `YES` if the enumeration succeeds; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)enumerateLogsNeedingUpload:(void (^)(ORKDataLogger *dataLogger, NSURL *logFileUrl, BOOL *stop))block error:(NSError * __autoreleasing *)error;
|
||||
- (BOOL)enumerateLogsNeedingUpload:(void (^)(ORKDataLogger *dataLogger, NSURL *logFileUrl, BOOL *stop))block error:(NSError * _Nullable *)error;
|
||||
|
||||
/**
|
||||
Unmarks the set of uploaded files.
|
||||
@@ -518,7 +532,7 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@return `YES` if the operation succeeds; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)unmarkUploadedFiles:(NSArray<NSURL *> *)fileURLs error:(NSError * _Nullable __autoreleasing *)error;
|
||||
- (BOOL)unmarkUploadedFiles:(NSArray<NSURL *> *)fileURLs error:(NSError * _Nullable *)error;
|
||||
|
||||
/**
|
||||
Removes a set of uploaded files.
|
||||
@@ -532,7 +546,7 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@return `YES` if the operation succeeds; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)removeUploadedFiles:(NSArray<NSURL *> *)fileURLs error:(NSError * _Nullable __autoreleasing *)error;
|
||||
- (BOOL)removeUploadedFiles:(NSArray<NSURL *> *)fileURLs error:(NSError * _Nullable *)error;
|
||||
|
||||
/**
|
||||
Removes old and uploaded logs to bring total bytes down to a threshold.
|
||||
@@ -545,7 +559,23 @@ ORK_CLASS_AVAILABLE
|
||||
|
||||
@return `YES` if the operation succeeds; otherwise, `NO`.
|
||||
*/
|
||||
- (BOOL)removeOldAndUploadedLogsToThreshold:(unsigned long long)bytes error:(NSError * _Nullable __autoreleasing *)error;
|
||||
- (BOOL)removeOldAndUploadedLogsToThreshold:(unsigned long long)bytes error:(NSError * _Nullable *)error;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface ORKDataLogger (Tests)
|
||||
|
||||
/// The file handle to which to write
|
||||
- (nullable NSFileHandle *)fileHandle;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface NSURL (ORKDataLogger)
|
||||
|
||||
- (BOOL)ork_isUploaded;
|
||||
- (BOOL)ork_setUploaded:(BOOL)uploaded error:(NSError * _Nullable *)error;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -30,13 +30,12 @@
|
||||
|
||||
|
||||
#import "ORKDataLogger.h"
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
#import "ORKHelpers.h"
|
||||
#include <sys/xattr.h>
|
||||
#import "ORKDataLogger_Private.h"
|
||||
#import "HKSample+ORKJSONDictionary.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
#import "CMMotionActivity+ORKJSONDictionary.h"
|
||||
#import "ORKDefines_Private.h"
|
||||
#import "HKSample+ORKJSONDictionary.h"
|
||||
|
||||
#include <sys/xattr.h>
|
||||
|
||||
|
||||
static const char *ORKDataLoggerUploadedAttr = "com.apple.ResearchKit.uploaded";
|
||||
@@ -109,7 +108,7 @@ static NSString *const ORKDataLoggerManagerConfigurationFilename = @".ORKDataLog
|
||||
return (string.integerValue != 0);
|
||||
}
|
||||
|
||||
- (BOOL)ork_setUploaded:(BOOL)uploaded error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)ork_setUploaded:(BOOL)uploaded error:(NSError **)error {
|
||||
NSString *value = (uploaded ? @"1" : @"0");
|
||||
NSData *encodedString = [value dataUsingEncoding:NSUTF8StringEncoding];
|
||||
return [self ork_setData:encodedString forAttr:ORKDataLoggerUploadedAttr error:error];
|
||||
@@ -133,12 +132,12 @@ static NSString *const ORKDataLoggerManagerConfigurationFilename = @".ORKDataLog
|
||||
return data;
|
||||
}
|
||||
|
||||
- (BOOL)ork_setData:(NSData *)data forAttr:(const char *)attr error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)ork_setData:(NSData *)data forAttr:(const char *)attr error:(NSError **)error {
|
||||
const char *path = [self fileSystemRepresentation];
|
||||
int rc = setxattr(path, attr, data.bytes, data.length, 0, 0);
|
||||
if (rc != 0) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:NSCocoaErrorDomain code:rc userInfo:@{NSLocalizedDescriptionKey : ORKLocalizedString(@"ERROR_DATALOGGER_SET_ATTRIBUTE", nil)}];
|
||||
*error = [NSError errorWithDomain:NSCocoaErrorDomain code:rc userInfo:@{NSLocalizedDescriptionKey: ORKLocalizedString(@"ERROR_DATALOGGER_SET_ATTRIBUTE", nil)}];
|
||||
}
|
||||
}
|
||||
return (rc == 0);
|
||||
@@ -239,11 +238,11 @@ static void *ORKObjectObserverContext = &ORKObjectObserverContext;
|
||||
return [object isKindOfClass:[NSData class]];
|
||||
}
|
||||
|
||||
- (BOOL)beginLogWithFileHandle:(NSFileHandle *)fileHandle error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)beginLogWithFileHandle:(NSFileHandle *)fileHandle error:(NSError **)error {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)writeData:(NSData *)data fileHandle:(NSFileHandle *)fileHandle error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)writeData:(NSData *)data fileHandle:(NSFileHandle *)fileHandle error:(NSError **)error {
|
||||
BOOL result = YES;
|
||||
@try {
|
||||
[fileHandle writeData:data];
|
||||
@@ -251,7 +250,7 @@ static void *ORKObjectObserverContext = &ORKObjectObserverContext;
|
||||
@catch (NSException *exception) {
|
||||
result = NO;
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ORKErrorDomain code:ORKErrorException userInfo:@{@"exception" : exception}];
|
||||
*error = [NSError errorWithDomain:ORKErrorDomain code:ORKErrorException userInfo:@{@"exception": exception}];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -266,14 +265,14 @@ static void *ORKObjectObserverContext = &ORKObjectObserverContext;
|
||||
[fileHandle truncateFileAtOffset:offset];
|
||||
}
|
||||
|
||||
- (BOOL)appendObject:(id)object fileHandle:(NSFileHandle *)fileHandle error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)appendObject:(id)object fileHandle:(NSFileHandle *)fileHandle error:(NSError **)error {
|
||||
if (![self canAcceptLogObject:object]) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"ORKLogFormatter accepts NSData only" userInfo:nil];
|
||||
}
|
||||
return [self writeData:(NSData *)object fileHandle:fileHandle error:error];
|
||||
}
|
||||
|
||||
- (BOOL)appendObjects:(NSArray *)objects fileHandle:(NSFileHandle *)fileHandle error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)appendObjects:(NSArray *)objects fileHandle:(NSFileHandle *)fileHandle error:(NSError **)error {
|
||||
unsigned long long checkpoint = [self checkpointWithFileHandle:fileHandle];
|
||||
|
||||
NSError *errorOut = nil;
|
||||
@@ -327,7 +326,7 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
return [object isKindOfClass:[NSDictionary class]] && [NSJSONSerialization isValidJSONObject:object];
|
||||
}
|
||||
|
||||
- (BOOL)beginLogWithFileHandle:(NSFileHandle *)fileHandle error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)beginLogWithFileHandle:(NSFileHandle *)fileHandle error:(NSError **)error {
|
||||
// Write valid JSON containing no objects
|
||||
NSData *data = [kJSONLogEmptyLogString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
return [self writeData:data fileHandle:fileHandle error:error];
|
||||
@@ -348,7 +347,7 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)appendObject:(id)object fileHandle:(NSFileHandle *)fileHandle error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)appendObject:(id)object fileHandle:(NSFileHandle *)fileHandle error:(NSError **)error {
|
||||
return [self appendObjects:@[object] fileHandle:fileHandle error:error];
|
||||
}
|
||||
|
||||
@@ -360,7 +359,7 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
* before writing. When writing, we write a separator (if needed), the JSON
|
||||
* object being appended, and the footer bytes.
|
||||
*/
|
||||
- (BOOL)appendObjects:(NSArray *)objects fileHandle:(NSFileHandle *)fileHandle error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)appendObjects:(NSArray *)objects fileHandle:(NSFileHandle *)fileHandle error:(NSError **)error {
|
||||
if (!fileHandle) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Filehandle is nil" userInfo:nil];
|
||||
}
|
||||
@@ -446,10 +445,12 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
return [[ORKDataLogger alloc] initWithDirectory:url logName:logName formatter:[ORKJSONLogFormatter new] delegate:delegate];
|
||||
}
|
||||
|
||||
+ (instancetype)new {
|
||||
ORKThrowMethodUnavailableException();
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
ORKThrowMethodUnavailableException();
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (instancetype)initWithDirectory:(NSURL *)url logName:(NSString *)logName formatter:(ORKLogFormatter *)formatter delegate:(id<ORKDataLoggerDelegate>)delegate {
|
||||
@@ -490,7 +491,7 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
@throw [NSException exceptionWithName:NSGenericException reason:[NSString stringWithFormat:@"%@ is not a class", configuration[@"formatterClass"]] userInfo:nil];
|
||||
}
|
||||
|
||||
self = [self initWithDirectory:url logName:configuration[@"logName"] formatter:[formatterClass new] delegate:delegate];
|
||||
self = [self initWithDirectory:url logName:configuration[@"logName"] formatter:[[formatterClass alloc] init] delegate:delegate];
|
||||
if (self) {
|
||||
// Don't notify about initial setup
|
||||
[_observer pause];
|
||||
@@ -502,11 +503,11 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
}
|
||||
|
||||
- (NSDictionary *)configuration {
|
||||
return @{@"logName" : self.logName,
|
||||
@"formatterClass" : NSStringFromClass([self.logFormatter class]),
|
||||
@"fileProtectionMode" : @(self.fileProtectionMode),
|
||||
@"maximumCurrentLogFileSize" : @(self.maximumCurrentLogFileSize),
|
||||
@"maximumCurrentLogFileLifetime" : @(self.maximumCurrentLogFileLifetime)
|
||||
return @{@"logName": self.logName,
|
||||
@"formatterClass": NSStringFromClass([self.logFormatter class]),
|
||||
@"fileProtectionMode": @(self.fileProtectionMode),
|
||||
@"maximumCurrentLogFileSize": @(self.maximumCurrentLogFileSize),
|
||||
@"maximumCurrentLogFileLifetime": @(self.maximumCurrentLogFileLifetime)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -527,9 +528,9 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
|
||||
if (_directorySource) {
|
||||
dispatch_source_set_cancel_handler(_directorySource, ^{ close(dirFD); });
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
ORKWeakTypeOf(self) weakSelf = self;
|
||||
dispatch_source_set_event_handler(_directorySource, ^{
|
||||
__strong __typeof(self) strongSelf = weakSelf;
|
||||
ORKStrongTypeOf(self) strongSelf = weakSelf;
|
||||
[strongSelf directoryUpdated];
|
||||
});
|
||||
dispatch_resume(_directorySource);
|
||||
@@ -570,7 +571,7 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
return _currentFileHandle;
|
||||
}
|
||||
|
||||
- (BOOL)enumerateLogs:(void (^)(NSURL *logFileUrl, BOOL *stop))block error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)enumerateLogs:(void (^)(NSURL *logFileUrl, BOOL *stop))block error:(NSError **)error {
|
||||
if (!block) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Block parameter is required" userInfo:nil];
|
||||
}
|
||||
@@ -582,7 +583,7 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
return success;
|
||||
}
|
||||
|
||||
- (BOOL)enumerateLogsUploaded:(BOOL)uploaded block:(void (^)(NSURL *logFileUrl, BOOL *stop))block error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)enumerateLogsUploaded:(BOOL)uploaded block:(void (^)(NSURL *logFileUrl, BOOL *stop))block error:(NSError **)error {
|
||||
if (!block) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Block parameter is required" userInfo:nil];
|
||||
}
|
||||
@@ -594,15 +595,15 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
return success;
|
||||
}
|
||||
|
||||
- (BOOL)enumerateLogsNeedingUpload:(void (^)(NSURL *logFileUrl, BOOL *stop))block error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)enumerateLogsNeedingUpload:(void (^)(NSURL *logFileUrl, BOOL *stop))block error:(NSError **)error {
|
||||
return [self enumerateLogsUploaded:NO block:block error:error];
|
||||
}
|
||||
|
||||
- (BOOL)enumerateLogsAlreadyUploaded:(void (^)(NSURL *logFileUrl, BOOL *stop))block error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)enumerateLogsAlreadyUploaded:(void (^)(NSURL *logFileUrl, BOOL *stop))block error:(NSError **)error {
|
||||
return [self enumerateLogsUploaded:YES block:block error:error];
|
||||
}
|
||||
|
||||
- (BOOL)append:(id)object error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)append:(id)object error:(NSError **)error {
|
||||
if (!object) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Nil object" userInfo:nil];
|
||||
}
|
||||
@@ -613,7 +614,7 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
return success;
|
||||
}
|
||||
|
||||
- (BOOL)appendObjects:(NSArray *)objects error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)appendObjects:(NSArray *)objects error:(NSError **)error {
|
||||
if (!objects.count) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Empty array" userInfo:nil];
|
||||
}
|
||||
@@ -624,7 +625,7 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
return success;
|
||||
}
|
||||
|
||||
- (BOOL)markFileUploaded:(BOOL)uploaded atURL:(NSURL *)url error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)markFileUploaded:(BOOL)uploaded atURL:(NSURL *)url error:(NSError **)error {
|
||||
__block BOOL success = NO;
|
||||
dispatch_sync(_queue, ^{
|
||||
success = [self queue_markFileUploaded:uploaded atURL:url error:error];
|
||||
@@ -632,7 +633,7 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
return success;
|
||||
}
|
||||
|
||||
- (BOOL)removeUploadedFiles:(NSArray<NSURL *> *)fileURLs withError:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)removeUploadedFiles:(NSArray<NSURL *> *)fileURLs withError:(NSError **)error {
|
||||
__block BOOL success = NO;
|
||||
dispatch_sync(_queue, ^{
|
||||
success = [self queue_removeUploadedFiles:fileURLs withError:error];
|
||||
@@ -640,7 +641,7 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
return success;
|
||||
}
|
||||
|
||||
- (BOOL)removeAllFilesWithError:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)removeAllFilesWithError:(NSError **)error {
|
||||
__block BOOL success = NO;
|
||||
dispatch_sync(_queue, ^{
|
||||
success = [self queue_removeAllFilesWithError:error];
|
||||
@@ -686,7 +687,7 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
});
|
||||
}
|
||||
|
||||
- (BOOL)queue_enumerateLogs:(void (^)(NSURL *logFileUrl, BOOL *stop))block error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)queue_enumerateLogs:(void (^)(NSURL *logFileUrl, BOOL *stop))block error:(NSError **)error {
|
||||
static NSArray *keys = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
@@ -744,7 +745,7 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
return (errorOut ? NO : YES);
|
||||
}
|
||||
|
||||
- (BOOL)queue_enumerateLogsUploaded:(BOOL)uploaded block:(void (^)(NSURL *logFileUrl, BOOL *stop))block error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)queue_enumerateLogsUploaded:(BOOL)uploaded block:(void (^)(NSURL *logFileUrl, BOOL *stop))block error:(NSError **)error {
|
||||
return [self queue_enumerateLogs:^(NSURL *logFileUrl, BOOL *stop) {
|
||||
NSError *errorOut = nil;
|
||||
BOOL wantUploaded = [logFileUrl ork_isUploaded];
|
||||
@@ -758,7 +759,7 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
} error:error];
|
||||
}
|
||||
|
||||
- (NSFileHandle *)queue_makeFileHandleWithError:(NSError * __autoreleasing *)error {
|
||||
- (NSFileHandle *)queue_makeFileHandleWithError:(NSError **)error {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *url = [self currentLogFileURL];
|
||||
|
||||
@@ -784,7 +785,7 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
BOOL success = [fileManager createFileAtPath:filePath contents:nil attributes:nil];
|
||||
if (!success) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileNoSuchFileError userInfo:@{NSLocalizedDescriptionKey : ORKLocalizedString(@"ERROR_DATALOGGER_CREATE_FILE", nil)}];
|
||||
*error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileNoSuchFileError userInfo:@{NSLocalizedDescriptionKey: ORKLocalizedString(@"ERROR_DATALOGGER_CREATE_FILE", nil)}];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
@@ -799,7 +800,7 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
assert(fileHandle);
|
||||
|
||||
// Set file protection after opening the file, so that class B works as expected.
|
||||
BOOL success = [fileManager setAttributes:@{NSFileProtectionKey : ORKFileProtectionFromMode(self.fileProtectionMode)} ofItemAtPath:[url path] error:error];
|
||||
BOOL success = [fileManager setAttributes:@{NSFileProtectionKey: ORKFileProtectionFromMode(self.fileProtectionMode)} ofItemAtPath:[url path] error:error];
|
||||
|
||||
// Allow formatter to initialize the log file with header content
|
||||
success = success && [self.logFormatter beginLogWithFileHandle:fileHandle error:error];
|
||||
@@ -814,7 +815,7 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
return _currentFileHandle;
|
||||
}
|
||||
|
||||
- (NSFileHandle *)queue_fileHandleWithError:(NSError * __autoreleasing *)error {
|
||||
- (NSFileHandle *)queue_fileHandleWithError:(NSError **)error {
|
||||
if (!_currentFileHandle) {
|
||||
_currentFileHandle = [self queue_makeFileHandleWithError:error];
|
||||
|
||||
@@ -824,15 +825,15 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
}
|
||||
|
||||
+ (NSURL *)nextUrlForDirectoryUrl:(NSURL *)directory logName:(NSString *)logName {
|
||||
static NSDateFormatter *dfm = nil;
|
||||
static NSDateFormatter *dateFromatter = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
dfm = [NSDateFormatter new];
|
||||
[dfm setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
|
||||
dfm.dateFormat = @"yyyyMMddHHmmss";
|
||||
dateFromatter = [NSDateFormatter new];
|
||||
[dateFromatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
|
||||
dateFromatter.dateFormat = @"yyyyMMddHHmmss";
|
||||
});
|
||||
|
||||
NSString *datedLog = [NSString stringWithFormat:@"%@-%@",logName, [dfm stringFromDate:[NSDate date]]];
|
||||
NSString *datedLog = [NSString stringWithFormat:@"%@-%@",logName, [dateFromatter stringFromDate:[NSDate date]]];
|
||||
NSURL *destinationUrl = [directory URLByAppendingPathComponent:datedLog];
|
||||
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
@@ -868,8 +869,8 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
if (self.fileProtectionMode == ORKFileProtectionCompleteUnlessOpen) {
|
||||
// Upgrade to complete file protection after roll-over
|
||||
NSError *error = nil;
|
||||
if (![fileManager setAttributes:@{NSFileProtectionKey : NSFileProtectionComplete}
|
||||
ofItemAtPath:[destinationUrl path] error:&error]) {
|
||||
if (![fileManager setAttributes:@{NSFileProtectionKey: NSFileProtectionComplete}
|
||||
ofItemAtPath:[destinationUrl path] error:&error]) {
|
||||
ORK_Log_Warning(@"Error setting NSFileProtectionComplete on %@: %@", destinationUrl, error);
|
||||
}
|
||||
}
|
||||
@@ -907,7 +908,7 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
[self queue_closeAndRenameLog];
|
||||
}
|
||||
|
||||
- (BOOL)queue_append:(id)object error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)queue_append:(id)object error:(NSError **)error {
|
||||
[self queue_rolloverIfNeeded];
|
||||
|
||||
NSFileHandle *fileHandle = [self queue_fileHandleWithError:error];
|
||||
@@ -925,7 +926,7 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)queue_appendObjects:(NSArray *)objects error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)queue_appendObjects:(NSArray *)objects error:(NSError **)error {
|
||||
[self queue_rolloverIfNeeded];
|
||||
|
||||
NSFileHandle *fileHandle = [self queue_fileHandleWithError:error];
|
||||
@@ -942,13 +943,13 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)queue_markFileUploaded:(BOOL)uploaded atURL:(NSURL *)url error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)queue_markFileUploaded:(BOOL)uploaded atURL:(NSURL *)url error:(NSError **)error {
|
||||
BOOL success = [url ork_setUploaded:uploaded error:error];
|
||||
[self queue_setNeedsUpdateBytes];
|
||||
return success;
|
||||
}
|
||||
|
||||
- (BOOL)queue_removeUploadedFiles:(NSArray<NSURL *> *)fileURLs withError:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)queue_removeUploadedFiles:(NSArray<NSURL *> *)fileURLs withError:(NSError **)error {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
__block NSMutableArray *errors = [NSMutableArray array];
|
||||
BOOL success = [self queue_enumerateLogs:^(NSURL *logFileUrl, BOOL *stop) {
|
||||
@@ -962,7 +963,9 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
}
|
||||
} else {
|
||||
// File was requested to be removed, but was not marked uploaded
|
||||
[errors addObject:[NSError errorWithDomain:ORKErrorDomain code:ORKErrorInvalidObject userInfo:@{NSLocalizedDescriptionKey : ORKLocalizedString(@"ERROR_DATALOGGER_COULD_NOT_MAORK", nil), @"url" : logFileUrl}]];
|
||||
[errors addObject:[NSError errorWithDomain:ORKErrorDomain
|
||||
code:ORKErrorInvalidObject
|
||||
userInfo:@{NSLocalizedDescriptionKey: ORKLocalizedString(@"ERROR_DATALOGGER_COULD_NOT_MAORK", nil), @"url": logFileUrl}]];
|
||||
}
|
||||
}
|
||||
} error:error];
|
||||
@@ -971,14 +974,16 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
if (errors.count) {
|
||||
if (!success && error && *error) {
|
||||
[errors addObject:*error];
|
||||
*error = [NSError errorWithDomain:ORKErrorDomain code:ORKErrorMultipleErrors userInfo:@{NSLocalizedDescriptionKey : ORKLocalizedString(@"ERROR_DATALOGGER_MULTIPLE", nil), @"errors" : errors}];
|
||||
*error = [NSError errorWithDomain:ORKErrorDomain
|
||||
code:ORKErrorMultipleErrors
|
||||
userInfo:@{NSLocalizedDescriptionKey: ORKLocalizedString(@"ERROR_DATALOGGER_MULTIPLE", nil), @"errors": errors}];
|
||||
}
|
||||
success = NO;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
- (BOOL)queue_removeAllFilesWithError:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)queue_removeAllFilesWithError:(NSError **)error {
|
||||
[_currentFileHandle closeFile];
|
||||
_currentFileHandle = nil;
|
||||
|
||||
@@ -1042,10 +1047,12 @@ static NSInteger _ORKJSON_terminatorLength = 0;
|
||||
|
||||
@implementation ORKDataLoggerManager
|
||||
|
||||
+ (instancetype)new {
|
||||
ORKThrowMethodUnavailableException();
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
ORKThrowMethodUnavailableException();
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (instancetype)initWithDirectory:(NSURL *)directory delegate:(id<ORKDataLoggerManagerDelegate>)delegate {
|
||||
@@ -1094,9 +1101,9 @@ static NSString *const LoggerConfigurationsKey = @"loggers";
|
||||
- (NSDictionary *)queue_configuration {
|
||||
NSMutableArray *loggerConfigurations = [_records.allValues valueForKey:@"configuration"];
|
||||
|
||||
return @{PendingUploadBytesThresholdKey : @(self.pendingUploadBytesThreshold),
|
||||
TotalBytesThresholdKey : @(self.totalBytesThreshold),
|
||||
LoggerConfigurationsKey : loggerConfigurations };
|
||||
return @{PendingUploadBytesThresholdKey: @(self.pendingUploadBytesThreshold),
|
||||
TotalBytesThresholdKey: @(self.totalBytesThreshold),
|
||||
LoggerConfigurationsKey: loggerConfigurations };
|
||||
}
|
||||
|
||||
- (void)queue_synchronizeConfiguration {
|
||||
@@ -1175,7 +1182,7 @@ static NSString *const LoggerConfigurationsKey = @"loggers";
|
||||
return logNames;
|
||||
}
|
||||
|
||||
- (BOOL)queue_enumerateLogsNeedingUpload:(void (^)(ORKDataLogger *dataLogger, NSURL *logFileUrl, BOOL *stop))block error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)queue_enumerateLogsNeedingUpload:(void (^)(ORKDataLogger *dataLogger, NSURL *logFileUrl, BOOL *stop))block error:(NSError **)error {
|
||||
BOOL success = YES;
|
||||
NSMutableArray *allFiles = [NSMutableArray array];
|
||||
// Collect all the log file URLs so we can sort them by date rather than enumerating by logger.
|
||||
@@ -1214,7 +1221,7 @@ static NSString *const LoggerConfigurationsKey = @"loggers";
|
||||
return success;
|
||||
}
|
||||
|
||||
- (BOOL)enumerateLogsNeedingUpload:(void (^)(ORKDataLogger *dataLogger, NSURL *logFileUrl, BOOL *stop))block error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)enumerateLogsNeedingUpload:(void (^)(ORKDataLogger *dataLogger, NSURL *logFileUrl, BOOL *stop))block error:(NSError **)error {
|
||||
if (!block) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Block argument required" userInfo:nil];
|
||||
}
|
||||
@@ -1226,7 +1233,7 @@ static NSString *const LoggerConfigurationsKey = @"loggers";
|
||||
return success;
|
||||
}
|
||||
|
||||
- (BOOL)queue_removeUploadedFiles:(NSArray<NSURL *> *)fileURLs error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)queue_removeUploadedFiles:(NSArray<NSURL *> *)fileURLs error:(NSError **)error {
|
||||
BOOL success = YES;
|
||||
NSMutableArray *notRemoved = [NSMutableArray array];
|
||||
for (NSURL *url in fileURLs) {
|
||||
@@ -1249,7 +1256,7 @@ static NSString *const LoggerConfigurationsKey = @"loggers";
|
||||
return success;
|
||||
}
|
||||
|
||||
- (BOOL)removeUploadedFiles:(NSArray<NSURL *> *)fileURLs error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)removeUploadedFiles:(NSArray<NSURL *> *)fileURLs error:(NSError **)error {
|
||||
|
||||
__block BOOL success = YES;
|
||||
dispatch_sync(_queue, ^{
|
||||
@@ -1258,7 +1265,7 @@ static NSString *const LoggerConfigurationsKey = @"loggers";
|
||||
return success;
|
||||
}
|
||||
|
||||
- (BOOL)queue_unmarkUploadedFiles:(NSArray<NSURL *> *)fileURLs error:(NSError * __autoreleasing *)error {
|
||||
- (BOOL)queue_unmarkUploadedFiles:(NSArray<NSURL *> *)fileURLs error:(NSError **)error {
|
||||
BOOL success = YES;
|
||||
NSMutableArray<NSURL *> *notRemoved = [NSMutableArray array];
|
||||
for (NSURL *url in fileURLs) {
|
||||
@@ -1281,7 +1288,7 @@ static NSString *const LoggerConfigurationsKey = @"loggers";
|
||||
return success;
|
||||
}
|
||||
|
||||
- (BOOL)unmarkUploadedFiles:(NSArray<NSURL *> *)fileURLs error:(NSError *__autoreleasing *)error {
|
||||
- (BOOL)unmarkUploadedFiles:(NSArray<NSURL *> *)fileURLs error:(NSError **)error {
|
||||
__block BOOL success = YES;
|
||||
dispatch_sync(_queue, ^{
|
||||
success = [self queue_unmarkUploadedFiles:fileURLs error:error];
|
||||
@@ -1289,7 +1296,7 @@ static NSString *const LoggerConfigurationsKey = @"loggers";
|
||||
return success;
|
||||
}
|
||||
|
||||
- (BOOL)queue_removeOldAndUploadedLogsToThreshold:(unsigned long long)bytes error:(NSError *__autoreleasing *)error {
|
||||
- (BOOL)queue_removeOldAndUploadedLogsToThreshold:(unsigned long long)bytes error:(NSError **)error {
|
||||
if (bytes == 0) {
|
||||
for (ORKDataLogger *logger in _records) {
|
||||
[logger removeAllFilesWithError:nil];
|
||||
@@ -1346,7 +1353,7 @@ static NSString *const LoggerConfigurationsKey = @"loggers";
|
||||
return (totalBytes <= bytes);
|
||||
}
|
||||
|
||||
- (BOOL)removeOldAndUploadedLogsToThreshold:(unsigned long long)bytes error:(NSError *__autoreleasing *)error {
|
||||
- (BOOL)removeOldAndUploadedLogsToThreshold:(unsigned long long)bytes error:(NSError **)error {
|
||||
__block BOOL success = YES;
|
||||
dispatch_sync(_queue, ^{
|
||||
success = [self queue_removeOldAndUploadedLogsToThreshold:bytes error:error];
|
||||
|
||||
@@ -28,8 +28,11 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
@import UIKit;
|
||||
#import <ResearchKit/ORKRecorder.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class CMDeviceMotion;
|
||||
|
||||
@@ -30,13 +30,16 @@
|
||||
|
||||
|
||||
#import "ORKDeviceMotionRecorder.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import "ORKRecorder_Internal.h"
|
||||
#import "ORKRecorder_Private.h"
|
||||
|
||||
#import "ORKDataLogger.h"
|
||||
#import <CoreMotion/CoreMotion.h>
|
||||
|
||||
#import "ORKRecorder_Internal.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
#import "CMDeviceMotion+ORKJSONDictionary.h"
|
||||
|
||||
@import CoreMotion;
|
||||
|
||||
|
||||
@interface ORKDeviceMotionRecorder () {
|
||||
ORKDataLogger *_logger;
|
||||
@@ -85,10 +88,10 @@
|
||||
[super start];
|
||||
|
||||
if (!_logger) {
|
||||
NSError *err = nil;
|
||||
_logger = [self makeJSONDataLoggerWithError:&err];
|
||||
NSError *error = nil;
|
||||
_logger = [self makeJSONDataLoggerWithError:&error];
|
||||
if (!_logger) {
|
||||
[self finishRecordingWithError:err];
|
||||
[self finishRecordingWithError:error];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,12 +30,16 @@
|
||||
|
||||
|
||||
#import "ORKFitnessContentView.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import <CoreMotion/CoreMotion.h>
|
||||
#import "ORKSkin.h"
|
||||
|
||||
#import "ORKActiveStepQuantityView.h"
|
||||
#import "ORKTintedImageView.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
#import "ORKSkin.h"
|
||||
|
||||
@import CoreMotion;
|
||||
@import HealthKit;
|
||||
|
||||
|
||||
// #define LAYOUT_TEST 1
|
||||
// #define LAYOUT_DEBUG 1
|
||||
@@ -325,19 +329,18 @@
|
||||
}
|
||||
|
||||
- (void)updateTimerLabel {
|
||||
static NSDateComponentsFormatter *_formatter = nil;
|
||||
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;
|
||||
formatter = [NSDateComponentsFormatter new];
|
||||
formatter.unitsStyle = NSDateComponentsFormatterUnitsStylePositional;
|
||||
formatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorPad;
|
||||
formatter.allowedUnits = NSCalendarUnitMinute | NSCalendarUnitSecond;
|
||||
});
|
||||
|
||||
NSString *s = [_formatter stringFromTimeInterval:MAX(round(_timeLeft),0)];
|
||||
_timerLabel.text = s;
|
||||
_timerLabel.hidden = (s == nil);
|
||||
NSString *labelString = [formatter stringFromTimeInterval:MAX(round(_timeLeft),0)];
|
||||
_timerLabel.text = labelString;
|
||||
_timerLabel.hidden = (labelString == nil);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -29,10 +29,13 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
@import Foundation;
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
#import <ResearchKit/ORKActiveStep.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
Fitness step.
|
||||
|
||||
@@ -45,3 +48,5 @@ ORK_CLASS_AVAILABLE
|
||||
@interface ORKFitnessStep : ORKActiveStep
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
|
||||
#import "ORKFitnessStep.h"
|
||||
|
||||
#import "ORKFitnessStepViewController.h"
|
||||
|
||||
|
||||
|
||||
@@ -29,9 +29,13 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit_Private.h>
|
||||
@import Foundation;
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
#import <ResearchKit/ORKActiveStepViewController.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
Step view controller corresponding to `ORKFitnessStep`.
|
||||
|
||||
@@ -42,3 +46,5 @@ ORK_CLASS_AVAILABLE
|
||||
@interface ORKFitnessStepViewController : ORKActiveStepViewController
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -30,16 +30,21 @@
|
||||
|
||||
|
||||
#import "ORKFitnessStepViewController.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import "ORKStep_Private.h"
|
||||
#import "ORKStepViewController_Internal.h"
|
||||
#import "ORKActiveStepViewController_Internal.h"
|
||||
|
||||
#import "ORKActiveStepTimer.h"
|
||||
#import "ORKActiveStepView.h"
|
||||
#import "ORKFitnessContentView.h"
|
||||
#import "ORKVerticalContainerView.h"
|
||||
#import "ORKFitnessStep.h"
|
||||
|
||||
#import "ORKStepViewController_Internal.h"
|
||||
#import "ORKHealthQuantityTypeRecorder.h"
|
||||
#import "ORKPedometerRecorder.h"
|
||||
#import "ORKActiveStepView.h"
|
||||
|
||||
#import "ORKActiveStepViewController_Internal.h"
|
||||
#import "ORKFitnessStep.h"
|
||||
#import "ORKStep_Private.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
|
||||
|
||||
@interface ORKFitnessStepViewController () <ORKHealthQuantityTypeRecorderDelegate,ORKPedometerRecorderDelegate> {
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
*/
|
||||
|
||||
|
||||
@import UIKit;
|
||||
#import <ResearchKit/ORKRecorder.h>
|
||||
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
|
||||
#import "ORKHealthQuantityTypeRecorder.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import "ORKHelpers_Internal.h"
|
||||
#import "ORKDataLogger.h"
|
||||
#import "ORKRecorder_Private.h"
|
||||
#import "ORKRecorder_Internal.h"
|
||||
@@ -43,12 +43,29 @@
|
||||
HKHealthStore *_healthStore;
|
||||
NSPredicate *_samplePredicate;
|
||||
HKObserverQuery *_observerQuery;
|
||||
NSInteger _anchor;
|
||||
/// Either the HKQueryAnchor object *or* NSUInteger value are tracked since the initializer for
|
||||
/// iOS 8 and iOS 9 use different objects. Only one will actually be referenced in the initalizer.
|
||||
HKQueryAnchor *_anchor;
|
||||
NSUInteger _anchorValue;
|
||||
HKQuantitySample *_lastSample;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#ifdef __IPHONE_10_0
|
||||
/// Add a protocol defining the initializer for iOS 8 apps. This signature was deprecated in iOS 9
|
||||
/// and deleted in iOS 10.
|
||||
@interface HKAnchoredObjectQuery (iOS8)
|
||||
- (instancetype)initWithType:(HKSampleType *)type
|
||||
predicate:(NSPredicate *)predicate
|
||||
anchor:(NSUInteger)anchor
|
||||
limit:(NSUInteger)limit
|
||||
completionHandler:(void (^)(HKAnchoredObjectQuery *query,
|
||||
NSArray<__kindof HKSample *> *results,
|
||||
NSUInteger newAnchor,
|
||||
NSError *error))handler NS_DEPRECATED_IOS(8_0, 9_0);
|
||||
@end
|
||||
#endif
|
||||
|
||||
@implementation ORKHealthQuantityTypeRecorder
|
||||
|
||||
@@ -67,7 +84,8 @@
|
||||
_quantityType = quantityType;
|
||||
_unit = unit;
|
||||
self.continuesInBackground = YES;
|
||||
_anchor = HKAnchoredObjectQueryNoAnchor;
|
||||
_anchorValue = HKAnchoredObjectQueryNoAnchor;
|
||||
_anchor = [HKQueryAnchor anchorFromValue:_anchorValue];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -89,7 +107,7 @@
|
||||
|
||||
static const NSInteger _HealthAnchoredQueryLimit = 100;
|
||||
|
||||
- (void)query_logResults:(NSArray *)results withAnchor:(NSUInteger)newAnchor {
|
||||
- (void)query_logResults:(NSArray *)results withAnchor:(HKQueryAnchor*)newAnchor anchorValue:(NSUInteger)anchorValue {
|
||||
|
||||
NSUInteger resultCount = results.count;
|
||||
if (resultCount == 0) {
|
||||
@@ -113,6 +131,7 @@ static const NSInteger _HealthAnchoredQueryLimit = 100;
|
||||
}
|
||||
|
||||
_anchor = newAnchor;
|
||||
_anchorValue = anchorValue;
|
||||
|
||||
if (resultCount == _HealthAnchoredQueryLimit) {
|
||||
// Do another fetch immediately rather than wait for an observation
|
||||
@@ -128,23 +147,45 @@ static const NSInteger _HealthAnchoredQueryLimit = 100;
|
||||
NSAssert(_samplePredicate != nil, @"Sample predicate should be non-nil if recording");
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
HKAnchoredObjectQuery *anchoredQuery = [[HKAnchoredObjectQuery alloc]
|
||||
initWithType:_quantityType
|
||||
predicate:_samplePredicate
|
||||
anchor:_anchor
|
||||
limit:_HealthAnchoredQueryLimit
|
||||
completionHandler:^(HKAnchoredObjectQuery *query, NSArray *results, NSUInteger newAnchor, NSError *error)
|
||||
{
|
||||
if (error) {
|
||||
// An error in the query's not the end of the world: we'll probably get another chance. Just log it.
|
||||
ORK_Log_Warning(@"Anchored query error: %@", error);
|
||||
return;
|
||||
}
|
||||
|
||||
__typeof(self) strongSelf = weakSelf;
|
||||
[strongSelf query_logResults:results withAnchor:newAnchor];
|
||||
|
||||
}];
|
||||
void (^handleResults)(NSArray <__kindof HKSample *> *, HKQueryAnchor *, NSUInteger, NSError *) = ^ (NSArray *results, HKQueryAnchor *newAnchor, NSUInteger newAnchorValue, NSError *error) {
|
||||
if (error) {
|
||||
// An error in the query's not the end of the world: we'll probably get another chance. Just log it.
|
||||
ORK_Log_Warning(@"Anchored query error: %@", error);
|
||||
return;
|
||||
}
|
||||
|
||||
__typeof(self) strongSelf = weakSelf;
|
||||
[strongSelf query_logResults:results withAnchor:newAnchor anchorValue:newAnchorValue];
|
||||
};
|
||||
|
||||
|
||||
HKAnchoredObjectQuery *anchoredQuery;
|
||||
if ([HKAnchoredObjectQuery instancesRespondToSelector:@selector(initWithType:predicate:anchor:limit:resultsHandler:)]) {
|
||||
|
||||
anchoredQuery = [[HKAnchoredObjectQuery alloc] initWithType:_quantityType
|
||||
predicate:_samplePredicate
|
||||
anchor:_anchor
|
||||
limit:_HealthAnchoredQueryLimit
|
||||
resultsHandler:
|
||||
^(HKAnchoredObjectQuery *query, NSArray *sampleObjects, NSArray *deletedObjects, HKQueryAnchor *newAnchor, NSError *error) {
|
||||
handleResults(sampleObjects, newAnchor, 0, error);
|
||||
}];
|
||||
} else if ([HKAnchoredObjectQuery instancesRespondToSelector:@selector(initWithType:predicate:anchor:limit:completionHandler:)]) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
anchoredQuery = [[HKAnchoredObjectQuery alloc] initWithType:_quantityType
|
||||
predicate:_samplePredicate
|
||||
anchor:_anchorValue
|
||||
limit:_HealthAnchoredQueryLimit
|
||||
completionHandler:
|
||||
^(HKAnchoredObjectQuery *query, NSArray<__kindof HKSample *> *results, NSUInteger newAnchor, NSError *error) {
|
||||
handleResults(results, nil, newAnchor, error);
|
||||
}];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
else {
|
||||
NSAssert(NO, @"Could not instantiate an HKAnchoredObjectQuery.");
|
||||
}
|
||||
[_healthStore executeQuery:anchoredQuery];
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,9 @@
|
||||
*/
|
||||
|
||||
|
||||
@import UIKit;
|
||||
#import "ORKCustomStepView_Internal.h"
|
||||
#import "ORKTypes.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -30,10 +30,12 @@
|
||||
|
||||
|
||||
#import "ORKHolePegTestPlaceContentView.h"
|
||||
#import "ORKHolePegTestPlacePegView.h"
|
||||
#import "ORKHolePegTestPlaceHoleView.h"
|
||||
|
||||
#import "ORKDirectionView.h"
|
||||
#import "ORKHelpers.h"
|
||||
#import "ORKHolePegTestPlaceHoleView.h"
|
||||
#import "ORKHolePegTestPlacePegView.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
#import "ORKSkin.h"
|
||||
|
||||
|
||||
@@ -163,7 +165,7 @@ static const CGFloat ORKHolePegViewDiameter = 88.0f;
|
||||
NSMutableArray *constraintsArray = [NSMutableArray array];
|
||||
|
||||
NSDictionary *views = NSDictionaryOfVariableBindings(_progressView, _pegView, _holeView, _directionView);
|
||||
NSDictionary *metrics = @{@"diameter" : @(ORKHolePegViewDiameter)};
|
||||
NSDictionary *metrics = @{@"diameter": @(ORKHolePegViewDiameter)};
|
||||
|
||||
[constraintsArray addObjectsFromArray:
|
||||
[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_progressView]-|"
|
||||
|
||||
@@ -29,13 +29,14 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit_Private.h>
|
||||
@import UIKit;
|
||||
#import "ORKDefines.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
ORK_CLASS_AVAILABLE
|
||||
@interface ORKHolePegTestPlaceHoleView : UIView
|
||||
@interface ORKHolePegTestPlaceHoleView : UIView <CAAnimationDelegate>
|
||||
|
||||
@property (nonatomic, assign, getter = isRotated) BOOL rotated;
|
||||
@property (nonatomic, assign, getter = isSuccess) BOOL success;
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
|
||||
#import "ORKHolePegTestPlaceHoleView.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
|
||||
|
||||
static const CGFloat ORKPlaceHoleViewRotation = 45.0f;
|
||||
|
||||
@@ -161,9 +163,9 @@ static const CGFloat ORKPlaceHoleViewRotation = 45.0f;
|
||||
}
|
||||
|
||||
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
ORKWeakTypeOf(self) weakSelf = self;
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.7f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
typeof(self) strongSelf = weakSelf;
|
||||
ORKStrongTypeOf(self) strongSelf = weakSelf;
|
||||
strongSelf.success = NO;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit_Private.h>
|
||||
@import UIKit;
|
||||
#import "ORKDefines.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -29,7 +29,9 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit_Private.h>
|
||||
@import Foundation;
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
#import <ResearchKit/ORKActiveStep.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
|
||||
#import "ORKHolePegTestPlaceStep.h"
|
||||
|
||||
#import "ORKHolePegTestPlaceStepViewController.h"
|
||||
|
||||
|
||||
@@ -73,7 +74,7 @@
|
||||
}
|
||||
|
||||
if (self.stepDuration < ORKHolePegTestMinimumDuration) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"duration can not be shorter than %@ seconds.", @(ORKHolePegTestMinimumDuration)] userInfo:nil];
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"duration cannot be shorter than %@ seconds.", @(ORKHolePegTestMinimumDuration)] userInfo:nil];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,9 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit_Private.h>
|
||||
@import UIKit;
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
#import <ResearchKit/ORKActiveStepViewController.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -30,11 +30,19 @@
|
||||
|
||||
|
||||
#import "ORKHolePegTestPlaceStepViewController.h"
|
||||
#import "ORKHolePegTestPlaceStep.h"
|
||||
#import "ORKHolePegTestPlaceContentView.h"
|
||||
#import "ORKActiveStepViewController_internal.h"
|
||||
#import "ORKStepViewController_internal.h"
|
||||
|
||||
#import "ORKActiveStepView.h"
|
||||
#import "ORKHolePegTestPlaceContentView.h"
|
||||
|
||||
#import "ORKActiveStepViewController_Internal.h"
|
||||
#import "ORKStepViewController_Internal.h"
|
||||
#import "ORKTaskViewController.h"
|
||||
|
||||
#import "ORKHolePegTestPlaceStep.h"
|
||||
#import "ORKNavigableOrderedTask.h"
|
||||
#import "ORKResult.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
|
||||
|
||||
@interface ORKHolePegTestPlaceStepViewController () <ORKHolePegTestPlaceContentViewDelegate>
|
||||
|
||||
@@ -29,7 +29,9 @@
|
||||
*/
|
||||
|
||||
|
||||
@import UIKit;
|
||||
#import "ORKCustomStepView_Internal.h"
|
||||
#import "ORKTypes.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -30,10 +30,12 @@
|
||||
|
||||
|
||||
#import "ORKHolePegTestRemoveContentView.h"
|
||||
|
||||
#import "ORKDirectionView.h"
|
||||
#import "ORKHolePegTestRemovePegView.h"
|
||||
#import "ORKSeparatorView.h"
|
||||
#import "ORKDirectionView.h"
|
||||
#import "ORKHelpers.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
#import "ORKSkin.h"
|
||||
|
||||
|
||||
@@ -156,7 +158,7 @@ static const CGFloat PegViewSeparatorWidth = 2.0f;
|
||||
NSMutableArray *constraintsArray = [NSMutableArray array];
|
||||
|
||||
NSDictionary *views = NSDictionaryOfVariableBindings(_progressView, _container, _pegView, _separatorView, _directionView);
|
||||
NSDictionary *metrics = @{@"diameter" : @(PegViewDiameter), @"separator" : @(PegViewSeparatorWidth), @"margin" : @((1 + self.threshold) * PegViewDiameter)};
|
||||
NSDictionary *metrics = @{@"diameter": @(PegViewDiameter), @"separator": @(PegViewSeparatorWidth), @"margin": @((1 + self.threshold) * PegViewDiameter)};
|
||||
|
||||
[constraintsArray addObjectsFromArray:
|
||||
[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_progressView]-|"
|
||||
|
||||
@@ -29,13 +29,14 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit_Private.h>
|
||||
@import UIKit;
|
||||
#import "ORKDefines.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
ORK_CLASS_AVAILABLE
|
||||
@interface ORKHolePegTestRemovePegView : UIView
|
||||
@interface ORKHolePegTestRemovePegView : UIView <CAAnimationDelegate>
|
||||
|
||||
@property (nonatomic, assign, getter = isSuccess) BOOL success;
|
||||
|
||||
|
||||
@@ -29,7 +29,9 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit_Private.h>
|
||||
@import Foundation;
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
#import <ResearchKit/ORKActiveStep.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
|
||||
#import "ORKHolePegTestRemoveStep.h"
|
||||
|
||||
#import "ORKHolePegTestRemoveStepViewController.h"
|
||||
|
||||
|
||||
@@ -73,7 +74,7 @@
|
||||
}
|
||||
|
||||
if (self.stepDuration < ORKHolePegTestMinimumDuration) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"duration can not be shorter than %@ seconds.", @(ORKHolePegTestMinimumDuration)] userInfo:nil];
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"duration cannot be shorter than %@ seconds.", @(ORKHolePegTestMinimumDuration)] userInfo:nil];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,9 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit_Private.h>
|
||||
@import UIKit;
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
#import <ResearchKit/ORKActiveStepViewController.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -30,11 +30,18 @@
|
||||
|
||||
|
||||
#import "ORKHolePegTestRemoveStepViewController.h"
|
||||
#import "ORKHolePegTestRemoveStep.h"
|
||||
#import "ORKHolePegTestRemoveContentView.h"
|
||||
#import "ORKActiveStepViewController_internal.h"
|
||||
#import "ORKStepViewController_internal.h"
|
||||
|
||||
#import "ORKActiveStepView.h"
|
||||
#import "ORKHolePegTestRemoveContentView.h"
|
||||
|
||||
#import "ORKActiveStepViewController_Internal.h"
|
||||
#import "ORKStepViewController_Internal.h"
|
||||
#import "ORKTaskViewController.h"
|
||||
|
||||
#import "ORKHolePegTestRemoveStep.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
#import "ORKResult.h"
|
||||
|
||||
|
||||
@interface ORKHolePegTestRemoveStepViewController () <ORKHolePegTestRemoveContentViewDelegate>
|
||||
|
||||
@@ -29,8 +29,9 @@
|
||||
*/
|
||||
|
||||
|
||||
@import UIKit;
|
||||
@import CoreLocation;
|
||||
#import <ResearchKit/ORKRecorder.h>
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -30,11 +30,14 @@
|
||||
|
||||
|
||||
#import "ORKLocationRecorder.h"
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
#import "CLLocation+ORKJSONDictionary.h"
|
||||
|
||||
#import "ORKDataLogger.h"
|
||||
|
||||
#import "ORKRecorder_Internal.h"
|
||||
#import "ORKRecorder_Private.h"
|
||||
|
||||
#import "CLLocation+ORKJSONDictionary.h"
|
||||
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
|
||||
|
||||
@interface ORKLocationRecorder () <CLLocationManagerDelegate> {
|
||||
@@ -76,10 +79,10 @@
|
||||
[super start];
|
||||
|
||||
if (!_logger) {
|
||||
NSError *err = nil;
|
||||
_logger = [self makeJSONDataLoggerWithError:&err];
|
||||
NSError *error = nil;
|
||||
_logger = [self makeJSONDataLoggerWithError:&error];
|
||||
if (!_logger) {
|
||||
[self finishRecordingWithError:err];
|
||||
[self finishRecordingWithError:error];
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -94,7 +97,7 @@
|
||||
if (!self.locationManager) {
|
||||
NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain
|
||||
code:NSFeatureUnsupportedError
|
||||
userInfo:@{@"recorder" : self}];
|
||||
userInfo:@{@"recorder": self}];
|
||||
[self finishRecordingWithError:error];
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit_Private.h>
|
||||
@import UIKit;
|
||||
#import "ORKTypes.h"
|
||||
#import "ORKCustomStepView_Internal.h"
|
||||
|
||||
|
||||
|
||||
@@ -30,12 +30,14 @@
|
||||
|
||||
|
||||
#import "ORKPSATContentView.h"
|
||||
#import "ORKSkin.h"
|
||||
|
||||
#import "ORKBorderedButton.h"
|
||||
#import "ORKPSATKeyboardView.h"
|
||||
#import "ORKTapCountLabel.h"
|
||||
#import "ORKBorderedButton.h"
|
||||
#import "ORKVoiceEngine.h"
|
||||
#import "ORKHelpers.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
#import "ORKSkin.h"
|
||||
|
||||
|
||||
@interface ORKPSATContentView ()
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit.h>
|
||||
@import UIKit;
|
||||
#import "ORKDefines.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
|
||||
#import "ORKPSATKeyboardView.h"
|
||||
|
||||
#import "ORKBorderedButton.h"
|
||||
|
||||
|
||||
|
||||
@@ -29,7 +29,9 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit_Private.h>
|
||||
@import Foundation;
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
#import <ResearchKit/ORKActiveStep.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
|
||||
#import "ORKPSATStep.h"
|
||||
|
||||
#import "ORKPSATStepViewController.h"
|
||||
|
||||
|
||||
|
||||
@@ -29,7 +29,9 @@
|
||||
*/
|
||||
|
||||
|
||||
#import <ResearchKit/ResearchKit_Private.h>
|
||||
@import UIKit;
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
#import <ResearchKit/ORKActiveStepViewController.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -30,13 +30,19 @@
|
||||
|
||||
|
||||
#import "ORKPSATStepViewController.h"
|
||||
#import "ORKActiveStepViewController_Internal.h"
|
||||
#import "ORKStepViewController_Internal.h"
|
||||
#import "ORKPSATContentView.h"
|
||||
#import "ORKPSATStep.h"
|
||||
#import "ORKVerticalContainerView.h"
|
||||
|
||||
#import "ORKActiveStepTimer.h"
|
||||
#import "ORKActiveStepView.h"
|
||||
#import "ORKPSATContentView.h"
|
||||
#import "ORKPSATKeyboardView.h"
|
||||
#import "ORKVerticalContainerView.h"
|
||||
|
||||
#import "ORKActiveStepViewController_Internal.h"
|
||||
#import "ORKPSATStep.h"
|
||||
#import "ORKResult.h"
|
||||
#import "ORKStepViewController_Internal.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
|
||||
|
||||
@interface ORKPSATStepViewController () <ORKPSATKeyboardViewDelegate>
|
||||
@@ -155,12 +161,12 @@
|
||||
([self psatStep].interStimulusInterval - [self psatStep].stimulusDuration) > 0.05 ) {
|
||||
|
||||
// Don't show `-` if the difference between stimulusDuration and interStimulusInterval is less than timer's resolution.
|
||||
__weak typeof(self) weakSelf = self;
|
||||
ORKWeakTypeOf(self) weakSelf = self;
|
||||
self.clearDigitsTimer = [[ORKActiveStepTimer alloc] initWithDuration:[self psatStep].stepDuration
|
||||
interval:[self psatStep].interStimulusInterval
|
||||
runtime:-[self psatStep].stimulusDuration
|
||||
handler:^(ORKActiveStepTimer *timer, BOOL finished) {
|
||||
typeof(self) strongSelf = weakSelf;
|
||||
ORKStrongTypeOf(self) strongSelf = weakSelf;
|
||||
[strongSelf clearDigitsTimerFired];
|
||||
}];
|
||||
[self.clearDigitsTimer resume];
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
*/
|
||||
|
||||
|
||||
@import UIKit;
|
||||
#import <ResearchKit/ORKRecorder.h>
|
||||
|
||||
|
||||
|
||||
@@ -30,10 +30,13 @@
|
||||
|
||||
|
||||
#import "ORKPedometerRecorder.h"
|
||||
|
||||
#import "ORKDataLogger.h"
|
||||
#import "CMPedometerData+ORKJSONDictionary.h"
|
||||
|
||||
#import "ORKRecorder_Internal.h"
|
||||
#import "ORKRecorder_Private.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
#import "CMPedometerData+ORKJSONDictionary.h"
|
||||
|
||||
|
||||
@interface ORKPedometerRecorder () {
|
||||
@@ -91,10 +94,10 @@
|
||||
_totalDistance = -1;
|
||||
|
||||
if (!_logger) {
|
||||
NSError *err = nil;
|
||||
_logger = [self makeJSONDataLoggerWithError:&err];
|
||||
NSError *error = nil;
|
||||
_logger = [self makeJSONDataLoggerWithError:&error];
|
||||
if (!_logger) {
|
||||
[self finishRecordingWithError:err];
|
||||
[self finishRecordingWithError:error];
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -104,25 +107,25 @@
|
||||
if (![[self.pedometer class] isStepCountingAvailable]) {
|
||||
[self finishRecordingWithError:[NSError errorWithDomain:NSCocoaErrorDomain
|
||||
code:NSFeatureUnsupportedError
|
||||
userInfo:@{@"recorder" : self}]];
|
||||
userInfo:@{@"recorder": self}]];
|
||||
return;
|
||||
}
|
||||
|
||||
_isRecording = YES;
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
ORKWeakTypeOf(self) weakSelf = self;
|
||||
[self.pedometer startPedometerUpdatesFromDate:[NSDate date] withHandler:^(CMPedometerData *pedometerData, NSError *error) {
|
||||
|
||||
BOOL success = NO;
|
||||
if (pedometerData) {
|
||||
success = [_logger append:[pedometerData ork_JSONDictionary] error:&error];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
__typeof(self) strongSelf = weakSelf;
|
||||
ORKStrongTypeOf(self) strongSelf = weakSelf;
|
||||
[strongSelf updateStatisticsWithData:pedometerData];
|
||||
});
|
||||
}
|
||||
if (!success || error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
__typeof(self) strongSelf = weakSelf;
|
||||
ORKStrongTypeOf(self) strongSelf = weakSelf;
|
||||
[strongSelf finishRecordingWithError:error];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -29,9 +29,12 @@
|
||||
*/
|
||||
|
||||
|
||||
@import UIKit;
|
||||
#import "ORKCustomStepView_Internal.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ORKReactionTimeContentView : ORKActiveStepCustomView
|
||||
|
||||
- (void)setStimulusHidden:(BOOL)hidden;
|
||||
@@ -43,3 +46,5 @@
|
||||
- (void)resetAfterDelay:(NSTimeInterval)delay completion:(nullable void (^)(void))completion;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -30,8 +30,9 @@
|
||||
|
||||
|
||||
#import "ORKReactionTimeContentView.h"
|
||||
#import "ORKReactionTimeStimulusView.h"
|
||||
|
||||
#import "ORKNavigationContainerView.h"
|
||||
#import "ORKReactionTimeStimulusView.h"
|
||||
|
||||
|
||||
@implementation ORKReactionTimeContentView {
|
||||
|
||||
@@ -26,11 +26,13 @@
|
||||
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>
|
||||
@import Foundation;
|
||||
@import AudioToolbox;
|
||||
#import <ResearchKit/ORKDefines.h>
|
||||
#import <ResearchKit/ORKActiveStep.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -30,8 +30,10 @@
|
||||
|
||||
|
||||
#import "ORKReactionTimeStep.h"
|
||||
|
||||
#import "ORKReactionTimeViewController.h"
|
||||
#import "ORKHelpers.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
|
||||
|
||||
@implementation ORKReactionTimeStep
|
||||
|
||||
@@ -29,9 +29,12 @@
|
||||
*/
|
||||
|
||||
|
||||
@import UIKit;
|
||||
#import "ORKCustomStepView_Internal.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ORKReactionTimeStimulusView : UIView
|
||||
|
||||
- (void)reset;
|
||||
@@ -41,3 +44,5 @@
|
||||
- (void)startFailureAnimationWithDuration:(NSTimeInterval)duration completion:(nullable void(^)(void))completion;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -26,10 +26,12 @@
|
||||
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 UIKit;
|
||||
#import "ORKDefines.h"
|
||||
#import "ORKActiveStepViewController.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -30,12 +30,19 @@
|
||||
|
||||
|
||||
#import "ORKReactionTimeViewController.h"
|
||||
#import "ORKActiveStepViewController_Internal.h"
|
||||
|
||||
#import "ORKActiveStepView.h"
|
||||
#import "ORKReactionTimeContentView.h"
|
||||
|
||||
#import "ORKActiveStepViewController_Internal.h"
|
||||
|
||||
#import "ORKReactionTimeStep.h"
|
||||
#import <CoreMotion/CMDeviceMotion.h>
|
||||
#import "ORKResult.h"
|
||||
|
||||
#import "ORKHelpers_Internal.h"
|
||||
|
||||
#import <AudioToolbox/AudioServices.h>
|
||||
#import <CoreMotion/CMDeviceMotion.h>
|
||||
|
||||
|
||||
@implementation ORKReactionTimeViewController {
|
||||
@@ -183,7 +190,7 @@ static const NSTimeInterval OutcomeAnimationDuration = 0.3;
|
||||
}
|
||||
|
||||
- (void)resetAfterDelay:(NSTimeInterval)delay {
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
ORKWeakTypeOf(self) weakSelf = self;
|
||||
[_reactionTimeContentView resetAfterDelay:delay completion:^{
|
||||
[weakSelf configureTitle];
|
||||
[weakSelf start];
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user