From b90beb74d8b08331ab43d0635e24ad10ff7b6305 Mon Sep 17 00:00:00 2001 From: Vishnu Ravi Date: Sat, 30 Mar 2024 14:41:33 -0400 Subject: [PATCH 1/3] Initial setup of onboarding flow --- .github/workflows/build-and-test.yml | 7 +- CITATION.cff | 16 +-- CONTRIBUTORS.md | 6 +- LICENSE.md | 9 -- README.md | 18 ++-- StrokeCog.xcodeproj/project.pbxproj | 22 +++-- .../xcshareddata/xcschemes/StrokeCog.xcscheme | 4 +- StrokeCog/Contacts/Contacts.swift | 6 +- StrokeCog/Onboarding/InterestingModules.swift | 8 -- StrokeCog/Onboarding/OnboardingFlow.swift | 1 + StrokeCog/Onboarding/StudyIDView.swift | 98 +++++++++++++++++++ StrokeCog/Onboarding/Welcome.swift | 4 +- StrokeCog/Resources/ConsentDocument.md | 63 +++++++++++- StrokeCog/Resources/Localizable.xcstrings | 76 +++++++++----- StrokeCog/Resources/studyIDs.csv | 5 + StrokeCog/StrokeCogDelegate.swift | 22 +++-- StrokeCogUITests/ContactsTests.swift | 2 +- StrokeCogUITests/OnboardingTests.swift | 32 +++--- 18 files changed, 294 insertions(+), 105 deletions(-) delete mode 100644 LICENSE.md create mode 100644 StrokeCog/Onboarding/StudyIDView.swift create mode 100644 StrokeCog/Resources/studyIDs.csv diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 352bc84..1591d59 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -14,11 +14,6 @@ on: workflow_call: jobs: - reuse_action: - name: REUSE Compliance Check - uses: StanfordBDHG/.github/.github/workflows/reuse.yml@v2 - permissions: - contents: read swiftlint: name: SwiftLint uses: StanfordBDHG/.github/.github/workflows/swiftlint.yml@v2 @@ -31,7 +26,7 @@ jobs: contents: read buildandtest: name: Build and Test - uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2 + uses: StanfordBDHG/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2 permissions: contents: read with: diff --git a/CITATION.cff b/CITATION.cff index 024d4cd..bfd75a2 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -1,5 +1,5 @@ # -# This source file is part of the StrokeCog based on the Stanford Spezi Template Application project +# This source file is part of the StrokeCog proejct based on the Stanford Spezi Template Application project # # SPDX-FileCopyrightText: 2023 Stanford University # @@ -9,13 +9,13 @@ cff-version: 1.2.0 message: "If you use this software, please cite it as below." authors: -- family-names: "Schmiedmayer" - given-names: "Paul" - orcid: "https://orcid.org/0000-0002-8607-9148" - family-names: "Ravi" given-names: "Vishnu" orcid: "https://orcid.org/0000-0003-0359-1275" -- family-names: "Aalami" - given-names: "Oliver" - orcid: "https://orcid.org/0000-0002-7799-2429" -title: "StrokeCog" +- family-names: "Dobrota Lai" + given-names: "Sylvie" + orcid: "https://orcid.org/0000-0001-5134-2105" +- family-names: "Odden" + given-names: "Michelle" + orcid: "https://orcid.org/0000-0002-5974-3648" +title: "StrokeCog" \ No newline at end of file diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 8d71621..fb9ed67 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -11,7 +11,5 @@ SPDX-License-Identifier: MIT StrokeCog Contributors ================================= -* [Paul Schmiedmayer](https://github.com/PSchmiedmayer) -* [Andreas Bauer](https://github.com/Supereg) -* [Philipp Zagar](https://github.com/philippzagar) -* [Nikolai Madlener](https://github.com/NikolaiMadlener) \ No newline at end of file +* [Vishnu Ravi](https://github.com/vishnuravi) +* [Sylvie Dobrata Lai](https://github.com/sylvieddl) \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index c76f2c7..0000000 --- a/LICENSE.md +++ /dev/null @@ -1,9 +0,0 @@ -MIT License - -Copyright (c) 2023 Stanford University - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 52d7a3a..f4376cf 100644 --- a/README.md +++ b/README.md @@ -10,23 +10,21 @@ SPDX-License-Identifier: MIT # StrokeCog -This repository contains the StrokeCog. -The StrokeCog is using the [Spezi](https://github.com/StanfordSpezi/Spezi) ecosystem and builds on top of the [Stanford Spezi Template Application](https://github.com/StanfordSpezi/SpeziTemplateApplication). +This repository contains the mobile application for the StrokeCog study at Stanford University. -> [!NOTE]  -> Do you want to learn more about the Stanford Spezi Template Application and how to use, extend, and modify this application? Check out the [Stanford Spezi Template Application documentation](https://stanfordspezi.github.io/SpeziTemplateApplication) +The StrokeCog study was built using the [Spezi](https://github.com/StanfordSpezi/Spezi) ecosystem and builds on top of the [Stanford Spezi Template Application](https://github.com/StanfordSpezi/SpeziTemplateApplication). ## StrokeCog Features -*Provide a comprehensive description of your application, including figures showing the application. You can learn more on how to structure a README in the [Stanford Spezi Documentation Guide](https://swiftpackageindex.com/stanfordspezi/spezi/documentation/spezi/documentation-guide)* +- Location data collection +- Health data collection +- Daily surveys ## Contributing -*Ensure that you add an adequate contribution section to this README.* +- Vishnu Ravi (vishnur@stanford.edu) +- Sylvie Dobrota Lai (sylvied@stanford.edu) - -## License - -This project is licensed under the MIT License. See [Licenses](LICENSES) for more information. +[Odden Research Lab at Stanford University](https://michelleodden.com) \ No newline at end of file diff --git a/StrokeCog.xcodeproj/project.pbxproj b/StrokeCog.xcodeproj/project.pbxproj index 32e5ab3..20c689d 100644 --- a/StrokeCog.xcodeproj/project.pbxproj +++ b/StrokeCog.xcodeproj/project.pbxproj @@ -67,6 +67,8 @@ 5680DD392AB8983D004E6D4A /* PackageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5680DD382AB8983D004E6D4A /* PackageCell.swift */; }; 5680DD3E2AB8CD84004E6D4A /* ContributionsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5680DD3D2AB8CD84004E6D4A /* ContributionsTest.swift */; }; 56F6F2A02AB441930022FE5A /* ContributionsList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56F6F29F2AB441930022FE5A /* ContributionsList.swift */; }; + 63BBF8162BB8993B006890CE /* StudyIDView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63BBF8152BB8993B006890CE /* StudyIDView.swift */; }; + 63BBF8192BB89CF7006890CE /* studyIDs.csv in Resources */ = {isa = PBXBuildFile; fileRef = 63BBF8182BB89CF7006890CE /* studyIDs.csv */; }; 653A2551283387FE005D4D48 /* StrokeCog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 653A2550283387FE005D4D48 /* StrokeCog.swift */; }; 653A255528338800005D4D48 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 653A255428338800005D4D48 /* Assets.xcassets */; }; 653A256228338800005D4D48 /* StrokeCogTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 653A256128338800005D4D48 /* StrokeCogTests.swift */; }; @@ -140,6 +142,8 @@ 5680DD382AB8983D004E6D4A /* PackageCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PackageCell.swift; sourceTree = ""; }; 5680DD3D2AB8CD84004E6D4A /* ContributionsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContributionsTest.swift; sourceTree = ""; }; 56F6F29F2AB441930022FE5A /* ContributionsList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContributionsList.swift; sourceTree = ""; }; + 63BBF8152BB8993B006890CE /* StudyIDView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StudyIDView.swift; sourceTree = ""; }; + 63BBF8182BB89CF7006890CE /* studyIDs.csv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = studyIDs.csv; sourceTree = ""; }; 653A254D283387FE005D4D48 /* StrokeCog.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = StrokeCog.app; sourceTree = BUILT_PRODUCTS_DIR; }; 653A2550283387FE005D4D48 /* StrokeCog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StrokeCog.swift; sourceTree = ""; }; 653A255428338800005D4D48 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -241,6 +245,7 @@ 2FE5DC2F29EDD7CA004B9AB4 /* Consent.swift */, 2FE5DC3029EDD7CA004B9AB4 /* HealthKitPermissions.swift */, 2F65B44D2A3B8B0600A36932 /* NotificationPermissions.swift */, + 63BBF8152BB8993B006890CE /* StudyIDView.swift */, ); path = Onboarding; sourceTree = ""; @@ -250,6 +255,7 @@ children = ( 653A255428338800005D4D48 /* Assets.xcassets */, 2FA0BFEC2ACC977500E0EF83 /* Localizable.xcstrings */, + 63BBF8182BB89CF7006890CE /* studyIDs.csv */, 2FE5DC2C29EDD78E004B9AB4 /* ConsentDocument.md */, 2FE5DC2A29EDD78D004B9AB4 /* AppIcon.png */, 2FE5DC5529EDD811004B9AB4 /* SocialSupportQuestionnaire.json */, @@ -537,6 +543,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 63BBF8192BB89CF7006890CE /* studyIDs.csv in Resources */, 2FC3439229EE634B002D773C /* ConsentDocument.md in Resources */, 2FC3439129EE6349002D773C /* AppIcon.png in Resources */, 653A255528338800005D4D48 /* Assets.xcassets in Resources */, @@ -615,6 +622,7 @@ 5680DD392AB8983D004E6D4A /* PackageCell.swift in Sources */, 2F5E32BD297E05EA003432F8 /* StrokeCogDelegate.swift in Sources */, 2FE5DC5229EDD7FA004B9AB4 /* StrokeCogScheduler.swift in Sources */, + 63BBF8162BB8993B006890CE /* StudyIDView.swift in Sources */, A9FE7AD02AA39BAB0077B045 /* AccountSheet.swift in Sources */, 653A2551283387FE005D4D48 /* StrokeCog.swift in Sources */, 2FE5DC3629EDD7CA004B9AB4 /* HealthKitPermissions.swift in Sources */, @@ -751,10 +759,10 @@ INFOPLIST_KEY_NSMotionUsageDescription = "This message should never appear. Please adjust this when you start using motion information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect."; INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "This message should never appear. Please adjust this when you start using speecg information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect."; INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; INFOPLIST_KEY_UIStrokeCoglicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIStrokeCoglicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -953,10 +961,10 @@ INFOPLIST_KEY_NSMotionUsageDescription = "This message should never appear. Please adjust this when you start using motion information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect."; INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "This message should never appear. Please adjust this when you start using speecg information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect."; INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; INFOPLIST_KEY_UIStrokeCoglicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIStrokeCoglicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1000,10 +1008,10 @@ INFOPLIST_KEY_NSMotionUsageDescription = "This message should never appear. Please adjust this when you start using motion information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect."; INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "This message should never appear. Please adjust this when you start using speecg information. We have to put this in here as ResearchKit has the possibility to use it and not putting it here returns an error on AppStore Connect."; INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; INFOPLIST_KEY_UIStrokeCoglicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIStrokeCoglicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1012,7 +1020,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.odden.strokecog; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "StrokeCog"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = StrokeCog; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; diff --git a/StrokeCog.xcodeproj/xcshareddata/xcschemes/StrokeCog.xcscheme b/StrokeCog.xcodeproj/xcshareddata/xcschemes/StrokeCog.xcscheme index 4b87a49..fcbb02d 100644 --- a/StrokeCog.xcodeproj/xcshareddata/xcschemes/StrokeCog.xcscheme +++ b/StrokeCog.xcodeproj/xcshareddata/xcschemes/StrokeCog.xcscheme @@ -79,7 +79,7 @@ + isEnabled = "NO"> + isEnabled = "YES"> diff --git a/StrokeCog/Contacts/Contacts.swift b/StrokeCog/Contacts/Contacts.swift index f5b8b36..25f2625 100644 --- a/StrokeCog/Contacts/Contacts.swift +++ b/StrokeCog/Contacts/Contacts.swift @@ -16,11 +16,11 @@ struct Contacts: View { let contacts = [ Contact( name: PersonNameComponents( - givenName: "Leland", - familyName: "Stanford" + givenName: "Dr. Michelle", + familyName: "Odden" ), image: Image(systemName: "figure.wave.circle"), // swiftlint:disable:this accessibility_label_for_image - title: "University Founder", + title: "Principal Investigator", description: String(localized: "LELAND_STANFORD_BIO"), organization: "Stanford University", address: { diff --git a/StrokeCog/Onboarding/InterestingModules.swift b/StrokeCog/Onboarding/InterestingModules.swift index e458620..1ec4957 100644 --- a/StrokeCog/Onboarding/InterestingModules.swift +++ b/StrokeCog/Onboarding/InterestingModules.swift @@ -26,14 +26,6 @@ struct InterestingModules: View { SequentialOnboardingView.Content( title: "INTERESTING_MODULES_AREA2_TITLE", description: "INTERESTING_MODULES_AREA2_DESCRIPTION" - ), - SequentialOnboardingView.Content( - title: "INTERESTING_MODULES_AREA3_TITLE", - description: "INTERESTING_MODULES_AREA3_DESCRIPTION" - ), - SequentialOnboardingView.Content( - title: "INTERESTING_MODULES_AREA4_TITLE", - description: "INTERESTING_MODULES_AREA4_DESCRIPTION" ) ], actionText: "INTERESTING_MODULES_BUTTON", diff --git a/StrokeCog/Onboarding/OnboardingFlow.swift b/StrokeCog/Onboarding/OnboardingFlow.swift index 4c45195..65586d3 100644 --- a/StrokeCog/Onboarding/OnboardingFlow.swift +++ b/StrokeCog/Onboarding/OnboardingFlow.swift @@ -37,6 +37,7 @@ struct OnboardingFlow: View { OnboardingStack(onboardingFlowComplete: $completedOnboardingFlow) { Welcome() InterestingModules() + StudyIDView() if !FeatureFlags.disableFirebase { AccountOnboarding() diff --git a/StrokeCog/Onboarding/StudyIDView.swift b/StrokeCog/Onboarding/StudyIDView.swift new file mode 100644 index 0000000..fbdf415 --- /dev/null +++ b/StrokeCog/Onboarding/StudyIDView.swift @@ -0,0 +1,98 @@ +// +// StudyIDView.swift +// StrokeCog +// +// Created by Vishnu Ravi on 3/30/24. +// + +import SpeziOnboarding +import SpeziValidation +import SpeziViews +import SwiftUI + +struct StudyIDView: View { + @Environment(OnboardingNavigationPath.self) private var onboardingNavigationPath + @State private var studyID = "" + @State private var showInvalidIDAlert = false + @ValidationState private var validation + + var body: some View { + VStack(spacing: 32) { + OnboardingView( + titleView: { + OnboardingTitleView( + title: "Study ID", + subtitle: "Welcome to the StrokeCog study. To get started, please enter your study ID." + ) + }, + contentView: { + studyIDEntryView + }, + actionView: { + OnboardingActionsView( + "Next", + action: { + guard validation.validateSubviews() else { + return + } + + if verify(id: studyID) { + onboardingNavigationPath.nextStep() + } else { + showInvalidIDAlert = true + } + } + ) + } + ) + } + } + + @ViewBuilder private var studyIDEntryView: some View { + VerifiableTextField( + LocalizedStringResource("Tap here and enter your Study ID"), + text: $studyID + ) + .autocorrectionDisabled() + .textInputAutocapitalization(.characters) + .textContentType(.oneTimeCode) + .validate(input: studyID, rules: [validationRule]) + .receiveValidation(in: $validation) + .alert( + "Error", + isPresented: $showInvalidIDAlert + ) { + Text("You've entered an invalid study ID.") + Button("Try Again") { } + } + } + + private var validationRule: ValidationRule { + ValidationRule( + rule: { studyID in + studyID.count >= 4 + }, + message: "A study ID should be at least 4 characters long." + ) + } + + private func verify(id: String) -> Bool { + var validStudyIDs = [String]() + + if let studyIDsURL = Bundle.main.url(forResource: "studyIDs", withExtension: ".csv"), + let studyIDs = try? String(contentsOf: studyIDsURL) { + let allStudyIDs = studyIDs.components(separatedBy: "\n") + + for studyID in allStudyIDs { + validStudyIDs.append(studyID.filter { !$0.isWhitespace }) + } + + return validStudyIDs.contains(id) + } + return false + } +} + +#Preview { + StudyIDView() +} diff --git a/StrokeCog/Onboarding/Welcome.swift b/StrokeCog/Onboarding/Welcome.swift index a8012d3..73f6055 100644 --- a/StrokeCog/Onboarding/Welcome.swift +++ b/StrokeCog/Onboarding/Welcome.swift @@ -21,7 +21,7 @@ struct Welcome: View { areas: [ OnboardingInformationView.Content( icon: { - Image(systemName: "apps.iphone") + Image(systemName: "map") .accessibilityHidden(true) }, title: "WELCOME_AREA1_TITLE", @@ -29,7 +29,7 @@ struct Welcome: View { ), OnboardingInformationView.Content( icon: { - Image(systemName: "shippingbox.fill") + Image(systemName: "figure.run") .accessibilityHidden(true) }, title: "WELCOME_AREA2_TITLE", diff --git a/StrokeCog/Resources/ConsentDocument.md b/StrokeCog/Resources/ConsentDocument.md index 5f9df6f..543b262 100644 --- a/StrokeCog/Resources/ConsentDocument.md +++ b/StrokeCog/Resources/ConsentDocument.md @@ -1 +1,62 @@ -Spezi can render consent documents in the markdown format: This is a *markdown* **example**. +You are invited to participate in a research study on life space, or the space within which people live, work, and recreate. Based on a custom iPhone Life Space application, we will develop individual life space maps (geographic footprint). We will sample Google Street View images from the life space and extract features using computer learning in order to evaluate the association of built environment features with health and well-being. This research study is looking for X participants to be enrolled. Stanford University expects to enroll X research study participants. + +To participate in this study, you will download the StrokeCog App and complete the consent form. The StrokeCog App will passively capture your location for two weeks. Each evening, you will complete a brief survey of three questions. At the end of the data collection period, you will be sent an anonymous survey asking about your experiences using the app. + +**Risks and Benefits** + +The primary risk associated with this study is a potential loss of privacy, and we have taken all measures to minimize this risk. If for any reason you do not wish to have your location recorded temporarily, you can toggle the “Track My Location” button and the app will stop recording until you start it again. Your data are stored securely, and your name and Apple ID will be removed from your data when it is downloaded. + +We cannot and do not guarantee or promise that you will receive any benefits from this study. + +**Time Involvement** + +Our StrokeCog App will passively capture location for a period of two weeks. Each evening, you will be asked to complete a brief survey of three questions (1-2 minutes). At the end of the data collection period, you will be sent a survey asking about your experiences using the app; this survey will take less than 10 minutes to complete. + +**Payment** + +You can receive up to $100 for your participation. You will receive $4/day for every day that you complete the survey for up to 14 days of participation and a bonus of $14 if you participate all 14 days. You will receive $30 for completion of the close out survey. + +**Privacy and Data Use** + +The information in this study will be used only for research purposes. This study is not anonymous. Your name will be shared with the investigators at the Coordinating Center so they know that you are participating in this study and they will send us a study specific ID to be used only for this study. Your location data and data from the evening survey will deidentified and coded with this ID and stored at Stanford. Summary level data about your life space (such as the area in km2) will be shared with the Coordinating Center using the study specific ID. These summary data will not contain any specific location data nor geographic identifiers. The summary level data will be linked to the other health data collected at the Coordinating Center, and shared with Stanford. At no point will your location data or other personal identifiers be linked with your health data. + +**Future Use of Private Information** + +Research using private information is an important way to try to understand human health. You are being given this information because the investigators want to save private information for future research. + +Identifiers will be removed from identifiable private information and, after such removal, the information could be used for future research studies or distributed to another investigator for future research studies without additional informed consent from you. + +**Participant's Rights** + +If you have read this form and have decided to participate in this project, please understand your participation is voluntary and you have the right to withdraw your consent or discontinue participation at any time without penalty or loss of benefits to which you are otherwise entitled. + +Your decision whether or not to participate in this study will not affect your participation in the REGARDS study. + +You have the right to refuse to answer particular questions. + +The alternative to participating in this study is not to participate. + +The results of this research study may be presented at scientific or professional meetings or published in scientific journals. However, your identity will not be disclosed. + +**Withdraw from Study** + +If you first agree to participate and then you change your mind, you are free to withdraw your consent and discontinue your participation at any time. + +If you decide to withdraw your consent to participate in this study, you should contact the research team by phone at (650) 721-0230 or email to lifespace@stanford.edu. + +If you decide to withdraw from the study, you can delete the LifeSpace App from your phone. + +The Protocol Director may also withdraw you from the study without your consent for one or more of the following reasons: + +- Failure to follow the instructions of the Protocol Director and study staff. +- Unanticipated circumstances. + +**Sponsor** + +Stanford University is providing financial support and/or material for this study. + +**Contact Information** + +Questions, Concerns, or Complaints: If you have any questions, concerns or complaints about this research study, its procedures, risks and benefits, you should ask the Protocol Director, Michelle Odden, (650) 721-0230 or lifespace@stanford.edu. You should also contact her at any time if you feel you have been hurt by being a part of this study. + +Independent Contact: If you are not satisfied with how this study is being conducted, or if you have any concerns, complaints, or general questions about the research or your rights as a participant, please contact the Stanford Institutional Review Board (IRB) to speak to someone independent of the research team at (650)-723-5244 or toll free at 1-866-680-2906. You can also write to the Stanford IRB, Stanford University, 1705 El Camino Real, Palo Alto, CA 94306. diff --git a/StrokeCog/Resources/Localizable.xcstrings b/StrokeCog/Resources/Localizable.xcstrings index af35f22..a2529b8 100644 --- a/StrokeCog/Resources/Localizable.xcstrings +++ b/StrokeCog/Resources/Localizable.xcstrings @@ -1,6 +1,9 @@ { "sourceLanguage" : "en", "strings" : { + "A study ID should be at least 4 characters long." : { + + }, "ACCOUNT_NEXT" : { "localizations" : { "en" : { @@ -36,7 +39,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "The StrokeCog demonstrates the usage of the Firebase Account Module." + "value" : "Sign Up or Log In to the StrokeCog Study with either your email or Sign In With Apple." } } } @@ -133,6 +136,9 @@ } } } + }, + "Error" : { + }, "HEALTHKIT_PERMISSIONS_BUTTON" : { "localizations" : { @@ -149,7 +155,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "This onboarding step allows you to customize the onboarding flow to explain how the application uses the HealhtKit data and allows a user to cusomize the selection." + "value" : "The data will be used to understand how your activity is related to your condition." } } } @@ -159,7 +165,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Spezi can access data from HealthKit using the HealthKitDataSource module." + "value" : "The StrokeCog study app will collect activity data from your phone and/or watch while you are enrolled in the study." } } } @@ -180,7 +186,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "The onboarding module allows you to build an onboarding flow like this one." + "value" : "Please make sure your phone is switched on at all times." } } } @@ -190,7 +196,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Onboarding" + "value" : "Phone" } } } @@ -200,7 +206,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "The application uses HL7 FHIR to provide a common standard to encode data gathered by the application." + "value" : "Please note that the app may reduce your phone's battery more quickly than expected." } } } @@ -210,47 +216,51 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "HL7 FHIR" + "value" : "Battery" } } } }, "INTERESTING_MODULES_AREA3_DESCRIPTION" : { + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "The contact module allows you to display contact information in your application." + "value" : "We can put additional important information here." } } } }, "INTERESTING_MODULES_AREA3_TITLE" : { + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Contact" + "value" : "Other important information." } } } }, "INTERESTING_MODULES_AREA4_DESCRIPTION" : { + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "The HealthKit data source module allows you to fetch data from HealthKit and e.g. transform it to FHIR resources." + "value" : "We can put additional important information here." } } } }, "INTERESTING_MODULES_AREA4_TITLE" : { + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "HealthKit Data Source" + "value" : "Other important information." } } } @@ -270,7 +280,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Here are a few Spezi modules that are interesting to know about ..." + "value" : "Please read the following information carefully." } } } @@ -281,7 +291,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Interesting Modules" + "value" : "Information" } } } @@ -316,6 +326,9 @@ } } } + }, + "Next" : { + }, "NOTIFICATION_PERMISSIONS_BUTTON" : { "localizations" : { @@ -332,7 +345,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "The Spezi Scheduler module enables to send out local notifications when a new event of a task is scheduled." + "value" : "You will receive a notification when it is time to complete each day's survey." } } } @@ -342,7 +355,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Spezi Scheduler Notifications." + "value" : "StrokeCog Notifications" } } } @@ -398,6 +411,12 @@ } } } + }, + "Study ID" : { + + }, + "Tap here and enter your Study ID" : { + }, "TASK_CONTEXT_ACTION_QUESTIONNAIRE" : { "localizations" : { @@ -448,13 +467,19 @@ } } } + }, + "Try Again" : { + + }, + "Welcome to the StrokeCog study. To get started, please enter your study ID." : { + }, "WELCOME_AREA1_DESCRIPTION" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "The Spezi Framework builds the foundation of this application." + "value" : "Your location data will be collected during the study period." } } } @@ -464,7 +489,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "The Spezi Framework" + "value" : "Location Data" } } } @@ -474,7 +499,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Spezi uses the Swift Package Manager to import it as a dependency." + "value" : "Your activity data will be collected during the study period." } } } @@ -484,7 +509,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Swift Package Manager" + "value" : "Activity Data" } } } @@ -494,7 +519,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Spezi offers several modules including HealthKit integration, questionnaires, and more ..." + "value" : "You will be asked to complete daily surveys during the study." } } } @@ -504,7 +529,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Spezi Modules" + "value" : "Daily Surveys" } } } @@ -514,7 +539,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Learn More" + "value" : "Next" } } } @@ -524,7 +549,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "This application demonstrates several Spezi features & modules." + "value" : "The StrokeCog Study investigates the relationship between life space and cognitive decline after a stroke." } } } @@ -535,10 +560,13 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Spezi\nStrokeCog" + "value" : "StrokeCog Study" } } } + }, + "You've entered an invalid study ID." : { + } }, "version" : "1.0" diff --git a/StrokeCog/Resources/studyIDs.csv b/StrokeCog/Resources/studyIDs.csv new file mode 100644 index 0000000..6084c9c --- /dev/null +++ b/StrokeCog/Resources/studyIDs.csv @@ -0,0 +1,5 @@ +1234 +4321 +1111 +2222 +3333 diff --git a/StrokeCog/StrokeCogDelegate.swift b/StrokeCog/StrokeCogDelegate.swift index b1715a4..9568c30 100644 --- a/StrokeCog/StrokeCogDelegate.swift +++ b/StrokeCog/StrokeCogDelegate.swift @@ -23,12 +23,7 @@ class StrokeCogDelegate: SpeziAppDelegate { Configuration(standard: StrokeCogStandard()) { if !FeatureFlags.disableFirebase { AccountConfiguration(configuration: [ - .requires(\.userId), - .requires(\.name), - - // additional values stored using the `FirestoreAccountStorage` within our Standard implementation - .collects(\.genderIdentity), - .collects(\.dateOfBirth) + .requires(\.userId) ]) if FeatureFlags.useFirebaseEmulator { @@ -75,9 +70,18 @@ class StrokeCogDelegate: SpeziAppDelegate { private var healthKit: HealthKit { HealthKit { - CollectSample( - HKQuantityType(.stepCount), - deliverySetting: .anchorQuery(.afterAuthorizationAndApplicationWillLaunch) + CollectSamples( + [ + HKQuantityType(.stepCount), + HKQuantityType(.walkingSpeed), + HKQuantityType(.walkingAsymmetryPercentage), + HKQuantityType(.appleWalkingSteadiness), + HKQuantityType(.stepCount), + HKQuantityType(.appleStandTime), + HKQuantityType(.appleMoveTime), + HKCategoryType(.sleepAnalysis) + ], + deliverySetting: .manual() ) } } diff --git a/StrokeCogUITests/ContactsTests.swift b/StrokeCogUITests/ContactsTests.swift index ee63845..7f927e7 100644 --- a/StrokeCogUITests/ContactsTests.swift +++ b/StrokeCogUITests/ContactsTests.swift @@ -27,7 +27,7 @@ class ContactsTests: XCTestCase { XCTAssertTrue(app.tabBars["Tab Bar"].buttons["Contacts"].waitForExistence(timeout: 2)) app.tabBars["Tab Bar"].buttons["Contacts"].tap() - XCTAssertTrue(app.staticTexts["Contact: Leland Stanford"].waitForExistence(timeout: 2)) + XCTAssertTrue(app.staticTexts["Contact: Dr. Michelle Odden"].waitForExistence(timeout: 2)) XCTAssertTrue(app.buttons["Call"].waitForExistence(timeout: 2)) XCTAssertTrue(app.buttons["Text"].waitForExistence(timeout: 2)) diff --git a/StrokeCogUITests/OnboardingTests.swift b/StrokeCogUITests/OnboardingTests.swift index 47299ef..2e62449 100644 --- a/StrokeCogUITests/OnboardingTests.swift +++ b/StrokeCogUITests/OnboardingTests.swift @@ -58,7 +58,7 @@ extension XCUIApplication { func conductOnboardingIfNeeded(email: String = "leland@stanford.edu") throws { let app = XCUIApplication() - if app.staticTexts["Spezi\nStrokeCog"].waitForExistence(timeout: 5) { + if app.staticTexts["StrokeCog Study"].waitForExistence(timeout: 5) { try app.navigateOnboardingFlow(email: email) } } @@ -69,6 +69,9 @@ extension XCUIApplication { ) throws { try navigateOnboardingFlowWelcome() try navigateOnboardingFlowInterestingModules() + if staticTexts["Study ID"].waitForExistence(timeout: 5) { + try navigateStudyID() + } if staticTexts["Your Account"].waitForExistence(timeout: 5) { try navigateOnboardingAccount(email: email) } @@ -82,16 +85,16 @@ extension XCUIApplication { } private func navigateOnboardingFlowWelcome() throws { - XCTAssertTrue(staticTexts["Spezi\nStrokeCog"].waitForExistence(timeout: 5)) + XCTAssertTrue(staticTexts["StrokeCog Study"].waitForExistence(timeout: 5)) - XCTAssertTrue(buttons["Learn More"].waitForExistence(timeout: 2)) - buttons["Learn More"].tap() + XCTAssertTrue(buttons["Next"].waitForExistence(timeout: 2)) + buttons["Next"].tap() } private func navigateOnboardingFlowInterestingModules() throws { - XCTAssertTrue(staticTexts["Interesting Modules"].waitForExistence(timeout: 5)) + XCTAssertTrue(staticTexts["Information"].waitForExistence(timeout: 5)) - for _ in 1..<4 { + for _ in 1..<2 { XCTAssertTrue(buttons["Next"].waitForExistence(timeout: 2)) buttons["Next"].tap() } @@ -100,6 +103,14 @@ extension XCUIApplication { buttons["Next"].tap() } + private func navigateStudyID() throws { + XCTAssertTrue(staticTexts["Study ID"].waitForExistence(timeout: 5)) + + try textFields["Tap here and enter your Study ID"].enter(value: "1234") + + buttons["Next"].tap() + } + private func navigateOnboardingAccount(email: String) throws { if buttons["Logout"].waitForExistence(timeout: 2.0) { buttons["Logout"].tap() @@ -112,8 +123,6 @@ extension XCUIApplication { try collectionViews.textFields["E-Mail Address"].enter(value: email) try collectionViews.secureTextFields["Password"].enter(value: "StanfordRocks") - try textFields["enter first name"].enter(value: "Leland") - try textFields["enter last name"].enter(value: "Stanford") XCTAssertTrue(collectionViews.buttons["Signup"].waitForExistence(timeout: 2)) collectionViews.buttons["Signup"].tap() @@ -123,7 +132,6 @@ extension XCUIApplication { if staticTexts["HealthKit Access"].waitForExistence(timeout: 5) && navigationBars.buttons["Back"].waitForExistence(timeout: 5) { navigationBars.buttons["Back"].tap() - XCTAssertTrue(staticTexts["Leland Stanford"].waitForExistence(timeout: 2)) XCTAssertTrue(staticTexts[email].waitForExistence(timeout: 2)) XCTAssertTrue(buttons["Next"].waitForExistence(timeout: 2)) @@ -134,6 +142,10 @@ extension XCUIApplication { private func navigateOnboardingFlowConsent() throws { XCTAssertTrue(staticTexts["Consent"].waitForExistence(timeout: 5)) + for _ in 1...7 { + swipeUp() + } + XCTAssertTrue(staticTexts["First Name"].waitForExistence(timeout: 2)) try textFields["Enter your first name ..."].enter(value: "Leland") @@ -180,9 +192,7 @@ extension XCUIApplication { navigationBars.buttons["Your Account"].tap() XCTAssertTrue(staticTexts["Account Overview"].waitForExistence(timeout: 5.0)) - XCTAssertTrue(staticTexts["Leland Stanford"].exists) XCTAssertTrue(staticTexts[email].exists) - XCTAssertTrue(staticTexts["Gender Identity, Choose not to answer"].exists) XCTAssertTrue(navigationBars.buttons["Close"].waitForExistence(timeout: 0.5)) From 5e50e89b0b58a9ad10f42a0595b825bcb9dcb85f Mon Sep 17 00:00:00 2001 From: Vishnu Ravi Date: Sat, 30 Mar 2024 19:43:29 -0400 Subject: [PATCH 2/3] Update strings --- StrokeCog/Resources/Localizable.xcstrings | 50 ++--------------------- 1 file changed, 3 insertions(+), 47 deletions(-) diff --git a/StrokeCog/Resources/Localizable.xcstrings b/StrokeCog/Resources/Localizable.xcstrings index a2529b8..399ec22 100644 --- a/StrokeCog/Resources/Localizable.xcstrings +++ b/StrokeCog/Resources/Localizable.xcstrings @@ -186,7 +186,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Please make sure your phone is switched on at all times." + "value" : "Please make sure your keep your phone switched on and your Apple Watch on all day during the study." } } } @@ -196,7 +196,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Phone" + "value" : "Note" } } } @@ -216,51 +216,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Battery" - } - } - } - }, - "INTERESTING_MODULES_AREA3_DESCRIPTION" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "We can put additional important information here." - } - } - } - }, - "INTERESTING_MODULES_AREA3_TITLE" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Other important information." - } - } - } - }, - "INTERESTING_MODULES_AREA4_DESCRIPTION" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "We can put additional important information here." - } - } - } - }, - "INTERESTING_MODULES_AREA4_TITLE" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Other important information." + "value" : "Note" } } } From 14e66eb595321ffe8fe8b534c5af168508eca28e Mon Sep 17 00:00:00 2001 From: Vishnu Ravi Date: Sat, 30 Mar 2024 19:49:59 -0400 Subject: [PATCH 3/3] Fix typo --- CITATION.cff | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CITATION.cff b/CITATION.cff index bfd75a2..81934f7 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -1,5 +1,5 @@ # -# This source file is part of the StrokeCog proejct based on the Stanford Spezi Template Application project +# This source file is part of the StrokeCog project based on the Stanford Spezi Template Application project # # SPDX-FileCopyrightText: 2023 Stanford University #