3084 lines
169 KiB
Objective-C
3084 lines
169 KiB
Objective-C
/*
|
|
Copyright (c) 2015, Apple Inc. All rights reserved.
|
|
Copyright (c) 2016, Sage Bionetworks
|
|
|
|
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 "ORKOrderedTask+ORKPredefinedActiveTask.h"
|
|
#import "ORKOrderedTask_Private.h"
|
|
|
|
#import "ORKAccelerometerRecorder.h"
|
|
#import "ORKActiveStep_Internal.h"
|
|
#import "ORKAmslerGridStep.h"
|
|
#import "ORKAnswerFormat_Internal.h"
|
|
#import "ORKAudioLevelNavigationRule.h"
|
|
#import "ORKAudioRecorder.h"
|
|
#import "ORKAudioStep.h"
|
|
#import "ORKCompletionStep.h"
|
|
#import "ORKCountdownStep.h"
|
|
#import "ORKEnvironmentSPLMeterStep.h"
|
|
#import "ORKHolePegTestPlaceStep.h"
|
|
#import "ORKHolePegTestRemoveStep.h"
|
|
#import "ORKTouchAnywhereStep.h"
|
|
#import "ORKFitnessStep.h"
|
|
#import "ORKFormStep.h"
|
|
#import "ORKHealthQuantityTypeRecorder.h"
|
|
#import "ORKNavigableOrderedTask.h"
|
|
#import "ORKPSATStep.h"
|
|
#import "ORKQuestionStep.h"
|
|
#import "ORKReactionTimeStep.h"
|
|
#import "ORKNormalizedReactionTimeStep.h"
|
|
#import "ORKSpatialSpanMemoryStep.h"
|
|
#import "ORKSpeechRecognitionStep.h"
|
|
#import "ORKStep_Private.h"
|
|
#import "ORKStroopStep.h"
|
|
#import "ORKTappingIntervalStep.h"
|
|
#import "ORKAudioFitnessStep.h"
|
|
#import "ORKTimedWalkStep.h"
|
|
#import "ORKToneAudiometryStep.h"
|
|
#import "ORKTowerOfHanoiStep.h"
|
|
#import "ORKTrailmakingStep.h"
|
|
#import "ORKRangeOfMotionStep.h"
|
|
#import "ORKShoulderRangeOfMotionStep.h"
|
|
#import "ORKWaitStep.h"
|
|
#import "ORKWalkingTaskStep.h"
|
|
#import "ORKResultPredicate.h"
|
|
#import "ORKSpeechInNoiseStep.h"
|
|
#import "ORKdBHLToneAudiometryStep.h"
|
|
#import "ORKdBHLToneAudiometryOnboardingStep.h"
|
|
|
|
#import "ORKSkin.h"
|
|
|
|
#import "ORKHelpers_Internal.h"
|
|
#import "UIImage+ResearchKit.h"
|
|
#import <limits.h>
|
|
|
|
#pragma mark - Predefined
|
|
|
|
NSString *const ORKInstruction0StepIdentifier = @"instruction";
|
|
NSString *const ORKInstruction1StepIdentifier = @"instruction1";
|
|
NSString *const ORKInstruction2StepIdentifier = @"instruction2";
|
|
NSString *const ORKInstruction3StepIdentifier = @"instruction3";
|
|
NSString *const ORKInstruction4StepIdentifier = @"instruction4";
|
|
NSString *const ORKInstruction5StepIdentifier = @"instruction5";
|
|
NSString *const ORKInstruction6StepIdentifier = @"instruction6";
|
|
NSString *const ORKInstruction7StepIdentifier = @"instruction7";
|
|
|
|
NSString *const ORKCountdownStepIdentifier = @"countdown";
|
|
NSString *const ORKCountdown1StepIdentifier = @"countdown1";
|
|
NSString *const ORKCountdown2StepIdentifier = @"countdown2";
|
|
NSString *const ORKCountdown3StepIdentifier = @"countdown3";
|
|
NSString *const ORKCountdown4StepIdentifier = @"countdown4";
|
|
NSString *const ORKCountdown5StepIdentifier = @"countdown5";
|
|
|
|
NSString *const ORKFollowUpQuestions0StepIdentifier = @"followUpQuestion0";
|
|
|
|
NSString *const ORKEditSpeechTranscript0StepIdentifier = @"editSpeechTranscript0";
|
|
|
|
NSString *const ORKConclusionStepIdentifier = @"conclusion";
|
|
|
|
NSString *const ORKActiveTaskLeftHandIdentifier = @"left";
|
|
NSString *const ORKActiveTaskMostAffectedHandIdentifier = @"mostAffected";
|
|
NSString *const ORKActiveTaskRightHandIdentifier = @"right";
|
|
NSString *const ORKActiveTaskSkipHandStepIdentifier = @"skipHand";
|
|
|
|
NSString *const ORKTouchAnywhereStepIdentifier = @"touch.anywhere";
|
|
|
|
NSString *const ORKAudioRecorderIdentifier = @"audio";
|
|
NSString *const ORKAccelerometerRecorderIdentifier = @"accelerometer";
|
|
NSString *const ORKStreamingAudioRecorderIdentifier = @"streamingAudio";
|
|
NSString *const ORKPedometerRecorderIdentifier = @"pedometer";
|
|
NSString *const ORKDeviceMotionRecorderIdentifier = @"deviceMotion";
|
|
NSString *const ORKLocationRecorderIdentifier = @"location";
|
|
NSString *const ORKHeartRateRecorderIdentifier = @"heartRate";
|
|
|
|
void ORKStepArrayAddStep(NSMutableArray *array, ORKStep *step) {
|
|
[step validateParameters];
|
|
[array addObject:step];
|
|
}
|
|
|
|
@implementation ORKOrderedTask (ORKMakeTaskUtilities)
|
|
|
|
+ (NSArray<ORKRecorderConfiguration*>*)makeRecorderConfigurationsWithOptions:(ORKPredefinedTaskOption)options {
|
|
|
|
#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION
|
|
HKUnit *bpmUnit = [[HKUnit countUnit] unitDividedByUnit:[HKUnit minuteUnit]];
|
|
HKQuantityType *heartRateType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate];
|
|
#endif
|
|
|
|
NSMutableArray<ORKRecorderConfiguration*> *recorderConfigurations = [NSMutableArray arrayWithCapacity:5];
|
|
|
|
if (!(ORKPredefinedTaskOptionExcludePedometer & options)) {
|
|
[recorderConfigurations addObject:[[ORKPedometerRecorderConfiguration alloc] initWithIdentifier:ORKPedometerRecorderIdentifier]];
|
|
}
|
|
if (!(ORKPredefinedTaskOptionExcludeAccelerometer & options)) {
|
|
[recorderConfigurations addObject:[[ORKAccelerometerRecorderConfiguration alloc] initWithIdentifier:ORKAccelerometerRecorderIdentifier
|
|
frequency:100]];
|
|
}
|
|
if (!(ORKPredefinedTaskOptionExcludeDeviceMotion & options)) {
|
|
[recorderConfigurations addObject:[[ORKDeviceMotionRecorderConfiguration alloc] initWithIdentifier:ORKDeviceMotionRecorderIdentifier
|
|
frequency:100]];
|
|
}
|
|
#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION
|
|
if (!(ORKPredefinedTaskOptionExcludeLocation & options)) {
|
|
[recorderConfigurations addObject:[[ORKLocationRecorderConfiguration alloc] initWithIdentifier:ORKLocationRecorderIdentifier]];
|
|
}
|
|
#endif
|
|
#if ORK_FEATURE_HEALTHKIT_AUTHORIZATION
|
|
if (!(ORKPredefinedTaskOptionExcludeHeartRate & options)) {
|
|
[recorderConfigurations addObject:[[ORKHealthQuantityTypeRecorderConfiguration alloc] initWithIdentifier:ORKHeartRateRecorderIdentifier
|
|
healthQuantityType:heartRateType unit:bpmUnit]];
|
|
}
|
|
#endif
|
|
|
|
return [recorderConfigurations copy];
|
|
}
|
|
|
|
+ (ORKCompletionStep *)makeCompletionStep {
|
|
ORKCompletionStep *step = [[ORKCompletionStep alloc] initWithIdentifier:ORKConclusionStepIdentifier];
|
|
step.title = ORKLocalizedString(@"TASK_COMPLETE_TITLE", nil);
|
|
step.text = ORKLocalizedString(@"TASK_COMPLETE_TEXT", nil);
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
return step;
|
|
}
|
|
|
|
+ (NSDateComponentsFormatter *)textTimeFormatter {
|
|
NSDateComponentsFormatter *formatter = [NSDateComponentsFormatter new];
|
|
formatter.unitsStyle = NSDateComponentsFormatterUnitsStyleSpellOut;
|
|
|
|
// Exception list: Korean, Chinese (all), Thai, and Vietnamese.
|
|
NSArray *nonSpelledOutLanguages = @[@"ko", @"zh", @"th", @"vi", @"ja"];
|
|
NSString *currentLanguage = [[NSBundle mainBundle] preferredLocalizations].firstObject;
|
|
NSString *currentLanguageCode = [NSLocale componentsFromLocaleIdentifier:currentLanguage][NSLocaleLanguageCode];
|
|
if ((currentLanguageCode != nil) && [nonSpelledOutLanguages containsObject:currentLanguageCode]) {
|
|
formatter.unitsStyle = NSDateComponentsFormatterUnitsStyleFull;
|
|
}
|
|
|
|
formatter.allowedUnits = NSCalendarUnitMinute | NSCalendarUnitSecond;
|
|
formatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorDropAll;
|
|
return formatter;
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@implementation ORKOrderedTask (ORKPredefinedActiveTask)
|
|
|
|
#pragma mark - AmslerGridTask
|
|
|
|
NSString *const ORKAmslerGridStepLeftIdentifier = @"amsler.grid.left";
|
|
NSString *const ORKAmslerGridStepRightIdentifier = @"amsler.grid.right";
|
|
NSString *const ORKAmslerGridCalibrationLeftIdentifier = @"amsler.grid.calibration.left";
|
|
NSString *const ORKAmslerGridCalibrationRightIdentifier = @"amsler.grid.calibration.Right";
|
|
|
|
+ (ORKOrderedTask *)amslerGridTaskWithIdentifier:(NSString *)identifier
|
|
intendedUseDescription:(NSString *)intendedUseDescription
|
|
options:(ORKPredefinedTaskOption)options {
|
|
NSMutableArray *steps = [NSMutableArray array];
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction0StepIdentifier];
|
|
step.title = ORKLocalizedString(@"AMSLER_GRID_TITLE", nil);
|
|
step.text = intendedUseDescription;
|
|
step.detailText = ORKLocalizedString(@"AMSLER_GRID_INTRO_TEXT", nil);
|
|
step.image = [UIImage imageNamed:@"amslerGrid" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
{
|
|
|
|
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier];
|
|
step.title = ORKLocalizedString(@"AMSLER_GRID_TITLE", nil);
|
|
step.text = ORKLocalizedString(@"AMSLER_GRID_INSTRUCTION_TEXT", nil);
|
|
|
|
NSString *leftEye = ORKLocalizedString(@"AMSLER_GRID_LEFT_EYE", nil);
|
|
NSString *detailText = [@"\n" stringByAppendingString:[NSString stringWithFormat:ORKLocalizedString(@"AMSLER_GRID_INSTRUCTION_DETAIL_TEXT", nil), leftEye]];
|
|
step.detailText = detailText;
|
|
|
|
step.image = [UIImage imageNamed:@"amslerGrid" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.shouldTintImages = YES;
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
{
|
|
ORKAmslerGridStep *step = [[ORKAmslerGridStep alloc] initWithIdentifier:ORKAmslerGridStepLeftIdentifier];
|
|
step.eyeSide = ORKAmslerGridEyeSideLeft;
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction2StepIdentifier];
|
|
step.title = ORKLocalizedString(@"AMSLER_GRID_TITLE", nil);
|
|
step.text = ORKLocalizedString(@"AMSLER_GRID_INSTRUCTION_TEXT", nil);
|
|
NSString *rightEye = ORKLocalizedString(@"AMSLER_GRID_RIGHT_EYE", nil);
|
|
NSString *detailText = [@"\n" stringByAppendingString:[NSString stringWithFormat:ORKLocalizedString(@"AMSLER_GRID_INSTRUCTION_DETAIL_TEXT", nil), rightEye]];
|
|
step.detailText = detailText;
|
|
|
|
step.image = [UIImage imageNamed:@"amslerGrid" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
{
|
|
ORKAmslerGridStep *step = [[ORKAmslerGridStep alloc] initWithIdentifier:ORKAmslerGridStepRightIdentifier];
|
|
step.eyeSide = ORKAmslerGridEyeSideRight;
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
if (!(options & ORKPredefinedTaskOptionExcludeConclusion)) {
|
|
ORKCompletionStep *completionStep = [self makeCompletionStep];
|
|
ORKStepArrayAddStep(steps, completionStep);
|
|
}
|
|
|
|
ORKOrderedTask *task = [[ORKOrderedTask alloc] initWithIdentifier:identifier steps:steps];
|
|
return task;
|
|
}
|
|
|
|
#pragma mark - holePegTestTask
|
|
|
|
NSString *const ORKHolePegTestDominantPlaceStepIdentifier = @"hole.peg.test.dominant.place";
|
|
NSString *const ORKHolePegTestDominantRemoveStepIdentifier = @"hole.peg.test.dominant.remove";
|
|
NSString *const ORKHolePegTestNonDominantPlaceStepIdentifier = @"hole.peg.test.non.dominant.place";
|
|
NSString *const ORKHolePegTestNonDominantRemoveStepIdentifier = @"hole.peg.test.non.dominant.remove";
|
|
|
|
+ (ORKNavigableOrderedTask *)holePegTestTaskWithIdentifier:(NSString *)identifier
|
|
intendedUseDescription:(nullable NSString *)intendedUseDescription
|
|
dominantHand:(ORKBodySagittal)dominantHand
|
|
numberOfPegs:(int)numberOfPegs
|
|
threshold:(double)threshold
|
|
rotated:(BOOL)rotated
|
|
timeLimit:(NSTimeInterval)timeLimit
|
|
options:(ORKPredefinedTaskOption)options {
|
|
|
|
NSMutableArray *steps = [NSMutableArray array];
|
|
BOOL dominantHandLeft = (dominantHand == ORKBodySagittalLeft);
|
|
NSTimeInterval stepDuration = (timeLimit == 0) ? CGFLOAT_MAX : timeLimit;
|
|
NSString *pegs = [NSNumberFormatter localizedStringFromNumber:@(numberOfPegs) numberStyle:NSNumberFormatterNoStyle];
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction0StepIdentifier];
|
|
step.title = [[NSString alloc] initWithFormat:ORKLocalizedString(@"HOLE_PEG_TEST_TITLE_%@", nil), pegs];
|
|
step.text = intendedUseDescription;
|
|
step.detailText = [[NSString alloc] initWithFormat:ORKLocalizedString(@"HOLE_PEG_TEST_INTRO_TEXT_%@", nil), pegs];
|
|
step.image = [UIImage imageNamed:@"phoneholepeg" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier];
|
|
step.title = [[NSString alloc] initWithFormat:ORKLocalizedString(@"HOLE_PEG_TEST_TITLE_%@", nil), pegs];
|
|
step.text = dominantHandLeft ? [[NSString alloc] initWithFormat:ORKLocalizedString(@"HOLE_PEG_TEST_INTRO_TEXT_2_LEFT_HAND_FIRST_%@%@", nil), pegs, pegs] : [[NSString alloc] initWithFormat:ORKLocalizedString(@"HOLE_PEG_TEST_INTRO_TEXT_2_RIGHT_HAND_FIRST_%@%@", nil), pegs, pegs];
|
|
step.detailText = ORKLocalizedString(@"HOLE_PEG_TEST_CALL_TO_ACTION", nil);
|
|
UIImage *image1 = [UIImage imageNamed:@"holepegtest1" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
UIImage *image2 = [UIImage imageNamed:@"holepegtest2" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
UIImage *image3 = [UIImage imageNamed:@"holepegtest3" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
UIImage *image4 = [UIImage imageNamed:@"holepegtest4" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
UIImage *image5 = [UIImage imageNamed:@"holepegtest5" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
UIImage *image6 = [UIImage imageNamed:@"holepegtest6" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.image = [UIImage animatedImageWithImages:@[image1, image2, image3, image4, image5, image6] duration:4];
|
|
step.imageContentMode = UIViewContentModeScaleAspectFit;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
{
|
|
{
|
|
ORKHolePegTestPlaceStep *step = [[ORKHolePegTestPlaceStep alloc] initWithIdentifier:ORKHolePegTestDominantPlaceStepIdentifier];
|
|
step.title = [[NSString alloc] initWithFormat:ORKLocalizedString(@"HOLE_PEG_TEST_TITLE_%@", nil), pegs];
|
|
step.text = [dominantHandLeft ? ORKLocalizedString(@"HOLE_PEG_TEST_PLACE_INSTRUCTION_LEFT_HAND", nil) : ORKLocalizedString(@"HOLE_PEG_TEST_PLACE_INSTRUCTION_RIGHT_HAND", nil) stringByAppendingString:[@"\n" stringByAppendingString:ORKLocalizedString(@"HOLE_PEG_TEST_TEXT", nil)]];
|
|
step.spokenInstruction = step.text;
|
|
step.movingDirection = dominantHand;
|
|
step.dominantHandTested = YES;
|
|
step.numberOfPegs = numberOfPegs;
|
|
step.threshold = threshold;
|
|
step.rotated = rotated;
|
|
step.shouldTintImages = YES;
|
|
step.stepDuration = stepDuration;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
ORKHolePegTestRemoveStep *step = [[ORKHolePegTestRemoveStep alloc] initWithIdentifier:ORKHolePegTestDominantRemoveStepIdentifier];
|
|
step.title = [[NSString alloc] initWithFormat:ORKLocalizedString(@"HOLE_PEG_TEST_TITLE_%@", nil), pegs];
|
|
step.text = [dominantHandLeft ? ORKLocalizedString(@"HOLE_PEG_TEST_REMOVE_INSTRUCTION_LEFT_HAND", nil) : ORKLocalizedString(@"HOLE_PEG_TEST_REMOVE_INSTRUCTION_RIGHT_HAND", nil) stringByAppendingString:[@"\n" stringByAppendingString:ORKLocalizedString(@"HOLE_PEG_TEST_TEXT", nil)]];
|
|
step.spokenInstruction = step.text;
|
|
step.movingDirection = (dominantHand == ORKBodySagittalLeft) ? ORKBodySagittalRight : ORKBodySagittalLeft;
|
|
step.dominantHandTested = YES;
|
|
step.numberOfPegs = numberOfPegs;
|
|
step.threshold = threshold;
|
|
step.shouldTintImages = YES;
|
|
step.stepDuration = stepDuration;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
ORKHolePegTestPlaceStep *step = [[ORKHolePegTestPlaceStep alloc] initWithIdentifier:ORKHolePegTestNonDominantPlaceStepIdentifier];
|
|
step.title = [[NSString alloc] initWithFormat:ORKLocalizedString(@"HOLE_PEG_TEST_TITLE_%@", nil), pegs];
|
|
step.text = [dominantHandLeft ? ORKLocalizedString(@"HOLE_PEG_TEST_PLACE_INSTRUCTION_RIGHT_HAND", nil) : ORKLocalizedString(@"HOLE_PEG_TEST_PLACE_INSTRUCTION_LEFT_HAND", nil) stringByAppendingString:[@"\n" stringByAppendingString:ORKLocalizedString(@"HOLE_PEG_TEST_TEXT", nil)]];
|
|
step.spokenInstruction = step.text;
|
|
step.movingDirection = (dominantHand == ORKBodySagittalLeft) ? ORKBodySagittalRight : ORKBodySagittalLeft;
|
|
step.dominantHandTested = NO;
|
|
step.numberOfPegs = numberOfPegs;
|
|
step.threshold = threshold;
|
|
step.rotated = rotated;
|
|
step.shouldTintImages = YES;
|
|
step.stepDuration = stepDuration;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
ORKHolePegTestRemoveStep *step = [[ORKHolePegTestRemoveStep alloc] initWithIdentifier:ORKHolePegTestNonDominantRemoveStepIdentifier];
|
|
step.title = [[NSString alloc] initWithFormat:ORKLocalizedString(@"HOLE_PEG_TEST_TITLE_%@", nil), pegs];
|
|
step.text = [dominantHandLeft ? ORKLocalizedString(@"HOLE_PEG_TEST_REMOVE_INSTRUCTION_RIGHT_HAND", nil) : ORKLocalizedString(@"HOLE_PEG_TEST_REMOVE_INSTRUCTION_LEFT_HAND", nil) stringByAppendingString:[@"\n" stringByAppendingString:ORKLocalizedString(@"HOLE_PEG_TEST_TEXT", nil)]];
|
|
step.spokenInstruction = step.text;
|
|
step.movingDirection = dominantHand;
|
|
step.dominantHandTested = NO;
|
|
step.numberOfPegs = numberOfPegs;
|
|
step.threshold = threshold;
|
|
step.shouldTintImages = YES;
|
|
step.stepDuration = stepDuration;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeConclusion)) {
|
|
ORKCompletionStep *step = [self makeCompletionStep];
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
|
|
// The task is actually dynamic. The direct navigation rules are used for skipping the peg
|
|
// removal steps if the user doesn't succeed in placing all the pegs in the allotted time
|
|
// (the rules are removed from `ORKHolePegTestPlaceStepViewController` if she succeeds).
|
|
ORKNavigableOrderedTask *task = [[ORKNavigableOrderedTask alloc] initWithIdentifier:identifier steps:steps];
|
|
|
|
ORKStepNavigationRule *navigationRule = [[ORKDirectStepNavigationRule alloc] initWithDestinationStepIdentifier:ORKHolePegTestNonDominantPlaceStepIdentifier];
|
|
[task setNavigationRule:navigationRule forTriggerStepIdentifier:ORKHolePegTestDominantPlaceStepIdentifier];
|
|
navigationRule = [[ORKDirectStepNavigationRule alloc] initWithDestinationStepIdentifier:ORKConclusionStepIdentifier];
|
|
[task setNavigationRule:navigationRule forTriggerStepIdentifier:ORKHolePegTestNonDominantPlaceStepIdentifier];
|
|
|
|
return task;
|
|
}
|
|
|
|
|
|
#pragma mark - twoFingerTappingInterval
|
|
|
|
NSString *const ORKTappingStepIdentifier = @"tapping";
|
|
|
|
+ (ORKOrderedTask *)twoFingerTappingIntervalTaskWithIdentifier:(NSString *)identifier
|
|
intendedUseDescription:(NSString *)intendedUseDescription
|
|
duration:(NSTimeInterval)duration
|
|
handOptions:(ORKPredefinedTaskHandOption)handOptions
|
|
options:(ORKPredefinedTaskOption)options {
|
|
|
|
NSString *durationString = [ORKDurationStringFormatter() stringFromTimeInterval:duration];
|
|
|
|
NSMutableArray *steps = [NSMutableArray array];
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction0StepIdentifier];
|
|
step.title = ORKLocalizedString(@"TAPPING_TASK_TITLE", nil);
|
|
step.text = intendedUseDescription;
|
|
step.detailText = ORKLocalizedString(@"TAPPING_INTRO_TEXT", nil);
|
|
|
|
NSString *imageName = @"phonetapping";
|
|
if (![[NSLocale preferredLanguages].firstObject hasPrefix:@"en"]) {
|
|
imageName = [imageName stringByAppendingString:@"_notap"];
|
|
}
|
|
step.image = [UIImage imageNamed:imageName inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.shouldTintImages = YES;
|
|
step.imageContentMode = UIViewContentModeScaleAspectFit;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
// Setup which hand to start with and how many hands to add based on the handOptions parameter
|
|
// Hand order is randomly determined.
|
|
NSUInteger handCount = ((handOptions & ORKPredefinedTaskHandOptionBoth) == ORKPredefinedTaskHandOptionBoth) ? 2 : 1;
|
|
BOOL undefinedHand = (handOptions == 0);
|
|
BOOL rightHand;
|
|
switch (handOptions) {
|
|
case ORKPredefinedTaskHandOptionLeft:
|
|
rightHand = NO; break;
|
|
case ORKPredefinedTaskHandOptionRight:
|
|
case ORKPredefinedTaskHandOptionUnspecified:
|
|
rightHand = YES; break;
|
|
default:
|
|
rightHand = (arc4random()%2 == 0); break;
|
|
}
|
|
|
|
for (NSUInteger hand = 1; hand <= handCount; hand++) {
|
|
|
|
NSString * (^appendIdentifier) (NSString *) = ^ (NSString * stepIdentifier) {
|
|
if (undefinedHand) {
|
|
return stepIdentifier;
|
|
} else {
|
|
NSString *handIdentifier = rightHand ? ORKActiveTaskRightHandIdentifier : ORKActiveTaskLeftHandIdentifier;
|
|
return [NSString stringWithFormat:@"%@.%@", stepIdentifier, handIdentifier];
|
|
}
|
|
};
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:appendIdentifier(ORKInstruction1StepIdentifier)];
|
|
|
|
// Set the title based on the hand
|
|
if (undefinedHand) {
|
|
step.title = ORKLocalizedString(@"TAPPING_TASK_TITLE", nil);
|
|
} else if (rightHand) {
|
|
step.title = ORKLocalizedString(@"TAPPING_TASK_TITLE_RIGHT", nil);
|
|
} else {
|
|
step.title = ORKLocalizedString(@"TAPPING_TASK_TITLE_LEFT", nil);
|
|
}
|
|
|
|
// Set the instructions for the tapping test screen that is displayed prior to each hand test
|
|
NSString *restText = ORKLocalizedString(@"TAPPING_INTRO_TEXT_2_REST_PHONE", nil);
|
|
NSString *tappingTextFormat = ORKLocalizedString(@"TAPPING_INTRO_TEXT_2_FORMAT", nil);
|
|
NSString *tappingText = [NSString localizedStringWithFormat:tappingTextFormat, durationString];
|
|
NSString *handText = nil;
|
|
|
|
if (hand == 1) {
|
|
if (undefinedHand) {
|
|
handText = ORKLocalizedString(@"TAPPING_INTRO_TEXT_2_MOST_AFFECTED", nil);
|
|
} else if (rightHand) {
|
|
handText = ORKLocalizedString(@"TAPPING_INTRO_TEXT_2_RIGHT_FIRST", nil);
|
|
} else {
|
|
handText = ORKLocalizedString(@"TAPPING_INTRO_TEXT_2_LEFT_FIRST", nil);
|
|
}
|
|
} else {
|
|
if (rightHand) {
|
|
handText = ORKLocalizedString(@"TAPPING_INTRO_TEXT_2_RIGHT_SECOND", nil);
|
|
} else {
|
|
handText = ORKLocalizedString(@"TAPPING_INTRO_TEXT_2_LEFT_SECOND", nil);
|
|
}
|
|
}
|
|
|
|
step.text = [NSString localizedStringWithFormat:@"%@ %@ %@", restText, handText, tappingText];
|
|
|
|
// Continue button will be different from first hand and second hand
|
|
if (hand == 1) {
|
|
step.detailText = ORKLocalizedString(@"TAPPING_CALL_TO_ACTION", nil);
|
|
} else {
|
|
step.detailText = ORKLocalizedString(@"TAPPING_CALL_TO_ACTION_NEXT", nil);
|
|
}
|
|
|
|
// Set the image
|
|
UIImage *im1 = [UIImage imageNamed:@"handtapping01" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
UIImage *im2 = [UIImage imageNamed:@"handtapping02" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
UIImage *imageAnimation = [UIImage animatedImageWithImages:@[im1, im2] duration:1];
|
|
|
|
if (rightHand || undefinedHand) {
|
|
step.image = imageAnimation;
|
|
} else {
|
|
step.image = [imageAnimation ork_flippedImage:UIImageOrientationUpMirrored];
|
|
}
|
|
step.imageContentMode = UIViewContentModeScaleAspectFit;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
// TAPPING STEP
|
|
{
|
|
NSMutableArray *recorderConfigurations = [NSMutableArray arrayWithCapacity:5];
|
|
if (!(ORKPredefinedTaskOptionExcludeAccelerometer & options)) {
|
|
[recorderConfigurations addObject:[[ORKAccelerometerRecorderConfiguration alloc] initWithIdentifier:ORKAccelerometerRecorderIdentifier
|
|
frequency:100]];
|
|
}
|
|
|
|
ORKTappingIntervalStep *step = [[ORKTappingIntervalStep alloc] initWithIdentifier:appendIdentifier(ORKTappingStepIdentifier)];
|
|
step.title = ORKLocalizedString(@"TAPPING_TASK_TITLE", nil);
|
|
|
|
if (undefinedHand) {
|
|
step.text = ORKLocalizedString(@"TAPPING_INSTRUCTION", nil);
|
|
} else if (rightHand) {
|
|
step.text = ORKLocalizedString(@"TAPPING_INSTRUCTION_RIGHT", nil);
|
|
} else {
|
|
step.text = ORKLocalizedString(@"TAPPING_INSTRUCTION_LEFT", nil);
|
|
}
|
|
if (UIAccessibilityIsVoiceOverRunning()) {
|
|
step.text = [NSString stringWithFormat:ORKLocalizedString(@"AX_TAPPING_INSTRUCTION_VOICEOVER", nil), step.text];
|
|
}
|
|
step.stepDuration = duration;
|
|
step.shouldContinueOnFinish = YES;
|
|
step.recorderConfigurations = recorderConfigurations;
|
|
step.optional = (handCount == 2);
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
// Flip to the other hand (ignored if handCount == 1)
|
|
rightHand = !rightHand;
|
|
}
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeConclusion)) {
|
|
ORKInstructionStep *step = [self makeCompletionStep];
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
ORKOrderedTask *task = [[ORKOrderedTask alloc] initWithIdentifier:identifier steps:[steps copy]];
|
|
|
|
return task;
|
|
}
|
|
|
|
|
|
#pragma mark - audioTask
|
|
|
|
NSString *const ORKAudioStepIdentifier = @"audio";
|
|
NSString *const ORKAudioTooLoudStepIdentifier = @"audio.tooloud";
|
|
|
|
+ (ORKNavigableOrderedTask *)audioTaskWithIdentifier:(NSString *)identifier
|
|
intendedUseDescription:(nullable NSString *)intendedUseDescription
|
|
speechInstruction:(nullable NSString *)speechInstruction
|
|
shortSpeechInstruction:(nullable NSString *)shortSpeechInstruction
|
|
duration:(NSTimeInterval)duration
|
|
recordingSettings:(nullable NSDictionary *)recordingSettings
|
|
checkAudioLevel:(BOOL)checkAudioLevel
|
|
options:(ORKPredefinedTaskOption)options {
|
|
|
|
recordingSettings = recordingSettings ? : @{ AVFormatIDKey : @(kAudioFormatAppleLossless),
|
|
AVNumberOfChannelsKey : @(2),
|
|
AVSampleRateKey: @(44100.0) };
|
|
|
|
if (options & ORKPredefinedTaskOptionExcludeAudio) {
|
|
@throw [NSException exceptionWithName:NSGenericException reason:@"Audio collection cannot be excluded from audio task" userInfo:nil];
|
|
}
|
|
|
|
NSMutableArray *steps = [NSMutableArray array];
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction0StepIdentifier];
|
|
step.title = ORKLocalizedString(@"AUDIO_TASK_TITLE", nil);
|
|
step.text = intendedUseDescription;
|
|
step.detailText = ORKLocalizedString(@"AUDIO_INTENDED_USE", nil);
|
|
step.image = [UIImage imageNamed:@"phonewaves" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.shouldTintImages = YES;
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier];
|
|
step.title = ORKLocalizedString(@"AUDIO_TASK_TITLE", nil);
|
|
step.text = speechInstruction ? : ORKLocalizedString(@"AUDIO_INTRO_TEXT", nil);
|
|
step.detailText = ORKLocalizedString(@"AUDIO_CALL_TO_ACTION", nil);
|
|
step.image = [UIImage imageNamed:@"phonesoundwaves" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.shouldTintImages = YES;
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
{
|
|
ORKCountdownStep *step = [[ORKCountdownStep alloc] initWithIdentifier:ORKCountdownStepIdentifier];
|
|
step.stepDuration = 5.0;
|
|
step.title = ORKLocalizedString(@"AUDIO_TASK_TITLE", nil);
|
|
|
|
// Collect audio during the countdown step too, to provide a baseline.
|
|
step.recorderConfigurations = @[[[ORKAudioRecorderConfiguration alloc] initWithIdentifier:ORKAudioRecorderIdentifier
|
|
recorderSettings:recordingSettings]];
|
|
|
|
// If checking the sound level then add text indicating that's what is happening
|
|
if (checkAudioLevel) {
|
|
step.text = ORKLocalizedString(@"AUDIO_LEVEL_CHECK_LABEL", nil);
|
|
}
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
if (checkAudioLevel) {
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKAudioTooLoudStepIdentifier];
|
|
step.title = ORKLocalizedString(@"AUDIO_TASK_TITLE", nil);
|
|
step.text = ORKLocalizedString(@"AUDIO_TOO_LOUD_MESSAGE", nil);
|
|
step.detailText = ORKLocalizedString(@"AUDIO_TOO_LOUD_ACTION_NEXT", nil);
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
ORKAudioStep *step = [[ORKAudioStep alloc] initWithIdentifier:ORKAudioStepIdentifier];
|
|
step.title = ORKLocalizedString(@"AUDIO_TASK_TITLE", nil);
|
|
step.text = shortSpeechInstruction ? : ORKLocalizedString(@"AUDIO_INSTRUCTION", nil);
|
|
step.recorderConfigurations = @[[[ORKAudioRecorderConfiguration alloc] initWithIdentifier:ORKAudioRecorderIdentifier
|
|
recorderSettings:recordingSettings]];
|
|
step.stepDuration = duration;
|
|
step.shouldContinueOnFinish = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeConclusion)) {
|
|
ORKInstructionStep *step = [self makeCompletionStep];
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
ORKNavigableOrderedTask *task = [[ORKNavigableOrderedTask alloc] initWithIdentifier:identifier steps:steps];
|
|
|
|
if (checkAudioLevel) {
|
|
|
|
// Add rules to check for audio and fail, looping back to the countdown step if required
|
|
ORKAudioLevelNavigationRule *audioRule = [[ORKAudioLevelNavigationRule alloc] initWithAudioLevelStepIdentifier:ORKCountdownStepIdentifier destinationStepIdentifier:ORKAudioStepIdentifier recordingSettings:recordingSettings];
|
|
ORKDirectStepNavigationRule *loopRule = [[ORKDirectStepNavigationRule alloc] initWithDestinationStepIdentifier:ORKCountdownStepIdentifier];
|
|
|
|
[task setNavigationRule:audioRule forTriggerStepIdentifier:ORKCountdownStepIdentifier];
|
|
[task setNavigationRule:loopRule forTriggerStepIdentifier:ORKAudioTooLoudStepIdentifier];
|
|
}
|
|
|
|
return task;
|
|
}
|
|
|
|
|
|
#pragma mark - fitnessCheckTask
|
|
|
|
NSString *const ORKFitnessWalkStepIdentifier = @"fitness.walk";
|
|
NSString *const ORKFitnessRestStepIdentifier = @"fitness.rest";
|
|
|
|
+ (ORKOrderedTask *)fitnessCheckTaskWithIdentifier:(NSString *)identifier
|
|
intendedUseDescription:(NSString *)intendedUseDescription
|
|
walkDuration:(NSTimeInterval)walkDuration
|
|
restDuration:(NSTimeInterval)restDuration
|
|
options:(ORKPredefinedTaskOption)options {
|
|
|
|
NSDateComponentsFormatter *formatter = [self textTimeFormatter];
|
|
|
|
NSMutableArray *steps = [NSMutableArray array];
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction0StepIdentifier];
|
|
step.title = ORKLocalizedString(@"FITNESS_TASK_TITLE", nil);
|
|
step.text = intendedUseDescription ? : [NSString localizedStringWithFormat:ORKLocalizedString(@"FITNESS_INTRO_TEXT_FORMAT", nil), [formatter stringFromTimeInterval:walkDuration]];
|
|
step.image = [UIImage imageNamed:@"heartbeat" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.shouldTintImages = YES;
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier];
|
|
step.title = ORKLocalizedString(@"FITNESS_TASK_TITLE", nil);
|
|
step.text = [NSString localizedStringWithFormat:ORKLocalizedString(@"FITNESS_INTRO_2_TEXT_FORMAT", nil), [formatter stringFromTimeInterval:walkDuration], [formatter stringFromTimeInterval:restDuration]];
|
|
step.image = [UIImage imageNamed:@"walkingman" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.shouldTintImages = YES;
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
{
|
|
ORKCountdownStep *step = [[ORKCountdownStep alloc] initWithIdentifier:ORKCountdownStepIdentifier];
|
|
step.title = ORKLocalizedString(@"FITNESS_TASK_TITLE", nil);
|
|
step.stepDuration = 5.0;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
if (walkDuration > 0) {
|
|
ORKFitnessStep *fitnessStep = [[ORKFitnessStep alloc] initWithIdentifier:ORKFitnessWalkStepIdentifier];
|
|
fitnessStep.stepDuration = walkDuration;
|
|
fitnessStep.title = ORKLocalizedString(@"FITNESS_TASK_TITLE", nil);
|
|
fitnessStep.text = [NSString localizedStringWithFormat:ORKLocalizedString(@"FITNESS_WALK_INSTRUCTION_FORMAT", nil), [formatter stringFromTimeInterval:walkDuration]];
|
|
fitnessStep.spokenInstruction = fitnessStep.text;
|
|
fitnessStep.recorderConfigurations = [self makeRecorderConfigurationsWithOptions:options];
|
|
fitnessStep.shouldContinueOnFinish = YES;
|
|
fitnessStep.optional = NO;
|
|
fitnessStep.shouldStartTimerAutomatically = YES;
|
|
fitnessStep.shouldTintImages = YES;
|
|
fitnessStep.image = [UIImage imageNamed:@"walkingman" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
fitnessStep.imageContentMode = UIViewContentModeCenter;
|
|
fitnessStep.shouldVibrateOnStart = YES;
|
|
fitnessStep.shouldPlaySoundOnStart = YES;
|
|
|
|
ORKStepArrayAddStep(steps, fitnessStep);
|
|
}
|
|
|
|
if (restDuration > 0) {
|
|
ORKFitnessStep *stillStep = [[ORKFitnessStep alloc] initWithIdentifier:ORKFitnessRestStepIdentifier];
|
|
stillStep.stepDuration = restDuration;
|
|
stillStep.title = ORKLocalizedString(@"FITNESS_TASK_TITLE", nil);
|
|
stillStep.text = [NSString localizedStringWithFormat:ORKLocalizedString(@"FITNESS_SIT_INSTRUCTION_FORMAT", nil), [formatter stringFromTimeInterval:restDuration]];
|
|
stillStep.spokenInstruction = stillStep.text;
|
|
stillStep.recorderConfigurations = [self makeRecorderConfigurationsWithOptions:options];
|
|
stillStep.shouldContinueOnFinish = YES;
|
|
stillStep.optional = NO;
|
|
stillStep.shouldStartTimerAutomatically = YES;
|
|
stillStep.shouldTintImages = YES;
|
|
stillStep.image = [UIImage imageNamed:@"sittingman" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
stillStep.imageContentMode = UIViewContentModeCenter;
|
|
stillStep.shouldVibrateOnStart = YES;
|
|
stillStep.shouldPlaySoundOnStart = YES;
|
|
stillStep.shouldPlaySoundOnFinish = YES;
|
|
stillStep.shouldVibrateOnFinish = YES;
|
|
|
|
ORKStepArrayAddStep(steps, stillStep);
|
|
}
|
|
}
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeConclusion)) {
|
|
ORKInstructionStep *step = [self makeCompletionStep];
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
ORKOrderedTask *task = [[ORKOrderedTask alloc] initWithIdentifier:identifier steps:steps];
|
|
|
|
return task;
|
|
}
|
|
|
|
#pragma mark - sixMinuteWalk
|
|
|
|
NSString *const ORKSixMinuteWalkStepIdentifier = @"6mwt";
|
|
NSString *const ORKSixMinuteWalkShortnessOfBreathIdentifier = @"6mwt.shortnessOfBreath";
|
|
NSString *const ORKSixMinuteWalkFatigueIdentifier = @"6mwt.fatigue";
|
|
|
|
+ (ORKOrderedTask *)sixMinuteWalkTaskWithIdentifier:(NSString *)identifier
|
|
intendedUseDescription:(NSString *)intendedUseDescription
|
|
options:(ORKPredefinedTaskOption)options {
|
|
|
|
NSTimeInterval walkDuration = 360; // 6 minutes
|
|
NSMutableArray *steps = [NSMutableArray array];
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
|
|
// Explanation Step
|
|
ORKInstructionStep *explainStep = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction0StepIdentifier];
|
|
explainStep.iconImage = [UIImage systemImageNamed:@"figure.walk"];
|
|
explainStep.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
explainStep.title = ORKLocalizedString(@"6MWT_TASK_TITLE", nil);
|
|
explainStep.text = intendedUseDescription ? : ORKLocalizedString(@"6MWT_INTRO", nil);
|
|
explainStep.bodyItems = @[
|
|
[[ORKBodyItem alloc] initWithText:ORKLocalizedString(@"6MWT_INTRO_DETAILS", nil)
|
|
detailText:nil
|
|
image:nil
|
|
learnMoreItem:nil
|
|
bodyItemStyle:ORKBodyItemStyleText],
|
|
|
|
[[ORKBodyItem alloc] initWithText:ORKLocalizedString(@"6MWT_INTRO_DETAIL_TIME", nil)
|
|
detailText:nil
|
|
image:[UIImage systemImageNamed:@"stopwatch"]
|
|
learnMoreItem:nil
|
|
bodyItemStyle:ORKBodyItemStyleImage],
|
|
|
|
[[ORKBodyItem alloc] initWithText:ORKLocalizedString(@"6MWT_INTRO_DETAIL_WATCH", nil)
|
|
detailText:nil
|
|
image:[UIImage systemImageNamed:@"applewatch"]
|
|
learnMoreItem:nil
|
|
bodyItemStyle:ORKBodyItemStyleImage],
|
|
|
|
[[ORKBodyItem alloc] initWithText:ORKLocalizedString(@"6MWT_INTRO_DETAIL_CLOTHING", nil)
|
|
detailText:nil
|
|
image:[UIImage systemImageNamed:@"figure.wave"]
|
|
learnMoreItem:nil
|
|
bodyItemStyle:ORKBodyItemStyleImage],
|
|
|
|
[[ORKBodyItem alloc] initWithText:ORKLocalizedString(@"6MWT_INTRO_DETAIL_LOCATION", nil)
|
|
detailText:nil
|
|
image:[UIImage systemImageNamed:@"location"]
|
|
learnMoreItem:nil
|
|
bodyItemStyle:ORKBodyItemStyleImage]
|
|
];
|
|
|
|
ORKStepArrayAddStep(steps, explainStep);
|
|
|
|
// Instructions Step
|
|
ORKInstructionStep *instructStep = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier];
|
|
instructStep.iconImage = [UIImage systemImageNamed:@"figure.walk"];
|
|
instructStep.title = ORKLocalizedString(@"6MWT_INSTRUCTIONS_TITLE", nil);
|
|
|
|
instructStep.bodyItems = @[
|
|
[[ORKBodyItem alloc] initWithText:ORKLocalizedString(@"6MWT_INSTRUCTIONS_1", nil)
|
|
detailText:nil
|
|
image:[UIImage systemImageNamed:@"1.circle"]
|
|
learnMoreItem:nil
|
|
bodyItemStyle:ORKBodyItemStyleImage],
|
|
|
|
[[ORKBodyItem alloc] initWithText:ORKLocalizedString(@"6MWT_INSTRUCTIONS_2", nil)
|
|
detailText:nil
|
|
image:[UIImage systemImageNamed:@"2.circle"]
|
|
learnMoreItem:nil
|
|
bodyItemStyle:ORKBodyItemStyleImage],
|
|
|
|
[[ORKBodyItem alloc] initWithText:ORKLocalizedString(@"6MWT_INSTRUCTIONS_3", nil)
|
|
detailText:nil
|
|
image:[UIImage systemImageNamed:@"3.circle"]
|
|
learnMoreItem:nil
|
|
bodyItemStyle:ORKBodyItemStyleImage],
|
|
|
|
[[ORKBodyItem alloc] initWithText:ORKLocalizedString(@"6MWT_INSTRUCTIONS_4", nil)
|
|
detailText:nil
|
|
image:[UIImage systemImageNamed:@"4.circle"]
|
|
learnMoreItem:nil
|
|
bodyItemStyle:ORKBodyItemStyleImage],
|
|
];
|
|
|
|
ORKStepArrayAddStep(steps, instructStep);
|
|
}
|
|
|
|
// Fitness Step
|
|
ORKFitnessStep *fitnessStep = [[ORKFitnessStep alloc] initWithIdentifier:ORKSixMinuteWalkStepIdentifier];
|
|
fitnessStep.stepDuration = walkDuration;
|
|
fitnessStep.title = ORKLocalizedString(@"6MWT_TEST_IN_PROGRESS", nil);
|
|
fitnessStep.text = ORKLocalizedString(@"6MWT_TEST_IN_PROGRESS_DETAIL", nil);
|
|
fitnessStep.spokenInstruction = fitnessStep.text;
|
|
fitnessStep.recorderConfigurations = [self makeRecorderConfigurationsWithOptions:options];
|
|
fitnessStep.shouldContinueOnFinish = YES;
|
|
fitnessStep.optional = NO;
|
|
fitnessStep.shouldStartTimerAutomatically = YES;
|
|
fitnessStep.shouldVibrateOnStart = YES;
|
|
fitnessStep.shouldVibrateOnFinish = YES;
|
|
fitnessStep.shouldPlaySoundOnStart = YES;
|
|
fitnessStep.shouldPlaySoundOnFinish = YES;
|
|
fitnessStep.shouldSpeakRemainingTimeAtHalfway = YES;
|
|
fitnessStep.shouldSpeakCountDown = YES;
|
|
ORKStepArrayAddStep(steps, fitnessStep);
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeConclusion)) {
|
|
|
|
// Follow Up Question Step
|
|
ORKFormStep *formStep = [[ORKFormStep alloc] initWithIdentifier:ORKFollowUpQuestions0StepIdentifier];
|
|
formStep.title = ORKLocalizedString(@"6MWT_QUESTIONS_TITLE", nil);
|
|
formStep.detailText = ORKLocalizedString(@"6MWT_QUESTIONS_DETAIL", nil);
|
|
formStep.showsProgress = NO;
|
|
formStep.formItems = @[
|
|
[[ORKFormItem alloc] initWithIdentifier:ORKSixMinuteWalkShortnessOfBreathIdentifier
|
|
text:ORKLocalizedString(@"6MWT_QUESTIONS_BREATH", nil)
|
|
detailText:nil
|
|
learnMoreItem:nil
|
|
showsProgress:NO
|
|
answerFormat:[ORKAnswerFormat
|
|
scaleAnswerFormatWithMaximumValue:10
|
|
minimumValue:1
|
|
defaultValue:5
|
|
step:1
|
|
vertical:NO
|
|
maximumValueDescription:ORKLocalizedString(@"6MWT_HIGH_SCORE", nil)
|
|
minimumValueDescription:ORKLocalizedString(@"6MWT_LOW_SCORE", nil)]
|
|
tagText:nil
|
|
optional:NO],
|
|
|
|
[[ORKFormItem alloc] initWithIdentifier:ORKSixMinuteWalkFatigueIdentifier
|
|
text:ORKLocalizedString(@"6MWT_QUESTIONS_FATIGUE", nil)
|
|
detailText:nil
|
|
learnMoreItem:nil
|
|
showsProgress:NO
|
|
answerFormat:[ORKAnswerFormat
|
|
scaleAnswerFormatWithMaximumValue:10
|
|
minimumValue:1
|
|
defaultValue:5
|
|
step:1
|
|
vertical:NO
|
|
maximumValueDescription:ORKLocalizedString(@"6MWT_HIGH_SCORE", nil)
|
|
minimumValueDescription:ORKLocalizedString(@"6MWT_LOW_SCORE", nil)]
|
|
tagText:nil
|
|
optional:NO]
|
|
];
|
|
ORKStepArrayAddStep(steps, formStep);
|
|
|
|
// Completion Step
|
|
ORKInstructionStep *completionStep = [self makeCompletionStep];
|
|
ORKStepArrayAddStep(steps, completionStep);
|
|
}
|
|
|
|
ORKOrderedTask *task = [[ORKOrderedTask alloc] initWithIdentifier:identifier steps:steps];
|
|
return task;
|
|
}
|
|
|
|
#pragma mark - tecusehCubeTask
|
|
|
|
NSString *const ORKTecumsehCubeStepIdentifier = @"tecumseh";
|
|
|
|
+ (ORKOrderedTask *)tecumsehCubeTaskWithIdentifier:(NSString *)identifier
|
|
intendedUseDescription:(nullable NSString *)intendedUseDescription
|
|
audioBundleIdentifier:(NSString *)audioBundleIdentifier
|
|
audioResourceName:(NSString *)audioResourceName
|
|
audioFileExtension:(nullable NSString*)audioFileExtension
|
|
options:(ORKPredefinedTaskOption)options {
|
|
|
|
NSTimeInterval stepDuration = 180; // 3 minutes
|
|
NSTimeInterval restDuration = 180; // 3 minutes
|
|
|
|
NSMutableArray *steps = [NSMutableArray array];
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
|
|
// Explanation Step
|
|
ORKInstructionStep *explainStep = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction0StepIdentifier];
|
|
explainStep.iconImage = [UIImage systemImageNamed:@"cube"];
|
|
explainStep.title = ORKLocalizedString(@"TC_TASK_TITLE", nil);
|
|
explainStep.text = intendedUseDescription ? : ORKLocalizedString(@"TC_INTRO", nil);
|
|
explainStep.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
explainStep.bodyItems = @[
|
|
[[ORKBodyItem alloc] initWithText:ORKLocalizedString(@"TC_INTRO_DETAILS", nil)
|
|
detailText:nil
|
|
image:nil
|
|
learnMoreItem:nil
|
|
bodyItemStyle:ORKBodyItemStyleText],
|
|
|
|
[[ORKBodyItem alloc] initWithText:ORKLocalizedString(@"TC_INTRO_DETAIL_TIME", nil)
|
|
detailText:nil
|
|
image:[UIImage systemImageNamed:@"stopwatch"]
|
|
learnMoreItem:nil
|
|
bodyItemStyle:ORKBodyItemStyleImage],
|
|
|
|
[[ORKBodyItem alloc] initWithText:ORKLocalizedString(@"TC_INTRO_DETAIL_WATCH", nil)
|
|
detailText:nil
|
|
image:[UIImage systemImageNamed:@"applewatch"]
|
|
learnMoreItem:nil
|
|
bodyItemStyle:ORKBodyItemStyleImage],
|
|
|
|
[[ORKBodyItem alloc] initWithText:ORKLocalizedString(@"TC_INTRO_DETAIL_DANGER", nil)
|
|
detailText:nil
|
|
image:[UIImage systemImageNamed:@"exclamationmark.triangle"]
|
|
learnMoreItem:nil
|
|
bodyItemStyle:ORKBodyItemStyleImage]
|
|
];
|
|
|
|
ORKStepArrayAddStep(steps, explainStep);
|
|
|
|
// Instructions Step
|
|
ORKInstructionStep *instructStep = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier];
|
|
instructStep.iconImage = [UIImage systemImageNamed:@"cube"];
|
|
instructStep.title = ORKLocalizedString(@"TC_INSTRUCTIONS_TITLE", nil);
|
|
instructStep.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
instructStep.bodyItems = @[
|
|
[[ORKBodyItem alloc] initWithText:ORKLocalizedString(@"TC_INSTRUCTIONS_1", nil)
|
|
detailText:nil
|
|
image:[UIImage systemImageNamed:@"1.circle"]
|
|
learnMoreItem:nil
|
|
bodyItemStyle:ORKBodyItemStyleImage],
|
|
|
|
[[ORKBodyItem alloc] initWithText:ORKLocalizedString(@"TC_INSTRUCTIONS_2", nil)
|
|
detailText:nil
|
|
image:[UIImage systemImageNamed:@"2.circle"]
|
|
learnMoreItem:nil
|
|
bodyItemStyle:ORKBodyItemStyleImage],
|
|
|
|
[[ORKBodyItem alloc] initWithText:ORKLocalizedString(@"TC_INSTRUCTIONS_3", nil)
|
|
detailText:nil
|
|
image:[UIImage systemImageNamed:@"3.circle"]
|
|
learnMoreItem:nil
|
|
bodyItemStyle:ORKBodyItemStyleImage]
|
|
];
|
|
|
|
ORKStepArrayAddStep(steps, instructStep);
|
|
}
|
|
|
|
// Fitness Step
|
|
ORKBundleAsset *audio = [[ORKBundleAsset alloc] initWithName:audioResourceName
|
|
bundleIdentifier:audioBundleIdentifier
|
|
fileExtension:audioFileExtension];
|
|
|
|
ORKAudioFitnessStep *cubeStep = [[ORKAudioFitnessStep alloc] initWithIdentifier:identifier
|
|
audioAsset:audio
|
|
vocalCues:nil];
|
|
|
|
cubeStep.stepDuration = stepDuration;
|
|
cubeStep.title = ORKLocalizedString(@"TC_TEST_IN_PROGRESS", nil);
|
|
cubeStep.text = ORKLocalizedString(@"TC_TEST_IN_PROGRESS_DETAIL", nil);
|
|
cubeStep.spokenInstruction = cubeStep.text;
|
|
cubeStep.recorderConfigurations = [self makeRecorderConfigurationsWithOptions:options];
|
|
cubeStep.shouldContinueOnFinish = YES;
|
|
cubeStep.optional = NO;
|
|
cubeStep.shouldStartTimerAutomatically = YES;
|
|
cubeStep.shouldVibrateOnStart = YES;
|
|
cubeStep.shouldVibrateOnFinish = YES;
|
|
cubeStep.shouldPlaySoundOnStart = YES;
|
|
cubeStep.shouldPlaySoundOnFinish = YES;
|
|
cubeStep.shouldSpeakRemainingTimeAtHalfway = YES;
|
|
cubeStep.shouldSpeakCountDown = YES;
|
|
ORKStepArrayAddStep(steps, cubeStep);
|
|
|
|
// Rest Step
|
|
ORKFitnessStep *stillStep = [[ORKFitnessStep alloc] initWithIdentifier:ORKFitnessRestStepIdentifier];
|
|
stillStep.stepDuration = restDuration;
|
|
stillStep.title = ORKLocalizedString(@"TC_REST_IN_PROGRESS", nil);
|
|
stillStep.text = ORKLocalizedString(@"TC_REST_IN_PROGRESS_DETAIL", nil);
|
|
stillStep.spokenInstruction = stillStep.text;
|
|
stillStep.recorderConfigurations = [self makeRecorderConfigurationsWithOptions:options];
|
|
stillStep.shouldContinueOnFinish = YES;
|
|
stillStep.optional = NO;
|
|
stillStep.shouldStartTimerAutomatically = YES;
|
|
stillStep.shouldVibrateOnStart = YES;
|
|
stillStep.shouldVibrateOnFinish = YES;
|
|
stillStep.shouldPlaySoundOnStart = YES;
|
|
stillStep.shouldPlaySoundOnFinish = YES;
|
|
stillStep.shouldSpeakRemainingTimeAtHalfway = YES;
|
|
stillStep.shouldSpeakCountDown = YES;
|
|
ORKStepArrayAddStep(steps, stillStep);
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeConclusion)) {
|
|
ORKInstructionStep *completionStep = [self makeCompletionStep];
|
|
ORKStepArrayAddStep(steps, completionStep);
|
|
}
|
|
|
|
ORKOrderedTask *task = [[ORKOrderedTask alloc] initWithIdentifier:identifier steps:steps];
|
|
return task;
|
|
}
|
|
|
|
#pragma mark - shortWalkTask
|
|
|
|
NSString *const ORKShortWalkOutboundStepIdentifier = @"walking.outbound";
|
|
NSString *const ORKShortWalkReturnStepIdentifier = @"walking.return";
|
|
NSString *const ORKShortWalkRestStepIdentifier = @"walking.rest";
|
|
|
|
+ (ORKOrderedTask *)shortWalkTaskWithIdentifier:(NSString *)identifier
|
|
intendedUseDescription:(NSString *)intendedUseDescription
|
|
numberOfStepsPerLeg:(NSInteger)numberOfStepsPerLeg
|
|
restDuration:(NSTimeInterval)restDuration
|
|
options:(ORKPredefinedTaskOption)options {
|
|
|
|
NSDateComponentsFormatter *formatter = [self textTimeFormatter];
|
|
|
|
NSMutableArray *steps = [NSMutableArray array];
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction0StepIdentifier];
|
|
step.title = ORKLocalizedString(@"WALK_TASK_TITLE", nil);
|
|
step.text = intendedUseDescription;
|
|
step.detailText = ORKLocalizedString(@"WALK_INTRO_TEXT", nil);
|
|
step.shouldTintImages = YES;
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier];
|
|
step.title = ORKLocalizedString(@"WALK_TASK_TITLE", nil);
|
|
step.text = [NSString localizedStringWithFormat:ORKLocalizedString(@"WALK_INTRO_2_TEXT_%ld", nil),numberOfStepsPerLeg];
|
|
step.detailText = ORKLocalizedString(@"WALK_INTRO_2_DETAIL", nil);
|
|
step.image = [UIImage imageNamed:@"pocket" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.shouldTintImages = YES;
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
{
|
|
ORKCountdownStep *step = [[ORKCountdownStep alloc] initWithIdentifier:ORKCountdownStepIdentifier];
|
|
step.title = ORKLocalizedString(@"WALK_TASK_TITLE", nil);
|
|
step.stepDuration = 5.0;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
{
|
|
NSMutableArray *recorderConfigurations = [NSMutableArray array];
|
|
if (!(ORKPredefinedTaskOptionExcludePedometer & options)) {
|
|
[recorderConfigurations addObject:[[ORKPedometerRecorderConfiguration alloc] initWithIdentifier:ORKPedometerRecorderIdentifier]];
|
|
}
|
|
if (!(ORKPredefinedTaskOptionExcludeAccelerometer & options)) {
|
|
[recorderConfigurations addObject:[[ORKAccelerometerRecorderConfiguration alloc] initWithIdentifier:ORKAccelerometerRecorderIdentifier
|
|
frequency:100]];
|
|
}
|
|
if (!(ORKPredefinedTaskOptionExcludeDeviceMotion & options)) {
|
|
[recorderConfigurations addObject:[[ORKDeviceMotionRecorderConfiguration alloc] initWithIdentifier:ORKDeviceMotionRecorderIdentifier
|
|
frequency:100]];
|
|
}
|
|
|
|
ORKWalkingTaskStep *walkingStep = [[ORKWalkingTaskStep alloc] initWithIdentifier:ORKShortWalkOutboundStepIdentifier];
|
|
walkingStep.numberOfStepsPerLeg = numberOfStepsPerLeg;
|
|
walkingStep.title = ORKLocalizedString(@"WALK_TASK_TITLE", nil);
|
|
walkingStep.text = [NSString localizedStringWithFormat:ORKLocalizedString(@"WALK_OUTBOUND_INSTRUCTION_FORMAT", nil), (long long)numberOfStepsPerLeg];
|
|
walkingStep.spokenInstruction = walkingStep.text;
|
|
walkingStep.recorderConfigurations = recorderConfigurations;
|
|
walkingStep.shouldContinueOnFinish = YES;
|
|
walkingStep.optional = NO;
|
|
walkingStep.shouldStartTimerAutomatically = YES;
|
|
walkingStep.stepDuration = numberOfStepsPerLeg * 1.5; // fallback duration in case no step count
|
|
walkingStep.shouldVibrateOnStart = YES;
|
|
walkingStep.shouldPlaySoundOnStart = YES;
|
|
|
|
ORKStepArrayAddStep(steps, walkingStep);
|
|
}
|
|
|
|
{
|
|
NSMutableArray *recorderConfigurations = [NSMutableArray array];
|
|
if (!(ORKPredefinedTaskOptionExcludePedometer & options)) {
|
|
[recorderConfigurations addObject:[[ORKPedometerRecorderConfiguration alloc] initWithIdentifier:ORKPedometerRecorderIdentifier]];
|
|
}
|
|
if (!(ORKPredefinedTaskOptionExcludeAccelerometer & options)) {
|
|
[recorderConfigurations addObject:[[ORKAccelerometerRecorderConfiguration alloc] initWithIdentifier:ORKAccelerometerRecorderIdentifier
|
|
frequency:100]];
|
|
}
|
|
if (!(ORKPredefinedTaskOptionExcludeDeviceMotion & options)) {
|
|
[recorderConfigurations addObject:[[ORKDeviceMotionRecorderConfiguration alloc] initWithIdentifier:ORKDeviceMotionRecorderIdentifier
|
|
frequency:100]];
|
|
}
|
|
|
|
ORKWalkingTaskStep *walkingStep = [[ORKWalkingTaskStep alloc] initWithIdentifier:ORKShortWalkReturnStepIdentifier];
|
|
walkingStep.numberOfStepsPerLeg = numberOfStepsPerLeg;
|
|
walkingStep.title = ORKLocalizedString(@"WALK_TASK_TITLE", nil);
|
|
walkingStep.text = [NSString localizedStringWithFormat:ORKLocalizedString(@"WALK_RETURN_INSTRUCTION_FORMAT", nil), (long long)numberOfStepsPerLeg];
|
|
walkingStep.spokenInstruction = walkingStep.text;
|
|
walkingStep.recorderConfigurations = recorderConfigurations;
|
|
walkingStep.shouldContinueOnFinish = YES;
|
|
walkingStep.shouldStartTimerAutomatically = YES;
|
|
walkingStep.optional = NO;
|
|
walkingStep.stepDuration = numberOfStepsPerLeg * 1.5; // fallback duration in case no step count
|
|
walkingStep.shouldVibrateOnStart = YES;
|
|
walkingStep.shouldPlaySoundOnStart = YES;
|
|
|
|
ORKStepArrayAddStep(steps, walkingStep);
|
|
}
|
|
|
|
if (restDuration > 0) {
|
|
NSMutableArray *recorderConfigurations = [NSMutableArray array];
|
|
if (!(ORKPredefinedTaskOptionExcludeAccelerometer & options)) {
|
|
[recorderConfigurations addObject:[[ORKAccelerometerRecorderConfiguration alloc] initWithIdentifier:ORKAccelerometerRecorderIdentifier
|
|
frequency:100]];
|
|
}
|
|
if (!(ORKPredefinedTaskOptionExcludeDeviceMotion & options)) {
|
|
[recorderConfigurations addObject:[[ORKDeviceMotionRecorderConfiguration alloc] initWithIdentifier:ORKDeviceMotionRecorderIdentifier
|
|
frequency:100]];
|
|
}
|
|
|
|
ORKFitnessStep *activeStep = [[ORKFitnessStep alloc] initWithIdentifier:ORKShortWalkRestStepIdentifier];
|
|
activeStep.recorderConfigurations = recorderConfigurations;
|
|
NSString *durationString = [formatter stringFromTimeInterval:restDuration];
|
|
activeStep.title = ORKLocalizedString(@"WALK_TASK_TITLE", nil);
|
|
activeStep.text = [NSString localizedStringWithFormat:ORKLocalizedString(@"WALK_STAND_INSTRUCTION_FORMAT", nil), durationString];
|
|
activeStep.spokenInstruction = [NSString localizedStringWithFormat:ORKLocalizedString(@"WALK_STAND_VOICE_INSTRUCTION_FORMAT", nil), durationString];
|
|
activeStep.shouldStartTimerAutomatically = YES;
|
|
activeStep.stepDuration = restDuration;
|
|
activeStep.shouldContinueOnFinish = YES;
|
|
activeStep.optional = NO;
|
|
activeStep.shouldVibrateOnStart = YES;
|
|
activeStep.shouldPlaySoundOnStart = YES;
|
|
activeStep.shouldVibrateOnFinish = YES;
|
|
activeStep.shouldPlaySoundOnFinish = YES;
|
|
|
|
ORKStepArrayAddStep(steps, activeStep);
|
|
}
|
|
}
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeConclusion)) {
|
|
ORKInstructionStep *step = [self makeCompletionStep];
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
ORKOrderedTask *task = [[ORKOrderedTask alloc] initWithIdentifier:identifier steps:steps];
|
|
return task;
|
|
}
|
|
|
|
|
|
#pragma mark - walkBackAndForthTask
|
|
|
|
+ (ORKOrderedTask *)walkBackAndForthTaskWithIdentifier:(NSString *)identifier
|
|
intendedUseDescription:(NSString *)intendedUseDescription
|
|
walkDuration:(NSTimeInterval)walkDuration
|
|
restDuration:(NSTimeInterval)restDuration
|
|
options:(ORKPredefinedTaskOption)options {
|
|
|
|
NSDateComponentsFormatter *formatter = [self textTimeFormatter];
|
|
formatter.unitsStyle = NSDateComponentsFormatterUnitsStyleFull;
|
|
|
|
NSMutableArray *steps = [NSMutableArray array];
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction0StepIdentifier];
|
|
step.title = ORKLocalizedString(@"WALK_TASK_TITLE", nil);
|
|
step.text = intendedUseDescription;
|
|
step.detailText = ORKLocalizedString(@"WALK_INTRO_TEXT", nil);
|
|
step.shouldTintImages = YES;
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier];
|
|
step.title = ORKLocalizedString(@"WALK_TASK_TITLE", nil);
|
|
step.text = ORKLocalizedString(@"WALK_INTRO_2_TEXT_BACK_AND_FORTH_INSTRUCTION", nil);
|
|
step.detailText = ORKLocalizedString(@"WALK_INTRO_2_DETAIL_BACK_AND_FORTH_INSTRUCTION", nil);
|
|
step.image = [UIImage imageNamed:@"pocket" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
{
|
|
ORKCountdownStep *step = [[ORKCountdownStep alloc] initWithIdentifier:ORKCountdownStepIdentifier];
|
|
step.title = ORKLocalizedString(@"WALK_TASK_TITLE", nil);
|
|
step.stepDuration = 5.0;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
{
|
|
NSMutableArray *recorderConfigurations = [NSMutableArray array];
|
|
if (!(ORKPredefinedTaskOptionExcludePedometer & options)) {
|
|
[recorderConfigurations addObject:[[ORKPedometerRecorderConfiguration alloc] initWithIdentifier:ORKPedometerRecorderIdentifier]];
|
|
}
|
|
if (!(ORKPredefinedTaskOptionExcludeAccelerometer & options)) {
|
|
[recorderConfigurations addObject:[[ORKAccelerometerRecorderConfiguration alloc] initWithIdentifier:ORKAccelerometerRecorderIdentifier
|
|
frequency:100]];
|
|
}
|
|
if (!(ORKPredefinedTaskOptionExcludeDeviceMotion & options)) {
|
|
[recorderConfigurations addObject:[[ORKDeviceMotionRecorderConfiguration alloc] initWithIdentifier:ORKDeviceMotionRecorderIdentifier
|
|
frequency:100]];
|
|
}
|
|
|
|
ORKWalkingTaskStep *walkingStep = [[ORKWalkingTaskStep alloc] initWithIdentifier:ORKShortWalkOutboundStepIdentifier];
|
|
walkingStep.numberOfStepsPerLeg = 1000; // Set the number of steps very high so it is ignored
|
|
NSString *walkingDurationString = [formatter stringFromTimeInterval:walkDuration];
|
|
walkingStep.title = ORKLocalizedString(@"WALK_TASK_TITLE", nil);
|
|
|
|
walkingStep.text = [NSString localizedStringWithFormat:ORKLocalizedString(@"WALK_BACK_AND_FORTH_INSTRUCTION_FORMAT", nil), walkingDurationString];
|
|
walkingStep.spokenInstruction = walkingStep.text;
|
|
walkingStep.recorderConfigurations = recorderConfigurations;
|
|
walkingStep.shouldContinueOnFinish = YES;
|
|
walkingStep.optional = NO;
|
|
walkingStep.shouldStartTimerAutomatically = YES;
|
|
walkingStep.stepDuration = walkDuration; // Set the walking duration to the step duration
|
|
walkingStep.shouldVibrateOnStart = YES;
|
|
walkingStep.shouldPlaySoundOnStart = YES;
|
|
walkingStep.shouldSpeakRemainingTimeAtHalfway = (walkDuration > 20);
|
|
|
|
ORKStepArrayAddStep(steps, walkingStep);
|
|
}
|
|
|
|
if (restDuration > 0) {
|
|
NSMutableArray *recorderConfigurations = [NSMutableArray array];
|
|
if (!(ORKPredefinedTaskOptionExcludeAccelerometer & options)) {
|
|
[recorderConfigurations addObject:[[ORKAccelerometerRecorderConfiguration alloc] initWithIdentifier:ORKAccelerometerRecorderIdentifier
|
|
frequency:100]];
|
|
}
|
|
if (!(ORKPredefinedTaskOptionExcludeDeviceMotion & options)) {
|
|
[recorderConfigurations addObject:[[ORKDeviceMotionRecorderConfiguration alloc] initWithIdentifier:ORKDeviceMotionRecorderIdentifier
|
|
frequency:100]];
|
|
}
|
|
|
|
ORKFitnessStep *activeStep = [[ORKFitnessStep alloc] initWithIdentifier:ORKShortWalkRestStepIdentifier];
|
|
activeStep.recorderConfigurations = recorderConfigurations;
|
|
NSString *durationString = [formatter stringFromTimeInterval:restDuration];
|
|
activeStep.title = ORKLocalizedString(@"WALK_TASK_TITLE", nil);
|
|
activeStep.text = [NSString localizedStringWithFormat:ORKLocalizedString(@"WALK_BACK_AND_FORTH_STAND_INSTRUCTION_FORMAT", nil), durationString];
|
|
activeStep.spokenInstruction = activeStep.text;
|
|
activeStep.shouldStartTimerAutomatically = YES;
|
|
activeStep.stepDuration = restDuration;
|
|
activeStep.shouldContinueOnFinish = YES;
|
|
activeStep.optional = NO;
|
|
activeStep.shouldVibrateOnStart = YES;
|
|
activeStep.shouldPlaySoundOnStart = YES;
|
|
activeStep.shouldVibrateOnFinish = YES;
|
|
activeStep.shouldPlaySoundOnFinish = YES;
|
|
activeStep.finishedSpokenInstruction = ORKLocalizedString(@"WALK_BACK_AND_FORTH_FINISHED_VOICE", nil);
|
|
activeStep.shouldSpeakRemainingTimeAtHalfway = (restDuration > 20);
|
|
|
|
ORKStepArrayAddStep(steps, activeStep);
|
|
}
|
|
}
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeConclusion)) {
|
|
ORKInstructionStep *step = [self makeCompletionStep];
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
ORKOrderedTask *task = [[ORKOrderedTask alloc] initWithIdentifier:identifier steps:steps];
|
|
return task;
|
|
}
|
|
|
|
|
|
#pragma mark - kneeRangeOfMotionTask
|
|
|
|
NSString *const ORKKneeRangeOfMotionStepIdentifier = @"knee.range.of.motion";
|
|
|
|
+ (ORKOrderedTask *)kneeRangeOfMotionTaskWithIdentifier:(NSString *)identifier
|
|
limbOption:(ORKPredefinedTaskLimbOption)limbOption
|
|
intendedUseDescription:(NSString *)intendedUseDescription
|
|
options:(ORKPredefinedTaskOption)options {
|
|
NSMutableArray *steps = [NSMutableArray array];
|
|
NSString *limbType = ORKLocalizedString(@"LIMB_RIGHT", nil);
|
|
UIImage *kneeStartImage = [UIImage imageNamed:@"knee_start_right" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
UIImage *kneeMaximumImage = [UIImage imageNamed:@"knee_maximum_right" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
|
|
if (limbOption == ORKPredefinedTaskLimbOptionLeft) {
|
|
limbType = ORKLocalizedString(@"LIMB_LEFT", nil);
|
|
|
|
kneeStartImage = [UIImage imageNamed:@"knee_start_left" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
kneeMaximumImage = [UIImage imageNamed:@"knee_maximum_left" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
}
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
ORKInstructionStep *instructionStep = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction0StepIdentifier];
|
|
instructionStep.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
instructionStep.title = ORKLocalizedString(@"RANGE_OF_MOTION_TITLE", nil);
|
|
instructionStep.text = intendedUseDescription;
|
|
instructionStep.shouldTintImages = YES;
|
|
instructionStep.iconImage = kneeStartImage;
|
|
instructionStep.imageContentMode = UIViewContentModeCenter;
|
|
instructionStep.bodyItems = @[
|
|
|
|
[[ORKBodyItem alloc] initWithText:
|
|
([limbType isEqualToString:ORKLocalizedString(@"LIMB_LEFT", nil)])?
|
|
ORKLocalizedString(@"KNEE_RANGE_OF_MOTION_TEXT_INSTRUCTION_0_LEFT", nil) :
|
|
ORKLocalizedString(@"KNEE_RANGE_OF_MOTION_TEXT_INSTRUCTION_0_RIGHT", nil)
|
|
detailText:nil
|
|
image:[UIImage systemImageNamed:@"1.circle.fill"]
|
|
learnMoreItem:nil
|
|
bodyItemStyle:ORKBodyItemStyleImage],
|
|
|
|
[[ORKBodyItem alloc] initWithText:
|
|
([limbType isEqualToString:ORKLocalizedString(@"LIMB_LEFT", nil)])?
|
|
ORKLocalizedString(@"KNEE_RANGE_OF_MOTION_TEXT_INSTRUCTION_1_LEFT", nil) :
|
|
ORKLocalizedString(@"KNEE_RANGE_OF_MOTION_TEXT_INSTRUCTION_1_RIGHT", nil)
|
|
detailText:nil
|
|
image:[UIImage systemImageNamed:@"2.circle.fill"]
|
|
learnMoreItem:nil
|
|
bodyItemStyle:ORKBodyItemStyleImage],
|
|
|
|
[[ORKBodyItem alloc] initWithText:
|
|
([limbType isEqualToString:ORKLocalizedString(@"LIMB_LEFT", nil)])?
|
|
ORKLocalizedString(@"KNEE_RANGE_OF_MOTION_TEXT_INSTRUCTION_2_LEFT", nil) :
|
|
ORKLocalizedString(@"KNEE_RANGE_OF_MOTION_TEXT_INSTRUCTION_2_RIGHT", nil)
|
|
detailText:nil
|
|
image:[UIImage systemImageNamed:@"3.circle.fill"]
|
|
learnMoreItem:nil
|
|
bodyItemStyle:ORKBodyItemStyleImage],
|
|
|
|
[[ORKBodyItem alloc] initWithText:
|
|
([limbType isEqualToString:ORKLocalizedString(@"LIMB_LEFT", nil)])?
|
|
ORKLocalizedString(@"KNEE_RANGE_OF_MOTION_TEXT_INSTRUCTION_3_LEFT", nil) :
|
|
ORKLocalizedString(@"KNEE_RANGE_OF_MOTION_TEXT_INSTRUCTION_3_RIGHT", nil)
|
|
detailText:nil
|
|
image:[UIImage systemImageNamed:@"4.circle.fill"]
|
|
learnMoreItem:nil
|
|
bodyItemStyle:ORKBodyItemStyleImage],
|
|
];
|
|
|
|
ORKStepArrayAddStep(steps, instructionStep);
|
|
}
|
|
|
|
NSString *instructionText = ([limbType isEqualToString:ORKLocalizedString(@"LIMB_LEFT", nil)])? ORKLocalizedString(@"KNEE_RANGE_OF_MOTION_TOUCH_ANYWHERE_STEP_INSTRUCTION_LEFT", nil) : ORKLocalizedString(@"KNEE_RANGE_OF_MOTION_TOUCH_ANYWHERE_STEP_INSTRUCTION_RIGHT", nil);
|
|
ORKTouchAnywhereStep *touchAnywhereStep = [[ORKTouchAnywhereStep alloc] initWithIdentifier:ORKTouchAnywhereStepIdentifier instructionText:instructionText];
|
|
touchAnywhereStep.title = ORKLocalizedString(@"RANGE_OF_MOTION_TITLE", nil);
|
|
touchAnywhereStep.image = kneeMaximumImage;
|
|
touchAnywhereStep.imageContentMode = UIViewContentModeCenter;
|
|
ORKStepArrayAddStep(steps, touchAnywhereStep);
|
|
|
|
touchAnywhereStep.spokenInstruction = touchAnywhereStep.text;
|
|
|
|
ORKDeviceMotionRecorderConfiguration *deviceMotionRecorderConfig = [[ORKDeviceMotionRecorderConfiguration alloc] initWithIdentifier:ORKDeviceMotionRecorderIdentifier frequency:100];
|
|
|
|
ORKRangeOfMotionStep *kneeRangeOfMotionStep = [[ORKRangeOfMotionStep alloc] initWithIdentifier:ORKKneeRangeOfMotionStepIdentifier limbOption:limbOption];
|
|
kneeRangeOfMotionStep.image = kneeStartImage;
|
|
kneeRangeOfMotionStep.imageContentMode = UIViewContentModeCenter;
|
|
kneeRangeOfMotionStep.title = ORKLocalizedString(@"RANGE_OF_MOTION_TITLE", nil);
|
|
kneeRangeOfMotionStep.text = ([limbType isEqualToString: ORKLocalizedString(@"LIMB_LEFT", nil)])? ORKLocalizedString(@"KNEE_RANGE_OF_MOTION_SPOKEN_INSTRUCTION_LEFT", nil) :
|
|
ORKLocalizedString(@"KNEE_RANGE_OF_MOTION_SPOKEN_INSTRUCTION_RIGHT", nil);
|
|
|
|
kneeRangeOfMotionStep.spokenInstruction = kneeRangeOfMotionStep.text;
|
|
kneeRangeOfMotionStep.recorderConfigurations = @[deviceMotionRecorderConfig];
|
|
kneeRangeOfMotionStep.optional = NO;
|
|
|
|
ORKStepArrayAddStep(steps, kneeRangeOfMotionStep);
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeConclusion)) {
|
|
ORKCompletionStep *completionStep = [self makeCompletionStep];
|
|
ORKStepArrayAddStep(steps, completionStep);
|
|
}
|
|
|
|
ORKOrderedTask *task = [[ORKOrderedTask alloc] initWithIdentifier:identifier steps:steps];
|
|
return task;
|
|
}
|
|
|
|
|
|
#pragma mark - shoulderRangeOfMotionTask
|
|
|
|
NSString *const ORKShoulderRangeOfMotionStepIdentifier = @"shoulder.range.of.motion";
|
|
|
|
+ (ORKOrderedTask *)shoulderRangeOfMotionTaskWithIdentifier:(NSString *)identifier
|
|
limbOption:(ORKPredefinedTaskLimbOption)limbOption
|
|
intendedUseDescription:(NSString *)intendedUseDescription
|
|
options:(ORKPredefinedTaskOption)options {
|
|
NSMutableArray *steps = [NSMutableArray array];
|
|
NSString *limbType = ORKLocalizedString(@"LIMB_RIGHT", nil);
|
|
UIImage *shoulderStartImage = [UIImage imageNamed:@"shoulder_start_right" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
UIImage *shoulderMaximumImage = [UIImage imageNamed:@"shoulder_maximum_right" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
|
|
if (limbOption == ORKPredefinedTaskLimbOptionLeft) {
|
|
limbType = ORKLocalizedString(@"LIMB_LEFT", nil);
|
|
shoulderStartImage = [UIImage imageNamed:@"shoulder_start_left" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
shoulderMaximumImage = [UIImage imageNamed:@"shoulder_maximum_left" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
}
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
ORKInstructionStep *instructionStep0 = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction0StepIdentifier];
|
|
instructionStep0.title = ORKLocalizedString(@"RANGE_OF_MOTION_TITLE", nil);
|
|
instructionStep0.text = intendedUseDescription;
|
|
instructionStep0.detailText = ([limbType isEqualToString:ORKLocalizedString(@"LIMB_LEFT", nil)])? ORKLocalizedString(@"SHOULDER_RANGE_OF_MOTION_TEXT_INSTRUCTION_0_LEFT", nil) : ORKLocalizedString(@"SHOULDER_RANGE_OF_MOTION_TEXT_INSTRUCTION_0_RIGHT", nil);
|
|
instructionStep0.shouldTintImages = YES;
|
|
instructionStep0.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
instructionStep0.imageContentMode = UIViewContentModeCenter;
|
|
ORKStepArrayAddStep(steps, instructionStep0);
|
|
|
|
ORKInstructionStep *instructionStep1 = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier];
|
|
instructionStep1.title = ORKLocalizedString(@"RANGE_OF_MOTION_TITLE", nil);
|
|
instructionStep1.text = ([limbType isEqualToString:ORKLocalizedString(@"LIMB_LEFT", nil)])? ORKLocalizedString(@"SHOULDER_RANGE_OF_MOTION_TEXT_INSTRUCTION_1_LEFT", nil) : ORKLocalizedString(@"SHOULDER_RANGE_OF_MOTION_TEXT_INSTRUCTION_1_RIGHT", nil);
|
|
instructionStep1.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
ORKStepArrayAddStep(steps, instructionStep1);
|
|
|
|
ORKInstructionStep *instructionStep2 = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction2StepIdentifier];
|
|
instructionStep2.title = ORKLocalizedString(@"RANGE_OF_MOTION_TITLE", nil);
|
|
instructionStep2.text = ([limbType isEqualToString:ORKLocalizedString(@"LIMB_LEFT", nil)])? ORKLocalizedString(@"SHOULDER_RANGE_OF_MOTION_TEXT_INSTRUCTION_2_LEFT", nil) : ORKLocalizedString(@"SHOULDER_RANGE_OF_MOTION_TEXT_INSTRUCTION_2_RIGHT", nil);
|
|
instructionStep2.image = shoulderStartImage;
|
|
instructionStep2.imageContentMode = UIViewContentModeCenter;
|
|
instructionStep2.shouldTintImages = YES;
|
|
instructionStep2.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
ORKStepArrayAddStep(steps, instructionStep2);
|
|
|
|
ORKInstructionStep *instructionStep3 = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction3StepIdentifier];
|
|
instructionStep3.title = ORKLocalizedString(@"RANGE_OF_MOTION_TITLE", nil);
|
|
instructionStep3.text = ([limbType isEqualToString:ORKLocalizedString(@"LIMB_LEFT", nil)])? ORKLocalizedString(@"SHOULDER_RANGE_OF_MOTION_TEXT_INSTRUCTION_3_LEFT", nil) : ORKLocalizedString(@"SHOULDER_RANGE_OF_MOTION_TEXT_INSTRUCTION_3_RIGHT", nil);
|
|
instructionStep3.image = shoulderMaximumImage;
|
|
instructionStep3.imageContentMode = UIViewContentModeCenter;
|
|
instructionStep3.shouldTintImages = YES;
|
|
instructionStep3.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
ORKStepArrayAddStep(steps, instructionStep3);
|
|
}
|
|
|
|
NSString *instructionText = ([limbType isEqualToString:ORKLocalizedString(@"LIMB_LEFT", nil)])? ORKLocalizedString(@"SHOULDER_RANGE_OF_MOTION_TOUCH_ANYWHERE_STEP_INSTRUCTION_LEFT", nil) : ORKLocalizedString(@"SHOULDER_RANGE_OF_MOTION_TOUCH_ANYWHERE_STEP_INSTRUCTION_RIGHT", nil);
|
|
ORKTouchAnywhereStep *touchAnywhereStep = [[ORKTouchAnywhereStep alloc] initWithIdentifier:ORKTouchAnywhereStepIdentifier instructionText:instructionText];
|
|
touchAnywhereStep.title = ORKLocalizedString(@"RANGE_OF_MOTION_TITLE", nil);
|
|
ORKStepArrayAddStep(steps, touchAnywhereStep);
|
|
|
|
touchAnywhereStep.spokenInstruction = touchAnywhereStep.title;
|
|
|
|
ORKDeviceMotionRecorderConfiguration *deviceMotionRecorderConfig = [[ORKDeviceMotionRecorderConfiguration alloc] initWithIdentifier:ORKDeviceMotionRecorderIdentifier frequency:100];
|
|
|
|
ORKShoulderRangeOfMotionStep *shoulderRangeOfMotionStep = [[ORKShoulderRangeOfMotionStep alloc] initWithIdentifier:ORKShoulderRangeOfMotionStepIdentifier limbOption:limbOption];
|
|
shoulderRangeOfMotionStep.title = ORKLocalizedString(@"RANGE_OF_MOTION_TITLE", nil);
|
|
shoulderRangeOfMotionStep.text = ([limbType isEqualToString: ORKLocalizedString(@"LIMB_LEFT", nil)])? ORKLocalizedString(@"SHOULDER_RANGE_OF_MOTION_SPOKEN_INSTRUCTION_LEFT", nil) :
|
|
ORKLocalizedString(@"SHOULDER_RANGE_OF_MOTION_SPOKEN_INSTRUCTION_RIGHT", nil);
|
|
|
|
shoulderRangeOfMotionStep.spokenInstruction = shoulderRangeOfMotionStep.text;
|
|
|
|
shoulderRangeOfMotionStep.recorderConfigurations = @[deviceMotionRecorderConfig];
|
|
shoulderRangeOfMotionStep.optional = NO;
|
|
|
|
ORKStepArrayAddStep(steps, shoulderRangeOfMotionStep);
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeConclusion)) {
|
|
ORKCompletionStep *completionStep = [self makeCompletionStep];
|
|
completionStep.title = ORKLocalizedString(@"RANGE_OF_MOTION_TITLE", nil);
|
|
ORKStepArrayAddStep(steps, completionStep);
|
|
}
|
|
|
|
ORKOrderedTask *task = [[ORKOrderedTask alloc] initWithIdentifier:identifier steps:steps];
|
|
return task;
|
|
}
|
|
|
|
|
|
#pragma mark - spatialSpanMemoryTask
|
|
|
|
NSString *const ORKSpatialSpanMemoryStepIdentifier = @"cognitive.memory.spatialspan";
|
|
|
|
+ (ORKOrderedTask *)spatialSpanMemoryTaskWithIdentifier:(NSString *)identifier
|
|
intendedUseDescription:(NSString *)intendedUseDescription
|
|
initialSpan:(NSInteger)initialSpan
|
|
minimumSpan:(NSInteger)minimumSpan
|
|
maximumSpan:(NSInteger)maximumSpan
|
|
playSpeed:(NSTimeInterval)playSpeed
|
|
maximumTests:(NSInteger)maximumTests
|
|
maximumConsecutiveFailures:(NSInteger)maximumConsecutiveFailures
|
|
customTargetImage:(UIImage *)customTargetImage
|
|
customTargetPluralName:(NSString *)customTargetPluralName
|
|
requireReversal:(BOOL)requireReversal
|
|
options:(ORKPredefinedTaskOption)options {
|
|
|
|
NSString *targetPluralName = customTargetPluralName ? : ORKLocalizedString(@"SPATIAL_SPAN_MEMORY_TARGET_PLURAL", nil);
|
|
|
|
NSMutableArray *steps = [NSMutableArray array];
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction0StepIdentifier];
|
|
step.title = ORKLocalizedString(@"SPATIAL_SPAN_MEMORY_TITLE", nil);
|
|
step.text = intendedUseDescription;
|
|
step.detailText = [NSString localizedStringWithFormat:ORKLocalizedString(@"SPATIAL_SPAN_MEMORY_INTRO_TEXT_%@", nil),targetPluralName];
|
|
|
|
step.image = [UIImage imageNamed:@"memory" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeScaleAspectFit;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier];
|
|
step.title = ORKLocalizedString(@"SPATIAL_SPAN_MEMORY_TITLE", nil);
|
|
step.text = [NSString localizedStringWithFormat:requireReversal ? ORKLocalizedString(@"SPATIAL_SPAN_MEMORY_INTRO_2_TEXT_REVERSE_%@%@", nil) : ORKLocalizedString(@"SPATIAL_SPAN_MEMORY_INTRO_2_TEXT_%@%@", nil), targetPluralName, targetPluralName];
|
|
step.detailText = ORKLocalizedString(@"SPATIAL_SPAN_MEMORY_CALL_TO_ACTION", nil);
|
|
|
|
if (!customTargetImage) {
|
|
step.image = [UIImage imageNamed:@"memory-second-screen" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
} else {
|
|
step.image = customTargetImage;
|
|
}
|
|
step.imageContentMode = UIViewContentModeScaleAspectFit;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
{
|
|
ORKSpatialSpanMemoryStep *step = [[ORKSpatialSpanMemoryStep alloc] initWithIdentifier:ORKSpatialSpanMemoryStepIdentifier];
|
|
step.title = ORKLocalizedString(@"SPATIAL_SPAN_MEMORY_TITLE", nil);
|
|
step.text = nil;
|
|
|
|
step.initialSpan = initialSpan;
|
|
step.minimumSpan = minimumSpan;
|
|
step.maximumSpan = maximumSpan;
|
|
step.playSpeed = playSpeed;
|
|
step.maximumTests = maximumTests;
|
|
step.maximumConsecutiveFailures = maximumConsecutiveFailures;
|
|
step.customTargetImage = customTargetImage;
|
|
step.customTargetPluralName = customTargetPluralName;
|
|
step.requireReversal = requireReversal;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeConclusion)) {
|
|
ORKInstructionStep *step = [self makeCompletionStep];
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
ORKOrderedTask *task = [[ORKOrderedTask alloc] initWithIdentifier:identifier steps:steps];
|
|
return task;
|
|
}
|
|
|
|
|
|
#pragma mark - speechRecognitionTask
|
|
|
|
NSString *const ORKSpeechRecognitionStepIdentifier = @"speech.recognition";
|
|
|
|
+ (ORKOrderedTask *)speechRecognitionTaskWithIdentifier:(NSString *)identifier
|
|
intendedUseDescription:(nullable NSString *)intendedUseDescription
|
|
speechRecognizerLocale:(ORKSpeechRecognizerLocale)speechRecognizerLocale
|
|
speechRecognitionImage:(nullable UIImage *)speechRecognitionImage
|
|
speechRecognitionText:(nullable NSString *)speechRecognitionText
|
|
shouldHideTranscript:(BOOL)shouldHideTranscript
|
|
allowsEdittingTranscript:(BOOL)allowsEdittingTranscript
|
|
options:(ORKPredefinedTaskOption)options {
|
|
NSMutableArray *steps = [NSMutableArray array];
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction0StepIdentifier];
|
|
step.title = ORKLocalizedString(@"SPEECH_TASK_TITLE", nil);
|
|
step.text = intendedUseDescription;
|
|
step.detailText = ORKLocalizedString(@"SPEECH_RECOGNITION_INTRO_TEXT", nil);
|
|
step.image = [UIImage imageNamed:@"phonewaves" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.shouldTintImages = YES;
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier];
|
|
step.title = ORKLocalizedString(@"SPEECH_TASK_TITLE", nil);
|
|
step.text = ORKLocalizedString(@"SPEECH_RECOGNITION_INTRO_2_TEXT", nil);
|
|
step.detailText = ORKLocalizedString(@"SPEECH_RECOGNITION_CALL_TO_ACTION", nil);
|
|
step.image = [UIImage imageNamed:@"phonewavesspeech" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
ORKSpeechRecognitionStep *step = [[ORKSpeechRecognitionStep alloc] initWithIdentifier: ORKSpeechRecognitionStepIdentifier image:speechRecognitionImage text:speechRecognitionText];
|
|
ORKStreamingAudioRecorderConfiguration *config = [[ORKStreamingAudioRecorderConfiguration alloc] initWithIdentifier: ORKStreamingAudioRecorderIdentifier];
|
|
step.title = ORKLocalizedString(@"SPEECH_TASK_TITLE", nil);
|
|
step.shouldHideTranscript = shouldHideTranscript;
|
|
step.recorderConfigurations = @[config];
|
|
step.speechRecognizerLocale = speechRecognizerLocale;
|
|
step.shouldContinueOnFinish = NO;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
|
|
if (allowsEdittingTranscript) {
|
|
ORKTextAnswerFormat *answerFormat = [ORKTextAnswerFormat new];
|
|
answerFormat.spellCheckingType = UITextSpellCheckingTypeNo;
|
|
answerFormat.autocorrectionType = UITextAutocorrectionTypeNo;
|
|
answerFormat.multipleLines = YES;
|
|
answerFormat.maximumLength = 280;
|
|
ORKQuestionStep *editTranscriptStep = [ORKQuestionStep questionStepWithIdentifier:ORKEditSpeechTranscript0StepIdentifier
|
|
title:ORKLocalizedString(@"SPEECH_RECOGNITION_QUESTION_TITLE", nil)
|
|
question:nil
|
|
answer:answerFormat];
|
|
editTranscriptStep.text = ORKLocalizedString(@"SPEECH_RECOGNITION_QUESTION_TEXT", nil);
|
|
ORKStepArrayAddStep(steps, editTranscriptStep);
|
|
}
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeConclusion)) {
|
|
ORKInstructionStep *completionStep = [self makeCompletionStep];
|
|
ORKStepArrayAddStep(steps, completionStep);
|
|
}
|
|
|
|
ORKOrderedTask *task = [[ORKOrderedTask alloc] initWithIdentifier:identifier steps:steps];
|
|
return task;
|
|
}
|
|
|
|
#pragma mark - speechInNoiseTask
|
|
|
|
NSString *const ORKSpeechInNoiseStep0Identifier = @"speech.in.noise0";
|
|
NSString *const ORKSpeechInNoiseStep1Identifier = @"speech.in.noise1";
|
|
NSString *const ORKSpeechInNoiseStep2Identifier = @"speech.in.noise2";
|
|
|
|
+ (ORKOrderedTask *)speechInNoiseTaskWithIdentifier:(NSString *)identifier
|
|
intendedUseDescription:(nullable NSString *)intendedUseDescription
|
|
options:(ORKPredefinedTaskOption)options {
|
|
NSMutableArray *steps = [NSMutableArray array];
|
|
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction0StepIdentifier];
|
|
step.title = ORKLocalizedString(@"SPEECH_IN_NOISE_TITLE", nil);
|
|
step.detailText = intendedUseDescription;
|
|
step.text = ORKLocalizedString(@"SPEECH_IN_NOISE_INTRO_TEXT", nil);
|
|
step.image = [UIImage imageNamed:@"speechInNoise" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier];
|
|
step.title = ORKLocalizedString(@"SPEECH_IN_NOISE_TITLE", nil);
|
|
step.detailText = ORKLocalizedString(@"SPEECH_IN_NOISE_DETAIL_TEXT", nil);
|
|
step.image = [UIImage imageNamed:@"speechInNoise" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
// SNR ranging from 18 dB to 0 dB with a 3 dB step
|
|
NSMutableArray *gainValues = [NSMutableArray new];
|
|
[gainValues addObject:[NSNumber numberWithDouble:0.18]];
|
|
[gainValues addObject:[NSNumber numberWithDouble:0.25]];
|
|
[gainValues addObject:[NSNumber numberWithDouble:0.36]];
|
|
[gainValues addObject:[NSNumber numberWithDouble:0.51]];
|
|
[gainValues addObject:[NSNumber numberWithDouble:0.73]];
|
|
[gainValues addObject:[NSNumber numberWithDouble:1.03]];
|
|
[gainValues addObject:[NSNumber numberWithDouble:1.46]];
|
|
|
|
|
|
{
|
|
ORKSpeechInNoiseStep *step = [[ORKSpeechInNoiseStep alloc] initWithIdentifier:ORKSpeechInNoiseStep1Identifier];
|
|
step.speechFileNameWithExtension = @"Sentence1.wav";
|
|
step.gainAppliedToNoise = [gainValues[0] doubleValue];
|
|
step.title = ORKLocalizedString(@"SPEECH_IN_NOISE_STEP_TITLE", nil);
|
|
step.text = ORKLocalizedString(@"SPEECH_IN_NOISE_STEP_TEXT", nil);
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
ORKSpeechRecognitionStep *step = [[ORKSpeechRecognitionStep alloc] initWithIdentifier: ORKSpeechInNoiseStep2Identifier image:nil text:nil];
|
|
ORKStreamingAudioRecorderConfiguration *config = [[ORKStreamingAudioRecorderConfiguration alloc] initWithIdentifier: ORKStreamingAudioRecorderIdentifier];
|
|
step.title = ORKLocalizedString(@"SPEECH_IN_NOISE_SPEAK_TITLE", nil);
|
|
step.text = ORKLocalizedString(@"SPEECH_IN_NOISE_SPEAK_TEXT", nil);
|
|
step.shouldHideTranscript = YES;
|
|
step.recorderConfigurations = @[config];
|
|
step.speechRecognizerLocale = ORKSpeechRecognizerLocaleEnglishUS;
|
|
step.shouldContinueOnFinish = NO;
|
|
step.optional = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
{
|
|
ORKTextAnswerFormat *answerFormat = [ORKTextAnswerFormat new];
|
|
answerFormat.spellCheckingType = UITextSpellCheckingTypeNo;
|
|
answerFormat.autocorrectionType = UITextAutocorrectionTypeNo;
|
|
answerFormat.multipleLines = YES;
|
|
answerFormat.maximumLength = 280;
|
|
ORKQuestionStep *editTranscriptStep = [ORKQuestionStep questionStepWithIdentifier:ORKEditSpeechTranscript0StepIdentifier
|
|
title:ORKLocalizedString(@"SPEECH_RECOGNITION_QUESTION_TITLE", nil)
|
|
question:nil
|
|
answer:answerFormat];
|
|
editTranscriptStep.text = ORKLocalizedString(@"SPEECH_RECOGNITION_QUESTION_TEXT", nil);
|
|
ORKStepArrayAddStep(steps, editTranscriptStep);
|
|
}
|
|
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeConclusion)) {
|
|
ORKInstructionStep *step = [self makeCompletionStep];
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
ORKOrderedTask *task = [[ORKOrderedTask alloc] initWithIdentifier:identifier steps:steps];
|
|
return task;
|
|
|
|
}
|
|
|
|
#pragma mark - stroopTask
|
|
|
|
NSString *const ORKStroopStepIdentifier = @"stroop";
|
|
|
|
+ (ORKOrderedTask *)stroopTaskWithIdentifier:(NSString *)identifier
|
|
intendedUseDescription:(nullable NSString *)intendedUseDescription
|
|
numberOfAttempts:(NSInteger)numberOfAttempts
|
|
options:(ORKPredefinedTaskOption)options {
|
|
NSMutableArray *steps = [NSMutableArray array];
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction0StepIdentifier];
|
|
step.title = ORKLocalizedString(@"STROOP_TASK_TITLE", nil);
|
|
step.text = intendedUseDescription;
|
|
step.detailText = ORKLocalizedString(@"STROOP_TASK_INTRO1_DETAIL_TEXT", nil);
|
|
step.image = [UIImage imageNamed:@"phonestrooplabel" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier];
|
|
step.title = ORKLocalizedString(@"STROOP_TASK_TITLE", nil);
|
|
step.detailText = ORKLocalizedString(@"STROOP_TASK_INTRO2_DETAIL_TEXT", nil);
|
|
step.image = [UIImage imageNamed:@"phonestroopbutton" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
{
|
|
ORKCountdownStep *step = [[ORKCountdownStep alloc] initWithIdentifier:ORKCountdownStepIdentifier];
|
|
step.title = ORKLocalizedString(@"STROOP_TASK_TITLE", nil);
|
|
step.stepDuration = 5.0;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
{
|
|
ORKStroopStep *step = [[ORKStroopStep alloc] initWithIdentifier:ORKStroopStepIdentifier];
|
|
step.title = ORKLocalizedString(@"STROOP_TASK_TITLE", nil);
|
|
step.text = ORKLocalizedString(@"STROOP_TASK_STEP_TEXT", nil);
|
|
step.spokenInstruction = step.text;
|
|
step.numberOfAttempts = numberOfAttempts;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeConclusion)) {
|
|
ORKInstructionStep *step = [self makeCompletionStep];
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
ORKOrderedTask *task = [[ORKOrderedTask alloc] initWithIdentifier:identifier steps:steps];
|
|
return task;
|
|
}
|
|
|
|
|
|
#pragma mark - toneAudiometryTask
|
|
|
|
NSString *const ORKToneAudiometryPracticeStepIdentifier = @"tone.audiometry.practice";
|
|
NSString *const ORKToneAudiometryStepIdentifier = @"tone.audiometry";
|
|
|
|
+ (ORKOrderedTask *)toneAudiometryTaskWithIdentifier:(NSString *)identifier
|
|
intendedUseDescription:(nullable NSString *)intendedUseDescription
|
|
speechInstruction:(nullable NSString *)speechInstruction
|
|
shortSpeechInstruction:(nullable NSString *)shortSpeechInstruction
|
|
toneDuration:(NSTimeInterval)toneDuration
|
|
options:(ORKPredefinedTaskOption)options {
|
|
|
|
if (options & ORKPredefinedTaskOptionExcludeAudio) {
|
|
@throw [NSException exceptionWithName:NSGenericException reason:@"Audio collection cannot be excluded from audio task" userInfo:nil];
|
|
}
|
|
|
|
NSMutableArray *steps = [NSMutableArray array];
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction0StepIdentifier];
|
|
step.title = ORKLocalizedString(@"TONE_AUDIOMETRY_TASK_TITLE", nil);
|
|
step.text = intendedUseDescription;
|
|
step.detailText = ORKLocalizedString(@"TONE_AUDIOMETRY_INTENDED_USE", nil);
|
|
step.image = [UIImage imageNamed:@"phonewaves_inverted" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier];
|
|
step.title = ORKLocalizedString(@"TONE_AUDIOMETRY_TASK_TITLE", nil);
|
|
step.text = speechInstruction ? : ORKLocalizedString(@"TONE_AUDIOMETRY_INTRO_TEXT", nil);
|
|
step.detailText = ORKLocalizedString(@"TONE_AUDIOMETRY_CALL_TO_ACTION", nil);
|
|
step.image = [UIImage imageNamed:@"phonefrequencywaves" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
NSString *instructionText = shortSpeechInstruction ? : ORKLocalizedString(@"TONE_AUDIOMETRY_INSTRUCTION", nil);
|
|
{
|
|
ORKToneAudiometryStep *step = [[ORKToneAudiometryStep alloc] initWithIdentifier:ORKToneAudiometryPracticeStepIdentifier];
|
|
step.title = ORKLocalizedString(@"TONE_AUDIOMETRY_TASK_TITLE", nil);
|
|
NSString *prepText = speechInstruction ? : ORKLocalizedString(@"TONE_AUDIOMETRY_PREP_TEXT", nil);
|
|
if (UIAccessibilityIsVoiceOverRunning()) {
|
|
prepText = [NSString stringWithFormat:ORKLocalizedString(@"AX_TONE_AUDIOMETRY_PREP_TEXT_VOICEOVER", nil), prepText, instructionText];
|
|
}
|
|
step.text = prepText;
|
|
step.toneDuration = CGFLOAT_MAX;
|
|
step.practiceStep = YES;
|
|
ORKStepArrayAddStep(steps, step);
|
|
|
|
}
|
|
|
|
{
|
|
ORKCountdownStep *step = [[ORKCountdownStep alloc] initWithIdentifier:ORKCountdownStepIdentifier];
|
|
step.title = ORKLocalizedString(@"TONE_AUDIOMETRY_TASK_TITLE", nil);
|
|
step.stepDuration = 5.0;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
ORKToneAudiometryStep *step = [[ORKToneAudiometryStep alloc] initWithIdentifier:ORKToneAudiometryStepIdentifier];
|
|
step.title = ORKLocalizedString(@"TONE_AUDIOMETRY_TASK_TITLE", nil);
|
|
step.text = instructionText;
|
|
step.toneDuration = toneDuration;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeConclusion)) {
|
|
ORKInstructionStep *step = [self makeCompletionStep];
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
ORKOrderedTask *task = [[ORKOrderedTask alloc] initWithIdentifier:identifier steps:steps];
|
|
|
|
return task;
|
|
}
|
|
|
|
#pragma mark - dBHLToneAudiometryTask
|
|
|
|
NSString *const ORKdBHLToneAudiometryStep1Identifier = @"dBHL1.tone.audiometry";
|
|
NSString *const ORKdBHLToneAudiometryStep2Identifier = @"dBHL2.tone.audiometry";
|
|
|
|
+ (ORKNavigableOrderedTask *)dBHLToneAudiometryTaskWithIdentifier:(NSString *)identifier
|
|
intendedUseDescription:(nullable NSString *)intendedUseDescription
|
|
options:(ORKPredefinedTaskOption)options {
|
|
|
|
if (options & ORKPredefinedTaskOptionExcludeAudio) {
|
|
@throw [NSException exceptionWithName:NSGenericException reason:@"Audio collection cannot be excluded from audio task" userInfo:nil];
|
|
}
|
|
|
|
NSMutableArray *steps = [NSMutableArray array];
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction0StepIdentifier];
|
|
step.title = ORKLocalizedString(@"dBHL_TONE_AUDIOMETRY_INTRO_TITLE", nil);
|
|
step.detailText = ORKLocalizedString(@"dBHL_TONE_AUDIOMETRY_INTRO_TEXT_2", nil);
|
|
step.image = [UIImage imageNamed:@"audiometry" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKBodyItem * item1 = [[ORKBodyItem alloc] initWithText:ORKLocalizedString(@"dBHL_TONE_AUDIOMETRY_BODY_ITEM_TEXT_1", nil) detailText:nil image:[UIImage systemImageNamed:@"ear"] learnMoreItem:nil bodyItemStyle:ORKBodyItemStyleImage];
|
|
ORKBodyItem * item2 = [[ORKBodyItem alloc] initWithText:ORKLocalizedString(@"dBHL_TONE_AUDIOMETRY_BODY_ITEM_TEXT_2", nil) detailText:nil image:[UIImage systemImageNamed:@"hand.draw.fill"] learnMoreItem:nil bodyItemStyle:ORKBodyItemStyleImage];
|
|
ORKBodyItem * item3 = [[ORKBodyItem alloc] initWithText:ORKLocalizedString(@"dBHL_TONE_AUDIOMETRY_BODY_ITEM_TEXT_3", nil) detailText:nil image:[UIImage systemImageNamed:@"volume.2.fill"] learnMoreItem:nil bodyItemStyle:ORKBodyItemStyleImage];
|
|
ORKBodyItem * item4 = [[ORKBodyItem alloc] initWithHorizontalRule];
|
|
ORKBodyItem * item5 = [[ORKBodyItem alloc] initWithText:ORKLocalizedString(@"dBHL_TONE_AUDIOMETRY_BODY_ITEM_TEXT_4", nil) detailText:nil image:[UIImage systemImageNamed:@"stopwatch"] learnMoreItem:nil bodyItemStyle:ORKBodyItemStyleImage];
|
|
item5.useSecondaryColor = YES;
|
|
ORKBodyItem * item6 = [[ORKBodyItem alloc] initWithText:ORKLocalizedString(@"dBHL_TONE_AUDIOMETRY_BODY_ITEM_TEXT_5", nil) detailText:nil image:[UIImage systemImageNamed:@"moon.fill"] learnMoreItem:nil bodyItemStyle:ORKBodyItemStyleImage];
|
|
item6.useSecondaryColor = YES;
|
|
step.bodyItems = @[item1, item2, item3, item4, item5, item6];
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
}
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier];
|
|
step.title = ORKLocalizedString(@"dBHL_TONE_AUDIOMETRY_TASK_TITLE", nil);
|
|
step.text = ORKLocalizedString(@"dBHL_TONE_AUDIOMETRY_INTRO_TEXT", nil);
|
|
if (UIAccessibilityIsVoiceOverRunning()) {
|
|
step.text = [NSString stringWithFormat:ORKLocalizedString(@"AX_dBHL_TONE_AUDIOMETRY_INTRO_TEXT", nil), step.text];
|
|
}
|
|
step.image = [UIImage imageNamed:@"audiometry" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
ORKEnvironmentSPLMeterStep *step = [[ORKEnvironmentSPLMeterStep alloc] initWithIdentifier:@"splMeter"];
|
|
step.requiredContiguousSamples = 5;
|
|
step.thresholdValue = 45;
|
|
step.title = ORKLocalizedString(@"ENVIRONMENTSPL_TITLE_2", nil);
|
|
step.text = ORKLocalizedString(@"ENVIRONMENTSPL_INTRO_TEXT_2", nil);
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction2StepIdentifier];
|
|
step.title = ORKLocalizedString(@"dBHL_TONE_AUDIOMETRY_STEP_TITLE_RIGHT_EAR", nil);
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
ORKdBHLToneAudiometryStep *step = [[ORKdBHLToneAudiometryStep alloc] initWithIdentifier:ORKdBHLToneAudiometryStep1Identifier];
|
|
step.title = ORKLocalizedString(@"dBHL_TONE_AUDIOMETRY_TASK_TITLE_2", nil);
|
|
step.stepDuration = CGFLOAT_MAX;
|
|
// manually adding the headphone type here for testing purposes within ORKCatalog
|
|
step.headphoneType = ORKHeadphoneTypeIdentifierAirPodsGen1;
|
|
step.earPreference = ORKAudioChannelRight;
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction3StepIdentifier];
|
|
step.title = ORKLocalizedString(@"dBHL_TONE_AUDIOMETRY_STEP_TITLE_LEFT_EAR", nil);
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
ORKdBHLToneAudiometryStep *step = [[ORKdBHLToneAudiometryStep alloc] initWithIdentifier:ORKdBHLToneAudiometryStep2Identifier];
|
|
step.title = ORKLocalizedString(@"dBHL_TONE_AUDIOMETRY_TASK_TITLE_2", nil);
|
|
step.stepDuration = CGFLOAT_MAX;
|
|
step.earPreference = ORKAudioChannelLeft;
|
|
// manually adding the headphone type here for testing purposes within ORKCatalog
|
|
step.headphoneType = ORKHeadphoneTypeIdentifierAirPodsGen1;
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeConclusion)) {
|
|
ORKInstructionStep *step = [self makeCompletionStep];
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
ORKNavigableOrderedTask *task = [[ORKNavigableOrderedTask alloc] initWithIdentifier:identifier steps:steps];
|
|
|
|
return task;
|
|
}
|
|
|
|
#pragma mark - towerOfHanoiTask
|
|
|
|
NSString *const ORKTowerOfHanoiStepIdentifier = @"towerOfHanoi";
|
|
|
|
+ (ORKOrderedTask *)towerOfHanoiTaskWithIdentifier:(NSString *)identifier
|
|
intendedUseDescription:(nullable NSString *)intendedUseDescription
|
|
numberOfDisks:(NSUInteger)numberOfDisks
|
|
options:(ORKPredefinedTaskOption)options {
|
|
|
|
NSMutableArray *steps = [NSMutableArray array];
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction0StepIdentifier];
|
|
step.title = ORKLocalizedString(@"TOWER_OF_HANOI_TASK_TITLE", nil);
|
|
step.text = intendedUseDescription;
|
|
step.detailText = ORKLocalizedString(@"TOWER_OF_HANOI_TASK_INTENDED_USE", nil);
|
|
step.image = [UIImage imageNamed:@"phone-tower-of-hanoi" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier];
|
|
step.title = ORKLocalizedString(@"TOWER_OF_HANOI_TASK_TITLE", nil);
|
|
step.text = ORKLocalizedString(@"TOWER_OF_HANOI_TASK_INTRO_TEXT", nil);
|
|
step.detailText = ORKLocalizedString(@"TOWER_OF_HANOI_TASK_TASK_CALL_TO_ACTION", nil);
|
|
step.image = [UIImage imageNamed:@"tower-of-hanoi-second-screen" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
ORKTowerOfHanoiStep *towerOfHanoiStep = [[ORKTowerOfHanoiStep alloc]initWithIdentifier:ORKTowerOfHanoiStepIdentifier];
|
|
towerOfHanoiStep.title = ORKLocalizedString(@"TOWER_OF_HANOI_TASK_TITLE", nil);
|
|
towerOfHanoiStep.numberOfDisks = numberOfDisks;
|
|
ORKStepArrayAddStep(steps, towerOfHanoiStep);
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeConclusion)) {
|
|
ORKInstructionStep *step = [self makeCompletionStep];
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
ORKOrderedTask *task = [[ORKOrderedTask alloc]initWithIdentifier:identifier steps:steps];
|
|
|
|
return task;
|
|
}
|
|
|
|
|
|
#pragma mark - reactionTimeTask
|
|
|
|
NSString *const ORKReactionTimeStepIdentifier = @"reactionTime";
|
|
|
|
+ (ORKOrderedTask *)reactionTimeTaskWithIdentifier:(NSString *)identifier
|
|
intendedUseDescription:(nullable NSString *)intendedUseDescription
|
|
maximumStimulusInterval:(NSTimeInterval)maximumStimulusInterval
|
|
minimumStimulusInterval:(NSTimeInterval)minimumStimulusInterval
|
|
thresholdAcceleration:(double)thresholdAcceleration
|
|
numberOfAttempts:(int)numberOfAttempts
|
|
timeout:(NSTimeInterval)timeout
|
|
successSound:(UInt32)successSoundID
|
|
timeoutSound:(UInt32)timeoutSoundID
|
|
failureSound:(UInt32)failureSoundID
|
|
options:(ORKPredefinedTaskOption)options {
|
|
|
|
NSMutableArray *steps = [NSMutableArray array];
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction0StepIdentifier];
|
|
step.title = ORKLocalizedString(@"REACTION_TIME_TASK_TITLE", nil);
|
|
step.text = intendedUseDescription;
|
|
step.detailText = ORKLocalizedString(@"REACTION_TIME_TASK_INTENDED_USE", nil);
|
|
step.image = [UIImage imageNamed:@"phoneshake" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier];
|
|
step.title = ORKLocalizedString(@"REACTION_TIME_TASK_TITLE", nil);
|
|
step.text = [NSString localizedStringWithFormat: ORKLocalizedString(@"REACTION_TIME_TASK_INTRO_TEXT_FORMAT", nil), numberOfAttempts];
|
|
step.detailText = ORKLocalizedString(@"REACTION_TIME_TASK_CALL_TO_ACTION", nil);
|
|
step.image = [UIImage imageNamed:@"phoneshakecircle" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
ORKReactionTimeStep *step = [[ORKReactionTimeStep alloc] initWithIdentifier:ORKReactionTimeStepIdentifier];
|
|
step.title = ORKLocalizedString(@"REACTION_TIME_TASK_TITLE", nil);
|
|
step.maximumStimulusInterval = maximumStimulusInterval;
|
|
step.minimumStimulusInterval = minimumStimulusInterval;
|
|
step.thresholdAcceleration = thresholdAcceleration;
|
|
step.numberOfAttempts = numberOfAttempts;
|
|
step.timeout = timeout;
|
|
step.successSound = successSoundID;
|
|
step.timeoutSound = timeoutSoundID;
|
|
step.failureSound = failureSoundID;
|
|
step.recorderConfigurations = @[ [[ORKDeviceMotionRecorderConfiguration alloc] initWithIdentifier:ORKDeviceMotionRecorderIdentifier frequency: 100]];
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeConclusion)) {
|
|
ORKInstructionStep *completionStep = [self makeCompletionStep];
|
|
ORKStepArrayAddStep(steps, completionStep);
|
|
}
|
|
|
|
ORKOrderedTask *task = [[ORKOrderedTask alloc] initWithIdentifier:identifier steps:steps];
|
|
|
|
return task;
|
|
}
|
|
|
|
#pragma mark - normalizedReactionTimeTask
|
|
|
|
NSString *const ORKNormalizedReactionTimeStepIdentifier = @"normalizedReactionTime";
|
|
|
|
+ (ORKOrderedTask *)normalizedReactionTimeTaskWithIdentifier:(NSString *)identifier
|
|
intendedUseDescription:(nullable NSString *)intendedUseDescription
|
|
maximumStimulusInterval:(NSTimeInterval)maximumStimulusInterval
|
|
minimumStimulusInterval:(NSTimeInterval)minimumStimulusInterval
|
|
thresholdAcceleration:(double)thresholdAcceleration
|
|
numberOfAttempts:(int)numberOfAttempts
|
|
timeout:(NSTimeInterval)timeout
|
|
successSound:(UInt32)successSoundID
|
|
timeoutSound:(UInt32)timeoutSoundID
|
|
failureSound:(UInt32)failureSoundID
|
|
options:(ORKPredefinedTaskOption)options {
|
|
|
|
NSMutableArray *steps = [NSMutableArray array];
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction0StepIdentifier];
|
|
step.title = ORKLocalizedString(@"REACTION_TIME_TASK_TITLE", nil);
|
|
step.text = intendedUseDescription;
|
|
step.detailText = ORKLocalizedString(@"REACTION_TIME_TASK_INTENDED_USE", nil);
|
|
step.image = [UIImage imageNamed:@"phoneshake" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier];
|
|
step.title = ORKLocalizedString(@"REACTION_TIME_TASK_TITLE", nil);
|
|
step.text = [NSString localizedStringWithFormat: ORKLocalizedString(@"REACTION_TIME_TASK_INTRO_TEXT_FORMAT", nil), numberOfAttempts];
|
|
step.detailText = ORKLocalizedString(@"REACTION_TIME_TASK_CALL_TO_ACTION", nil);
|
|
step.image = [UIImage imageNamed:@"phoneshakecircle" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
ORKNormalizedReactionTimeStep *step = [[ORKNormalizedReactionTimeStep alloc] initWithIdentifier:ORKReactionTimeStepIdentifier];
|
|
step.title = ORKLocalizedString(@"REACTION_TIME_TASK_TITLE", nil);
|
|
step.maximumStimulusInterval = maximumStimulusInterval;
|
|
step.minimumStimulusInterval = minimumStimulusInterval;
|
|
step.thresholdAcceleration = thresholdAcceleration;
|
|
step.numberOfAttempts = numberOfAttempts;
|
|
step.timeout = timeout;
|
|
step.successSound = successSoundID;
|
|
step.timeoutSound = timeoutSoundID;
|
|
step.failureSound = failureSoundID;
|
|
step.recorderConfigurations = @[ [[ORKDeviceMotionRecorderConfiguration alloc] initWithIdentifier:ORKDeviceMotionRecorderIdentifier frequency: 100]];
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeConclusion)) {
|
|
ORKInstructionStep *completionStep = [self makeCompletionStep];
|
|
ORKStepArrayAddStep(steps, completionStep);
|
|
}
|
|
|
|
ORKOrderedTask *task = [[ORKOrderedTask alloc] initWithIdentifier:identifier steps:steps];
|
|
|
|
return task;
|
|
}
|
|
|
|
#pragma mark - timedWalkTask
|
|
|
|
NSString *const ORKTimedWalkFormStepIdentifier = @"timed.walk.form";
|
|
NSString *const ORKTimedWalkFormAFOStepIdentifier = @"timed.walk.form.afo";
|
|
NSString *const ORKTimedWalkFormAssistanceStepIdentifier = @"timed.walk.form.assistance";
|
|
NSString *const ORKTimedWalkTrial1StepIdentifier = @"timed.walk.trial1";
|
|
NSString *const ORKTimedWalkTurnAroundStepIdentifier = @"timed.walk.turn.around";
|
|
NSString *const ORKTimedWalkTrial2StepIdentifier = @"timed.walk.trial2";
|
|
|
|
+ (ORKOrderedTask *)timedWalkTaskWithIdentifier:(NSString *)identifier
|
|
intendedUseDescription:(nullable NSString *)intendedUseDescription
|
|
distanceInMeters:(double)distanceInMeters
|
|
timeLimit:(NSTimeInterval)timeLimit
|
|
turnAroundTimeLimit:(NSTimeInterval)turnAroundTimeLimit
|
|
includeAssistiveDeviceForm:(BOOL)includeAssistiveDeviceForm
|
|
options:(ORKPredefinedTaskOption)options {
|
|
|
|
NSMutableArray *steps = [NSMutableArray array];
|
|
|
|
NSLengthFormatter *lengthFormatter = [NSLengthFormatter new];
|
|
lengthFormatter.numberFormatter.maximumFractionDigits = 1;
|
|
lengthFormatter.numberFormatter.maximumSignificantDigits = 3;
|
|
NSString *formattedLength = [lengthFormatter stringFromMeters:distanceInMeters];
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction0StepIdentifier];
|
|
step.title = ORKLocalizedString(@"TIMED_WALK_TITLE", nil);
|
|
step.text = intendedUseDescription;
|
|
step.detailText = ORKLocalizedString(@"TIMED_WALK_INTRO_DETAIL", nil);
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
if (includeAssistiveDeviceForm) {
|
|
ORKFormStep *step = [[ORKFormStep alloc] initWithIdentifier:ORKTimedWalkFormStepIdentifier
|
|
title:ORKLocalizedString(@"TIMED_WALK_FORM_TITLE", nil)
|
|
text:ORKLocalizedString(@"TIMED_WALK_FORM_TEXT", nil)];
|
|
|
|
ORKAnswerFormat *answerFormat1 = [ORKAnswerFormat booleanAnswerFormat];
|
|
ORKFormItem *formItem1 = [[ORKFormItem alloc] initWithIdentifier:ORKTimedWalkFormAFOStepIdentifier
|
|
text:ORKLocalizedString(@"TIMED_WALK_QUESTION_TEXT", nil)
|
|
answerFormat:answerFormat1];
|
|
formItem1.optional = NO;
|
|
|
|
NSArray *textChoices = @[ [ORKTextChoice choiceWithText:ORKLocalizedString(@"TIMED_WALK_QUESTION_2_CHOICE", nil) value:@"TIMED_WALK_QUESTION_2_CHOICE"],
|
|
[ORKTextChoice choiceWithText:ORKLocalizedString(@"TIMED_WALK_QUESTION_2_CHOICE_2", nil) value:@"TIMED_WALK_QUESTION_2_CHOICE_2"],
|
|
[ORKTextChoice choiceWithText:ORKLocalizedString(@"TIMED_WALK_QUESTION_2_CHOICE_3", nil) value:@"TIMED_WALK_QUESTION_2_CHOICE_3"],
|
|
[ORKTextChoice choiceWithText:ORKLocalizedString(@"TIMED_WALK_QUESTION_2_CHOICE_4", nil) value:@"TIMED_WALK_QUESTION_2_CHOICE_4"],
|
|
[ORKTextChoice choiceWithText:ORKLocalizedString(@"TIMED_WALK_QUESTION_2_CHOICE_5", nil) value:@"TIMED_WALK_QUESTION_2_CHOICE_5"],
|
|
[ORKTextChoice choiceWithText:ORKLocalizedString(@"TIMED_WALK_QUESTION_2_CHOICE_6", nil) value:@"TIMED_WALK_QUESTION_2_CHOICE_6"] ];
|
|
ORKAnswerFormat *answerFormat2 = [ORKAnswerFormat valuePickerAnswerFormatWithTextChoices:textChoices];
|
|
|
|
ORKFormItem *formItem2 = [[ORKFormItem alloc] initWithSectionTitle:ORKLocalizedString(@"TIMED_WALK_QUESTION_2_TITLE", nil) detailText:nil learnMoreItem:nil showsProgress:YES];
|
|
|
|
ORKFormItem *formItem3 = [[ORKFormItem alloc] initWithIdentifier:ORKTimedWalkFormAssistanceStepIdentifier
|
|
text:nil
|
|
answerFormat:answerFormat2];
|
|
formItem3.placeholder = ORKLocalizedString(@"TIMED_WALK_QUESTION_2_TEXT", nil);
|
|
formItem3.optional = NO;
|
|
|
|
step.formItems = @[formItem1, formItem2, formItem3];
|
|
step.optional = NO;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier];
|
|
step.title = ORKLocalizedString(@"TIMED_WALK_TITLE", nil);
|
|
step.text = [NSString localizedStringWithFormat:ORKLocalizedString(@"TIMED_WALK_INTRO_2_TEXT_%@", nil), formattedLength];
|
|
step.detailText = ORKLocalizedString(@"TIMED_WALK_INTRO_2_DETAIL", nil);
|
|
step.image = [UIImage imageNamed:@"timer" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
{
|
|
ORKCountdownStep *step = [[ORKCountdownStep alloc] initWithIdentifier:ORKCountdownStepIdentifier];
|
|
step.title = ORKLocalizedString(@"TIMED_WALK_TITLE", nil);
|
|
step.stepDuration = 5.0;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
NSMutableArray *recorderConfigurations = [NSMutableArray array];
|
|
if (!(options & ORKPredefinedTaskOptionExcludePedometer)) {
|
|
[recorderConfigurations addObject:[[ORKPedometerRecorderConfiguration alloc] initWithIdentifier:ORKPedometerRecorderIdentifier]];
|
|
}
|
|
if (!(options & ORKPredefinedTaskOptionExcludeAccelerometer)) {
|
|
[recorderConfigurations addObject:[[ORKAccelerometerRecorderConfiguration alloc] initWithIdentifier:ORKAccelerometerRecorderIdentifier
|
|
frequency:100]];
|
|
}
|
|
if (!(options & ORKPredefinedTaskOptionExcludeDeviceMotion)) {
|
|
[recorderConfigurations addObject:[[ORKDeviceMotionRecorderConfiguration alloc] initWithIdentifier:ORKDeviceMotionRecorderIdentifier
|
|
frequency:100]];
|
|
}
|
|
|
|
#if ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION
|
|
if (! (options & ORKPredefinedTaskOptionExcludeLocation)) {
|
|
[recorderConfigurations addObject:[[ORKLocationRecorderConfiguration alloc] initWithIdentifier:ORKLocationRecorderIdentifier]];
|
|
}
|
|
#endif
|
|
{
|
|
ORKTimedWalkStep *step = [[ORKTimedWalkStep alloc] initWithIdentifier:ORKTimedWalkTrial1StepIdentifier];
|
|
step.title = ORKLocalizedString(@"TIMED_WALK_TITLE", nil);
|
|
step.text = [[[NSString alloc] initWithFormat:ORKLocalizedString(@"TIMED_WALK_INSTRUCTION_%@", nil), formattedLength] stringByAppendingString:[@"\n" stringByAppendingString:ORKLocalizedString(@"TIMED_WALK_INSTRUCTION_TEXT", nil)]];
|
|
step.spokenInstruction = step.text;
|
|
step.recorderConfigurations = recorderConfigurations;
|
|
step.distanceInMeters = distanceInMeters;
|
|
step.shouldTintImages = YES;
|
|
step.image = [UIImage imageNamed:@"timed-walkingman-outbound" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.stepDuration = timeLimit == 0 ? CGFLOAT_MAX : timeLimit;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
if (turnAroundTimeLimit > 0) {
|
|
ORKTimedWalkStep *step = [[ORKTimedWalkStep alloc] initWithIdentifier:ORKTimedWalkTurnAroundStepIdentifier];
|
|
step.title = ORKLocalizedString(@"TIMED_WALK_TITLE", nil);
|
|
step.text = [ORKLocalizedString(@"TIMED_WALK_INSTRUCTION_TURN", nil) stringByAppendingString:[@"\n" stringByAppendingString:ORKLocalizedString(@"TIMED_WALK_INSTRUCTION_TEXT", nil)]];
|
|
step.spokenInstruction = step.text;
|
|
step.recorderConfigurations = recorderConfigurations;
|
|
step.distanceInMeters = 1;
|
|
step.shouldTintImages = YES;
|
|
step.image = [UIImage imageNamed:@"turnaround" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.stepDuration = turnAroundTimeLimit == 0 ? CGFLOAT_MAX : turnAroundTimeLimit;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
{
|
|
ORKTimedWalkStep *step = [[ORKTimedWalkStep alloc] initWithIdentifier:ORKTimedWalkTrial2StepIdentifier];
|
|
step.title = ORKLocalizedString(@"TIMED_WALK_TITLE", nil);
|
|
step.text = [[[NSString alloc] initWithFormat:ORKLocalizedString(@"TIMED_WALK_INSTRUCTION_2", nil), formattedLength] stringByAppendingString:[@"\n" stringByAppendingString:ORKLocalizedString(@"TIMED_WALK_INSTRUCTION_TEXT", nil)]];
|
|
step.spokenInstruction = step.text;
|
|
step.recorderConfigurations = recorderConfigurations;
|
|
step.distanceInMeters = distanceInMeters;
|
|
step.shouldTintImages = YES;
|
|
step.image = [UIImage imageNamed:@"timed-walkingman-return" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.stepDuration = timeLimit == 0 ? CGFLOAT_MAX : timeLimit;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeConclusion)) {
|
|
ORKInstructionStep *step = [self makeCompletionStep];
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
ORKOrderedTask *task = [[ORKOrderedTask alloc] initWithIdentifier:identifier steps:steps];
|
|
return task;
|
|
}
|
|
|
|
|
|
#pragma mark - PSATTask
|
|
|
|
NSString *const ORKPSATStepIdentifier = @"psat";
|
|
|
|
+ (ORKOrderedTask *)PSATTaskWithIdentifier:(NSString *)identifier
|
|
intendedUseDescription:(nullable NSString *)intendedUseDescription
|
|
presentationMode:(ORKPSATPresentationMode)presentationMode
|
|
interStimulusInterval:(NSTimeInterval)interStimulusInterval
|
|
stimulusDuration:(NSTimeInterval)stimulusDuration
|
|
seriesLength:(NSInteger)seriesLength
|
|
options:(ORKPredefinedTaskOption)options {
|
|
|
|
NSMutableArray *steps = [NSMutableArray array];
|
|
NSString *versionTitle = @"";
|
|
NSString *versionDetailText = @"";
|
|
|
|
if (presentationMode == ORKPSATPresentationModeAuditory) {
|
|
versionTitle = ORKLocalizedString(@"PASAT_TITLE", nil);
|
|
versionDetailText = ORKLocalizedString(@"PASAT_INTRO_TEXT", nil);
|
|
} else if (presentationMode == ORKPSATPresentationModeVisual) {
|
|
versionTitle = ORKLocalizedString(@"PVSAT_TITLE", nil);
|
|
versionDetailText = ORKLocalizedString(@"PVSAT_INTRO_TEXT", nil);
|
|
} else {
|
|
versionTitle = ORKLocalizedString(@"PAVSAT_TITLE", nil);
|
|
versionDetailText = ORKLocalizedString(@"PAVSAT_INTRO_TEXT", nil);
|
|
}
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction0StepIdentifier];
|
|
step.title = versionTitle;
|
|
step.detailText = versionDetailText;
|
|
step.text = intendedUseDescription;
|
|
step.image = [UIImage imageNamed:@"psatDice" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier];
|
|
step.title = versionTitle;
|
|
|
|
NSDateComponentsFormatter *secondsFormatter = [NSDateComponentsFormatter new];
|
|
secondsFormatter.unitsStyle = NSDateComponentsFormatterUnitsStyleSpellOut;
|
|
secondsFormatter.allowedUnits = NSCalendarUnitSecond;
|
|
secondsFormatter.formattingContext = NSFormattingContextDynamic;
|
|
secondsFormatter.maximumUnitCount = 1;
|
|
|
|
NSString *seconds = [secondsFormatter stringFromTimeInterval:interStimulusInterval];
|
|
step.text = [NSString localizedStringWithFormat:ORKLocalizedString(@"PSAT_INTRO_TEXT_2_%@", nil), seconds];
|
|
step.detailText = ORKLocalizedString(@"PSAT_CALL_TO_ACTION", nil);
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
{
|
|
ORKCountdownStep *step = [[ORKCountdownStep alloc] initWithIdentifier:ORKCountdownStepIdentifier];
|
|
step.stepDuration = 5.0;
|
|
step.title = versionTitle;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
ORKPSATStep *step = [[ORKPSATStep alloc] initWithIdentifier:ORKPSATStepIdentifier];
|
|
step.title = versionTitle;
|
|
step.text = ORKLocalizedString(@"PSAT_INITIAL_INSTRUCTION", nil);
|
|
step.stepDuration = (seriesLength + 1) * interStimulusInterval;
|
|
step.presentationMode = presentationMode;
|
|
step.interStimulusInterval = interStimulusInterval;
|
|
step.stimulusDuration = stimulusDuration;
|
|
step.seriesLength = seriesLength;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeConclusion)) {
|
|
ORKInstructionStep *step = [self makeCompletionStep];
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
ORKOrderedTask *task = [[ORKOrderedTask alloc] initWithIdentifier:identifier steps:[steps copy]];
|
|
|
|
return task;
|
|
}
|
|
|
|
|
|
#pragma mark - tremorTestTask
|
|
|
|
NSString *const ORKTremorTestInLapStepIdentifier = @"tremor.handInLap";
|
|
NSString *const ORKTremorTestExtendArmStepIdentifier = @"tremor.handAtShoulderLength";
|
|
NSString *const ORKTremorTestBendArmStepIdentifier = @"tremor.handAtShoulderLengthWithElbowBent";
|
|
NSString *const ORKTremorTestTouchNoseStepIdentifier = @"tremor.handToNose";
|
|
NSString *const ORKTremorTestTurnWristStepIdentifier = @"tremor.handQueenWave";
|
|
|
|
+ (NSString *)stepIdentifier:(NSString *)stepIdentifier withHandIdentifier:(NSString *)handIdentifier {
|
|
return [NSString stringWithFormat:@"%@.%@", stepIdentifier, handIdentifier];
|
|
}
|
|
|
|
+ (NSMutableArray *)stepsForOneHandTremorTestTaskWithIdentifier:(NSString *)identifier
|
|
activeStepDuration:(NSTimeInterval)activeStepDuration
|
|
activeTaskOptions:(ORKTremorActiveTaskOption)activeTaskOptions
|
|
lastHand:(BOOL)lastHand
|
|
leftHand:(BOOL)leftHand
|
|
handIdentifier:(NSString *)handIdentifier
|
|
introDetailText:(NSString *)detailText
|
|
options:(ORKPredefinedTaskOption)options {
|
|
NSMutableArray<ORKStep *> *steps = [NSMutableArray array];
|
|
NSString *stepFinishedInstruction = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_FINISHED_INSTRUCTION", nil);
|
|
BOOL rightHand = !leftHand && ![handIdentifier isEqualToString:ORKActiveTaskMostAffectedHandIdentifier];
|
|
|
|
{
|
|
NSString *stepIdentifier = [self stepIdentifier:ORKInstruction1StepIdentifier withHandIdentifier:handIdentifier];
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:stepIdentifier];
|
|
step.title = ORKLocalizedString(@"TREMOR_TEST_TITLE", nil);
|
|
|
|
if ([identifier isEqualToString:ORKActiveTaskMostAffectedHandIdentifier]) {
|
|
step.detailText = ORKLocalizedString(@"TREMOR_TEST_INTRO_2_DEFAULT_TEXT", nil);
|
|
step.text = detailText;
|
|
} else {
|
|
if (leftHand) {
|
|
step.detailText = ORKLocalizedString(@"TREMOR_TEST_INTRO_2_LEFT_HAND_TEXT", nil);
|
|
} else {
|
|
step.detailText = ORKLocalizedString(@"TREMOR_TEST_INTRO_2_RIGHT_HAND_TEXT", nil);
|
|
}
|
|
}
|
|
|
|
NSString *imageName = leftHand ? @"tremortestLeft" : @"tremortestRight";
|
|
step.image = [UIImage imageNamed:imageName inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
// TODO: Awaiting newer assets.
|
|
step.imageContentMode = UIViewContentModeScaleAspectFit;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
if (!(activeTaskOptions & ORKTremorActiveTaskOptionExcludeHandInLap)) {
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
NSString *stepIdentifier = [self stepIdentifier:ORKInstruction2StepIdentifier withHandIdentifier:handIdentifier];
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:stepIdentifier];
|
|
step.title = ORKLocalizedString(@"TREMOR_TEST_TITLE", nil);
|
|
step.text = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_IN_LAP_INTRO", nil);
|
|
step.detailText = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_INTRO_TEXT", nil);
|
|
step.image = [UIImage imageNamed:@"tremortest3a" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
// TODO: Awaiting newer assets.
|
|
step.imageContentMode = UIViewContentModeScaleAspectFit;
|
|
step.auxiliaryImage = [UIImage imageNamed:@"tremortest3b" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
if (leftHand) {
|
|
step.text = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_IN_LAP_INTRO_LEFT", nil);
|
|
step.image = [step.image ork_flippedImage:UIImageOrientationUpMirrored];
|
|
step.auxiliaryImage = [step.auxiliaryImage ork_flippedImage:UIImageOrientationUpMirrored];
|
|
} else if (rightHand) {
|
|
step.text = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_IN_LAP_INTRO_RIGHT", nil);
|
|
}
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
NSString *stepIdentifier = [self stepIdentifier:ORKCountdown1StepIdentifier withHandIdentifier:handIdentifier];
|
|
ORKCountdownStep *step = [[ORKCountdownStep alloc] initWithIdentifier:stepIdentifier];
|
|
step.title = ORKLocalizedString(@"TREMOR_TEST_TITLE", nil);
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
NSString *titleFormat = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_IN_LAP_INSTRUCTION_%ld", nil);
|
|
NSString *stepIdentifier = [self stepIdentifier:ORKTremorTestInLapStepIdentifier withHandIdentifier:handIdentifier];
|
|
ORKActiveStep *step = [[ORKActiveStep alloc] initWithIdentifier:stepIdentifier];
|
|
step.recorderConfigurations = @[[[ORKAccelerometerRecorderConfiguration alloc] initWithIdentifier:@"ac1_acc" frequency:100.0], [[ORKDeviceMotionRecorderConfiguration alloc] initWithIdentifier:@"ac1_motion" frequency:100.0]];
|
|
step.title = ORKLocalizedString(@"TREMOR_TEST_TITLE", nil);
|
|
step.text = [NSString localizedStringWithFormat:titleFormat, (long)activeStepDuration];
|
|
step.spokenInstruction = step.text;
|
|
step.finishedSpokenInstruction = stepFinishedInstruction;
|
|
step.stepDuration = activeStepDuration;
|
|
step.shouldPlaySoundOnStart = YES;
|
|
step.shouldVibrateOnStart = YES;
|
|
step.shouldPlaySoundOnFinish = YES;
|
|
step.shouldVibrateOnFinish = YES;
|
|
step.shouldContinueOnFinish = NO;
|
|
step.shouldStartTimerAutomatically = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
if (!(activeTaskOptions & ORKTremorActiveTaskOptionExcludeHandAtShoulderHeight)) {
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
NSString *stepIdentifier = [self stepIdentifier:ORKInstruction4StepIdentifier withHandIdentifier:handIdentifier];
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:stepIdentifier];
|
|
step.title = ORKLocalizedString(@"TREMOR_TEST_TITLE", nil);
|
|
step.text = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_EXTEND_ARM_INTRO", nil);
|
|
step.detailText = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_INTRO_TEXT", nil);
|
|
step.image = [UIImage imageNamed:@"tremortest4a" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
// TODO: Awaiting newer assets.
|
|
step.imageContentMode = UIViewContentModeScaleAspectFit;
|
|
step.auxiliaryImage = [UIImage imageNamed:@"tremortest4b" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
if (leftHand) {
|
|
step.text = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_EXTEND_ARM_INTRO_LEFT", nil);
|
|
step.image = [step.image ork_flippedImage:UIImageOrientationUpMirrored];
|
|
step.auxiliaryImage = [step.auxiliaryImage ork_flippedImage:UIImageOrientationUpMirrored];
|
|
} else if (rightHand) {
|
|
step.text = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_EXTEND_ARM_INTRO_RIGHT", nil);
|
|
}
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
NSString *stepIdentifier = [self stepIdentifier:ORKCountdown2StepIdentifier withHandIdentifier:handIdentifier];
|
|
ORKCountdownStep *step = [[ORKCountdownStep alloc] initWithIdentifier:stepIdentifier];
|
|
step.title = ORKLocalizedString(@"TREMOR_TEST_TITLE", nil);
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
NSString *titleFormat = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_EXTEND_ARM_INSTRUCTION_%ld", nil);
|
|
NSString *stepIdentifier = [self stepIdentifier:ORKTremorTestExtendArmStepIdentifier withHandIdentifier:handIdentifier];
|
|
ORKActiveStep *step = [[ORKActiveStep alloc] initWithIdentifier:stepIdentifier];
|
|
step.recorderConfigurations = @[[[ORKAccelerometerRecorderConfiguration alloc] initWithIdentifier:@"ac2_acc" frequency:100.0], [[ORKDeviceMotionRecorderConfiguration alloc] initWithIdentifier:@"ac2_motion" frequency:100.0]];
|
|
step.title = ORKLocalizedString(@"TREMOR_TEST_TITLE", nil);
|
|
step.text = [NSString localizedStringWithFormat:titleFormat, (long)activeStepDuration];
|
|
step.spokenInstruction = step.text;
|
|
step.finishedSpokenInstruction = stepFinishedInstruction;
|
|
step.stepDuration = activeStepDuration;
|
|
step.image = [UIImage imageNamed:@"tremortest4a" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
if (leftHand) {
|
|
step.image = [step.image ork_flippedImage:UIImageOrientationUpMirrored];
|
|
}
|
|
step.imageContentMode = UIViewContentModeScaleAspectFit;
|
|
|
|
step.shouldPlaySoundOnStart = YES;
|
|
step.shouldVibrateOnStart = YES;
|
|
step.shouldPlaySoundOnFinish = YES;
|
|
step.shouldVibrateOnFinish = YES;
|
|
step.shouldContinueOnFinish = NO;
|
|
step.shouldStartTimerAutomatically = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
if (!(activeTaskOptions & ORKTremorActiveTaskOptionExcludeHandAtShoulderHeightElbowBent)) {
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
NSString *stepIdentifier = [self stepIdentifier:ORKInstruction5StepIdentifier withHandIdentifier:handIdentifier];
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:stepIdentifier];
|
|
step.title = ORKLocalizedString(@"TREMOR_TEST_TITLE", nil);
|
|
step.text = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_BEND_ARM_INTRO", nil);
|
|
step.detailText = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_INTRO_TEXT", nil);
|
|
step.image = [UIImage imageNamed:@"tremortest5a" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeScaleAspectFit;
|
|
step.auxiliaryImage = [UIImage imageNamed:@"tremortest5b" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
if (leftHand) {
|
|
step.text = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_BEND_ARM_INTRO_LEFT", nil);
|
|
step.image = [step.image ork_flippedImage:UIImageOrientationUpMirrored];
|
|
step.auxiliaryImage = [step.auxiliaryImage ork_flippedImage:UIImageOrientationUpMirrored];
|
|
} else if (rightHand) {
|
|
step.text = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_BEND_ARM_INTRO_RIGHT", nil);
|
|
}
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
NSString *stepIdentifier = [self stepIdentifier:ORKCountdown3StepIdentifier withHandIdentifier:handIdentifier];
|
|
ORKCountdownStep *step = [[ORKCountdownStep alloc] initWithIdentifier:stepIdentifier];
|
|
step.title = ORKLocalizedString(@"TREMOR_TEST_TITLE", nil);
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
NSString *titleFormat = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_BEND_ARM_INSTRUCTION_%ld", nil);
|
|
NSString *stepIdentifier = [self stepIdentifier:ORKTremorTestBendArmStepIdentifier withHandIdentifier:handIdentifier];
|
|
ORKActiveStep *step = [[ORKActiveStep alloc] initWithIdentifier:stepIdentifier];
|
|
step.recorderConfigurations = @[[[ORKAccelerometerRecorderConfiguration alloc] initWithIdentifier:@"ac3_acc" frequency:100.0], [[ORKDeviceMotionRecorderConfiguration alloc] initWithIdentifier:@"ac3_motion" frequency:100.0]];
|
|
step.title = ORKLocalizedString(@"TREMOR_TEST_TITLE", nil);
|
|
step.text = [NSString localizedStringWithFormat:titleFormat, (long)activeStepDuration];
|
|
step.spokenInstruction = step.text;
|
|
step.finishedSpokenInstruction = stepFinishedInstruction;
|
|
step.stepDuration = activeStepDuration;
|
|
step.shouldPlaySoundOnStart = YES;
|
|
step.shouldVibrateOnStart = YES;
|
|
step.shouldPlaySoundOnFinish = YES;
|
|
step.shouldVibrateOnFinish = YES;
|
|
step.shouldContinueOnFinish = NO;
|
|
step.shouldStartTimerAutomatically = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
if (!(activeTaskOptions & ORKTremorActiveTaskOptionExcludeHandToNose)) {
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
NSString *stepIdentifier = [self stepIdentifier:ORKInstruction6StepIdentifier withHandIdentifier:handIdentifier];
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:stepIdentifier];
|
|
step.title = ORKLocalizedString(@"TREMOR_TEST_TITLE", nil);
|
|
step.text = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_TOUCH_NOSE_INTRO", nil);
|
|
step.detailText = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_INTRO_TEXT", nil);
|
|
step.image = [UIImage imageNamed:@"tremortest6a" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.auxiliaryImage = [UIImage imageNamed:@"tremortest6b" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
if (leftHand) {
|
|
step.text = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_TOUCH_NOSE_INTRO_LEFT", nil);
|
|
step.image = [step.image ork_flippedImage:UIImageOrientationUpMirrored];
|
|
step.auxiliaryImage = [step.auxiliaryImage ork_flippedImage:UIImageOrientationUpMirrored];
|
|
} else if (rightHand) {
|
|
step.text = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_TOUCH_NOSE_INTRO_RIGHT", nil);
|
|
}
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
NSString *stepIdentifier = [self stepIdentifier:ORKCountdown4StepIdentifier withHandIdentifier:handIdentifier];
|
|
ORKCountdownStep *step = [[ORKCountdownStep alloc] initWithIdentifier:stepIdentifier];
|
|
step.title = ORKLocalizedString(@"TREMOR_TEST_TITLE", nil);
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
NSString *titleFormat = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_TOUCH_NOSE_INSTRUCTION_%ld", nil);
|
|
NSString *stepIdentifier = [self stepIdentifier:ORKTremorTestTouchNoseStepIdentifier withHandIdentifier:handIdentifier];
|
|
ORKActiveStep *step = [[ORKActiveStep alloc] initWithIdentifier:stepIdentifier];
|
|
step.recorderConfigurations = @[[[ORKAccelerometerRecorderConfiguration alloc] initWithIdentifier:@"ac4_acc" frequency:100.0], [[ORKDeviceMotionRecorderConfiguration alloc] initWithIdentifier:@"ac4_motion" frequency:100.0]];
|
|
step.title = ORKLocalizedString(@"TREMOR_TEST_TITLE", nil);
|
|
step.text = [NSString localizedStringWithFormat:titleFormat, (long)activeStepDuration];
|
|
step.spokenInstruction = step.text;
|
|
step.finishedSpokenInstruction = stepFinishedInstruction;
|
|
step.stepDuration = activeStepDuration;
|
|
step.shouldPlaySoundOnStart = YES;
|
|
step.shouldVibrateOnStart = YES;
|
|
step.shouldPlaySoundOnFinish = YES;
|
|
step.shouldVibrateOnFinish = YES;
|
|
step.shouldContinueOnFinish = NO;
|
|
step.shouldStartTimerAutomatically = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
if (!(activeTaskOptions & ORKTremorActiveTaskOptionExcludeQueenWave)) {
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
NSString *stepIdentifier = [self stepIdentifier:ORKInstruction7StepIdentifier withHandIdentifier:handIdentifier];
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:stepIdentifier];
|
|
step.title = ORKLocalizedString(@"TREMOR_TEST_TITLE", nil);
|
|
step.text = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_TURN_WRIST_INTRO", nil);
|
|
step.detailText = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_INTRO_TEXT", nil);
|
|
step.image = [UIImage imageNamed:@"tremortest7" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
if (leftHand) {
|
|
step.text = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_TURN_WRIST_INTRO_LEFT", nil);
|
|
step.image = [step.image ork_flippedImage:UIImageOrientationUpMirrored];
|
|
} else if (rightHand) {
|
|
step.text = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_TURN_WRIST_INTRO_RIGHT", nil);
|
|
}
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
NSString *stepIdentifier = [self stepIdentifier:ORKCountdown5StepIdentifier withHandIdentifier:handIdentifier];
|
|
ORKCountdownStep *step = [[ORKCountdownStep alloc] initWithIdentifier:stepIdentifier];
|
|
step.title = ORKLocalizedString(@"TREMOR_TEST_TITLE", nil);
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
NSString *titleFormat = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_TURN_WRIST_INSTRUCTION_%ld", nil);
|
|
NSString *stepIdentifier = [self stepIdentifier:ORKTremorTestTurnWristStepIdentifier withHandIdentifier:handIdentifier];
|
|
ORKActiveStep *step = [[ORKActiveStep alloc] initWithIdentifier:stepIdentifier];
|
|
step.recorderConfigurations = @[[[ORKAccelerometerRecorderConfiguration alloc] initWithIdentifier:@"ac5_acc" frequency:100.0], [[ORKDeviceMotionRecorderConfiguration alloc] initWithIdentifier:@"ac5_motion" frequency:100.0]];
|
|
step.title = ORKLocalizedString(@"TREMOR_TEST_TITLE", nil);
|
|
step.text = [NSString localizedStringWithFormat:titleFormat, (long)activeStepDuration];
|
|
step.spokenInstruction = step.text;
|
|
step.finishedSpokenInstruction = stepFinishedInstruction;
|
|
step.stepDuration = activeStepDuration;
|
|
step.shouldPlaySoundOnStart = YES;
|
|
step.shouldVibrateOnStart = YES;
|
|
step.shouldPlaySoundOnFinish = YES;
|
|
step.shouldVibrateOnFinish = YES;
|
|
step.shouldContinueOnFinish = NO;
|
|
step.shouldStartTimerAutomatically = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
// fix the spoken instruction on the last included step, depending on which hand we're on
|
|
ORKActiveStep *lastStep = (ORKActiveStep *)[steps lastObject];
|
|
if (lastHand) {
|
|
lastStep.finishedSpokenInstruction = ORKLocalizedString(@"TREMOR_TEST_COMPLETED_INSTRUCTION", nil);
|
|
} else if (leftHand) {
|
|
lastStep.finishedSpokenInstruction = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_SWITCH_HANDS_RIGHT_INSTRUCTION", nil);
|
|
} else {
|
|
lastStep.finishedSpokenInstruction = ORKLocalizedString(@"TREMOR_TEST_ACTIVE_STEP_SWITCH_HANDS_LEFT_INSTRUCTION", nil);
|
|
}
|
|
|
|
return steps;
|
|
}
|
|
|
|
+ (ORKNavigableOrderedTask *)tremorTestTaskWithIdentifier:(NSString *)identifier
|
|
intendedUseDescription:(nullable NSString *)intendedUseDescription
|
|
activeStepDuration:(NSTimeInterval)activeStepDuration
|
|
activeTaskOptions:(ORKTremorActiveTaskOption)activeTaskOptions
|
|
handOptions:(ORKPredefinedTaskHandOption)handOptions
|
|
options:(ORKPredefinedTaskOption)options {
|
|
|
|
NSMutableArray<__kindof ORKStep *> *steps = [NSMutableArray array];
|
|
// coin toss for which hand first (in case we're doing both)
|
|
BOOL leftFirstIfDoingBoth = arc4random_uniform(2) == 1;
|
|
BOOL doingBoth = ((handOptions & ORKPredefinedTaskHandOptionLeft) && (handOptions & ORKPredefinedTaskHandOptionRight));
|
|
BOOL firstIsLeft = (leftFirstIfDoingBoth && doingBoth) || (!doingBoth && (handOptions & ORKPredefinedTaskHandOptionLeft));
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction0StepIdentifier];
|
|
step.title = ORKLocalizedString(@"TREMOR_TEST_TITLE", nil);
|
|
step.detailText = intendedUseDescription;
|
|
step.text = ORKLocalizedString(@"TREMOR_TEST_INTRO_1_DETAIL", nil);
|
|
step.image = [UIImage imageNamed:@"tremortest1" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
if (firstIsLeft) {
|
|
step.image = [step.image ork_flippedImage:UIImageOrientationUpMirrored];
|
|
}
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
// Build the string for the detail texts
|
|
NSArray<NSString *>*detailStringForNumberOfTasks = @[
|
|
ORKLocalizedString(@"TREMOR_TEST_INTRO_2_DETAIL_1_TASK", nil),
|
|
ORKLocalizedString(@"TREMOR_TEST_INTRO_2_DETAIL_2_TASK", nil),
|
|
ORKLocalizedString(@"TREMOR_TEST_INTRO_2_DETAIL_3_TASK", nil),
|
|
ORKLocalizedString(@"TREMOR_TEST_INTRO_2_DETAIL_4_TASK", nil),
|
|
ORKLocalizedString(@"TREMOR_TEST_INTRO_2_DETAIL_5_TASK", nil)
|
|
];
|
|
|
|
// start with the count for all the tasks, then subtract one for each excluded task flag
|
|
static const NSInteger allTasks = 5; // hold in lap, outstretched arm, elbow bent, repeatedly touching nose, queen wave
|
|
NSInteger actualTasksIndex = allTasks - 1;
|
|
for (NSInteger i = 0; i < allTasks; ++i) {
|
|
if (activeTaskOptions & (1 << i)) {
|
|
actualTasksIndex--;
|
|
}
|
|
}
|
|
|
|
NSString *detailFormat = doingBoth ? ORKLocalizedString(@"TREMOR_TEST_SKIP_QUESTION_BOTH_HANDS_%@", nil) : ORKLocalizedString(@"TREMOR_TEST_INTRO_2_DETAIL_DEFAULT_%@", nil);
|
|
NSString *detailText = [NSString localizedStringWithFormat:detailFormat, detailStringForNumberOfTasks[actualTasksIndex]];
|
|
|
|
if (doingBoth) {
|
|
// If doing both hands then ask the user if they need to skip one of the hands
|
|
ORKTextChoice *skipRight = [ORKTextChoice choiceWithText:ORKLocalizedString(@"TREMOR_SKIP_RIGHT_HAND", nil)
|
|
value:ORKActiveTaskRightHandIdentifier];
|
|
ORKTextChoice *skipLeft = [ORKTextChoice choiceWithText:ORKLocalizedString(@"TREMOR_SKIP_LEFT_HAND", nil)
|
|
value:ORKActiveTaskLeftHandIdentifier];
|
|
ORKTextChoice *skipNeither = [ORKTextChoice choiceWithText:ORKLocalizedString(@"TREMOR_SKIP_NEITHER", nil)
|
|
value:@""];
|
|
|
|
ORKAnswerFormat *answerFormat = [ORKAnswerFormat choiceAnswerFormatWithStyle:ORKChoiceAnswerStyleSingleChoice
|
|
textChoices:@[skipRight, skipLeft, skipNeither]];
|
|
ORKQuestionStep *step = [ORKQuestionStep questionStepWithIdentifier:ORKActiveTaskSkipHandStepIdentifier
|
|
title:ORKLocalizedString(@"TREMOR_TEST_TITLE", nil)
|
|
question:detailText
|
|
answer:answerFormat];
|
|
step.optional = NO;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
// right or most-affected hand
|
|
NSArray<__kindof ORKStep *> *rightSteps = nil;
|
|
if (handOptions == ORKPredefinedTaskHandOptionUnspecified) {
|
|
rightSteps = [self stepsForOneHandTremorTestTaskWithIdentifier:identifier
|
|
activeStepDuration:activeStepDuration
|
|
activeTaskOptions:activeTaskOptions
|
|
lastHand:YES
|
|
leftHand:NO
|
|
handIdentifier:ORKActiveTaskMostAffectedHandIdentifier
|
|
introDetailText:detailText
|
|
options:options];
|
|
} else if (handOptions & ORKPredefinedTaskHandOptionRight) {
|
|
rightSteps = [self stepsForOneHandTremorTestTaskWithIdentifier:identifier
|
|
activeStepDuration:activeStepDuration
|
|
activeTaskOptions:activeTaskOptions
|
|
lastHand:firstIsLeft
|
|
leftHand:NO
|
|
handIdentifier:ORKActiveTaskRightHandIdentifier
|
|
introDetailText:nil
|
|
options:options];
|
|
}
|
|
|
|
// left hand
|
|
NSArray<__kindof ORKStep *> *leftSteps = nil;
|
|
if (handOptions & ORKPredefinedTaskHandOptionLeft) {
|
|
leftSteps = [self stepsForOneHandTremorTestTaskWithIdentifier:identifier
|
|
activeStepDuration:activeStepDuration
|
|
activeTaskOptions:activeTaskOptions
|
|
lastHand:!firstIsLeft || !(handOptions & ORKPredefinedTaskHandOptionRight)
|
|
leftHand:YES
|
|
handIdentifier:ORKActiveTaskLeftHandIdentifier
|
|
introDetailText:nil
|
|
options:options];
|
|
}
|
|
|
|
if (firstIsLeft && leftSteps != nil) {
|
|
[steps addObjectsFromArray:leftSteps];
|
|
}
|
|
|
|
if (rightSteps != nil) {
|
|
[steps addObjectsFromArray:rightSteps];
|
|
}
|
|
|
|
if (!firstIsLeft && leftSteps != nil) {
|
|
[steps addObjectsFromArray:leftSteps];
|
|
}
|
|
|
|
BOOL hasCompletionStep = NO;
|
|
if (!(options & ORKPredefinedTaskOptionExcludeConclusion)) {
|
|
hasCompletionStep = YES;
|
|
ORKCompletionStep *step = [self makeCompletionStep];
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
ORKNavigableOrderedTask *task = [[ORKNavigableOrderedTask alloc] initWithIdentifier:identifier steps:steps];
|
|
|
|
if (doingBoth) {
|
|
// Setup rules for skipping all the steps in either the left or right hand if called upon to do so.
|
|
ORKResultSelector *resultSelector = [ORKResultSelector selectorWithStepIdentifier:ORKActiveTaskSkipHandStepIdentifier
|
|
resultIdentifier:ORKActiveTaskSkipHandStepIdentifier];
|
|
NSPredicate *predicateRight = [ORKResultPredicate predicateForChoiceQuestionResultWithResultSelector:resultSelector expectedAnswerValue:ORKActiveTaskRightHandIdentifier];
|
|
NSPredicate *predicateLeft = [ORKResultPredicate predicateForChoiceQuestionResultWithResultSelector:resultSelector expectedAnswerValue:ORKActiveTaskLeftHandIdentifier];
|
|
|
|
// Setup rule for skipping first hand
|
|
NSString *secondHandIdentifier = firstIsLeft ? [[rightSteps firstObject] identifier] : [[leftSteps firstObject] identifier];
|
|
NSPredicate *firstPredicate = firstIsLeft ? predicateLeft : predicateRight;
|
|
ORKStepNavigationRule *skipFirst = [[ORKPredicateStepNavigationRule alloc] initWithResultPredicates:@[firstPredicate]
|
|
destinationStepIdentifiers:@[secondHandIdentifier]];
|
|
[task setNavigationRule:skipFirst forTriggerStepIdentifier:ORKActiveTaskSkipHandStepIdentifier];
|
|
|
|
// Setup rule for skipping the second hand
|
|
NSString *triggerIdentifier = firstIsLeft ? [[leftSteps lastObject] identifier] : [[rightSteps lastObject] identifier];
|
|
NSString *conclusionIdentifier = hasCompletionStep ? [[steps lastObject] identifier] : ORKNullStepIdentifier;
|
|
NSPredicate *secondPredicate = firstIsLeft ? predicateRight : predicateLeft;
|
|
ORKStepNavigationRule *skipSecond = [[ORKPredicateStepNavigationRule alloc] initWithResultPredicates:@[secondPredicate]
|
|
destinationStepIdentifiers:@[conclusionIdentifier]];
|
|
[task setNavigationRule:skipSecond forTriggerStepIdentifier:triggerIdentifier];
|
|
|
|
// Setup step modifier to change the finished spoken step if skipping the second hand
|
|
NSString *key = NSStringFromSelector(@selector(finishedSpokenInstruction));
|
|
NSString *value = ORKLocalizedString(@"TREMOR_TEST_COMPLETED_INSTRUCTION", nil);
|
|
ORKStepModifier *stepModifier = [[ORKKeyValueStepModifier alloc] initWithResultPredicate:secondPredicate
|
|
keyValueMap:@{key: value}];
|
|
[task setStepModifier:stepModifier forStepIdentifier:triggerIdentifier];
|
|
}
|
|
|
|
return task;
|
|
}
|
|
|
|
|
|
#pragma mark - trailmakingTask
|
|
|
|
NSString *const ORKTrailmakingStepIdentifier = @"trailmaking";
|
|
|
|
+ (ORKOrderedTask *)trailmakingTaskWithIdentifier:(NSString *)identifier
|
|
intendedUseDescription:(nullable NSString *)intendedUseDescription
|
|
trailmakingInstruction:(nullable NSString *)trailmakingInstruction
|
|
trailType:(ORKTrailMakingTypeIdentifier)trailType
|
|
options:(ORKPredefinedTaskOption)options {
|
|
NSMutableArray<__kindof ORKStep *> *steps = [NSMutableArray array];
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction0StepIdentifier];
|
|
step.title = ORKLocalizedString(@"TRAILMAKING_TASK_TITLE", nil);
|
|
step.text = intendedUseDescription;
|
|
step.detailText = ORKLocalizedString(@"TRAILMAKING_INTENDED_USE", nil);
|
|
step.image = [UIImage imageNamed:@"trailmaking" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier];
|
|
step.title = ORKLocalizedString(@"TRAILMAKING_TASK_TITLE", nil);
|
|
if ([trailType isEqualToString:ORKTrailMakingTypeIdentifierA]) {
|
|
step.detailText = ORKLocalizedString(@"TRAILMAKING_INTENDED_USE2_A", nil);
|
|
} else {
|
|
step.detailText = ORKLocalizedString(@"TRAILMAKING_INTENDED_USE2_B", nil);
|
|
}
|
|
step.image = [UIImage imageNamed:@"trailmaking" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
|
|
{
|
|
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction2StepIdentifier];
|
|
step.title = ORKLocalizedString(@"TRAILMAKING_TASK_TITLE", nil);
|
|
step.text = trailmakingInstruction ? : ORKLocalizedString(@"TRAILMAKING_INTRO_TEXT", nil);
|
|
step.detailText = ORKLocalizedString(@"TRAILMAKING_CALL_TO_ACTION", nil);
|
|
if (UIAccessibilityIsVoiceOverRunning()) {
|
|
step.detailText = [NSString stringWithFormat:ORKLocalizedString(@"AX_TRAILMAKING_CALL_TO_ACTION_VOICEOVER", nil), step.detailText];
|
|
}
|
|
step.image = [UIImage imageNamed:@"trailmaking" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
|
|
step.imageContentMode = UIViewContentModeCenter;
|
|
step.shouldTintImages = YES;
|
|
step.shouldAutomaticallyAdjustImageTintColor = YES;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
}
|
|
|
|
{
|
|
ORKCountdownStep *step = [[ORKCountdownStep alloc] initWithIdentifier:ORKCountdownStepIdentifier];
|
|
step.title = ORKLocalizedString(@"TRAILMAKING_TASK_TITLE", nil);
|
|
step.stepDuration = 3.0;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
{
|
|
ORKTrailmakingStep *step = [[ORKTrailmakingStep alloc] initWithIdentifier:ORKTrailmakingStepIdentifier];
|
|
step.title = ORKLocalizedString(@"TRAILMAKING_TASK_TITLE", nil);
|
|
step.trailType = trailType;
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
if (!(options & ORKPredefinedTaskOptionExcludeConclusion)) {
|
|
ORKInstructionStep *step = [self makeCompletionStep];
|
|
|
|
ORKStepArrayAddStep(steps, step);
|
|
}
|
|
|
|
|
|
ORKOrderedTask *task = [[ORKOrderedTask alloc] initWithIdentifier:identifier steps:steps];
|
|
|
|
return task;
|
|
}
|
|
|
|
@end
|