diff --git a/CareKit.xcodeproj/project.pbxproj b/CareKit.xcodeproj/project.pbxproj index aa3e725cb..81ce00b02 100644 --- a/CareKit.xcodeproj/project.pbxproj +++ b/CareKit.xcodeproj/project.pbxproj @@ -22,6 +22,8 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 186D42B51D78D8B60083EF5B /* OCKCarePlanThreshold.m in Sources */ = {isa = PBXBuildFile; fileRef = 186D42B41D78D8B60083EF5B /* OCKCarePlanThreshold.m */; }; + 186D42B71D78D8D60083EF5B /* OCKCarePlanThreshold.h in Headers */ = {isa = PBXBuildFile; fileRef = 186D42B61D78D8D60083EF5B /* OCKCarePlanThreshold.h */; settings = {ATTRIBUTES = (Public, ); }; }; 241707C31C9A3AB7005D7123 /* OCKBarChart.h in Headers */ = {isa = PBXBuildFile; fileRef = 241707A71C9A3AB7005D7123 /* OCKBarChart.h */; settings = {ATTRIBUTES = (Public, ); }; }; 241707C41C9A3AB7005D7123 /* OCKBarChart.m in Sources */ = {isa = PBXBuildFile; fileRef = 241707A81C9A3AB7005D7123 /* OCKBarChart.m */; }; 241707C51C9A3AB7005D7123 /* OCKBarSeries.h in Headers */ = {isa = PBXBuildFile; fileRef = 241707A91C9A3AB7005D7123 /* OCKBarSeries.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -44,12 +46,8 @@ 241707DE1C9A3AB7005D7123 /* OCKMessageItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 241707C21C9A3AB7005D7123 /* OCKMessageItem.m */; }; 242DBA901C9F7F8700529386 /* OCKSymptomTrackerTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 242DBA821C9F7F8700529386 /* OCKSymptomTrackerTableViewCell.h */; }; 242DBA911C9F7F8700529386 /* OCKSymptomTrackerTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 242DBA831C9F7F8700529386 /* OCKSymptomTrackerTableViewCell.m */; }; - 242DBA941C9F7F8700529386 /* OCKSymptomTrackerTableViewHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 242DBA861C9F7F8700529386 /* OCKSymptomTrackerTableViewHeader.h */; }; - 242DBA951C9F7F8700529386 /* OCKSymptomTrackerTableViewHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = 242DBA871C9F7F8700529386 /* OCKSymptomTrackerTableViewHeader.m */; }; 242DBA961C9F7F8700529386 /* OCKSymptomTrackerViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 242DBA881C9F7F8700529386 /* OCKSymptomTrackerViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 242DBA971C9F7F8700529386 /* OCKSymptomTrackerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 242DBA891C9F7F8700529386 /* OCKSymptomTrackerViewController.m */; }; - 242DBA981C9F7F8700529386 /* OCKSymptomTrackerWeekView.h in Headers */ = {isa = PBXBuildFile; fileRef = 242DBA8A1C9F7F8700529386 /* OCKSymptomTrackerWeekView.h */; }; - 242DBA991C9F7F8700529386 /* OCKSymptomTrackerWeekView.m in Sources */ = {isa = PBXBuildFile; fileRef = 242DBA8B1C9F7F8700529386 /* OCKSymptomTrackerWeekView.m */; }; 242DBA9A1C9F7F8700529386 /* OCKRingButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 242DBA8C1C9F7F8700529386 /* OCKRingButton.h */; }; 242DBA9B1C9F7F8700529386 /* OCKRingButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 242DBA8D1C9F7F8700529386 /* OCKRingButton.m */; }; 242DBA9C1C9F7F8700529386 /* OCKRingView.h in Headers */ = {isa = PBXBuildFile; fileRef = 242DBA8E1C9F7F8700529386 /* OCKRingView.h */; }; @@ -78,6 +76,7 @@ 2489298D1C90E71700EBBE1F /* OCKHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 248929891C90E71700EBBE1F /* OCKHelpers.h */; }; 2489298E1C90E71700EBBE1F /* OCKHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 2489298A1C90E71700EBBE1F /* OCKHelpers.m */; }; 24935CD21C7E341E00B208A9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 24935CD11C7E341E00B208A9 /* Assets.xcassets */; }; + 24CE2BA91E023BA30088CAF0 /* OCKCareCardDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 24EA9F9A1C9A3D420036028E /* OCKCareCardDetailViewController.m */; }; 24EA9FB21C9A3D420036028E /* OCKCareCardAdditionalInfoTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 24EA9F931C9A3D420036028E /* OCKCareCardAdditionalInfoTableViewCell.h */; }; 24EA9FB31C9A3D420036028E /* OCKCareCardAdditionalInfoTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 24EA9F941C9A3D420036028E /* OCKCareCardAdditionalInfoTableViewCell.m */; }; 24EA9FB41C9A3D420036028E /* OCKCareCardButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 24EA9F951C9A3D420036028E /* OCKCareCardButton.h */; }; @@ -85,26 +84,16 @@ 24EA9FB61C9A3D420036028E /* OCKCareCardDetailHeaderView.h in Headers */ = {isa = PBXBuildFile; fileRef = 24EA9F971C9A3D420036028E /* OCKCareCardDetailHeaderView.h */; }; 24EA9FB71C9A3D420036028E /* OCKCareCardDetailHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 24EA9F981C9A3D420036028E /* OCKCareCardDetailHeaderView.m */; }; 24EA9FB81C9A3D420036028E /* OCKCareCardDetailViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 24EA9F991C9A3D420036028E /* OCKCareCardDetailViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 24EA9FB91C9A3D420036028E /* OCKCareCardDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 24EA9F9A1C9A3D420036028E /* OCKCareCardDetailViewController.m */; }; 24EA9FBA1C9A3D420036028E /* OCKCareCardInstructionsTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 24EA9F9B1C9A3D420036028E /* OCKCareCardInstructionsTableViewCell.h */; }; 24EA9FBB1C9A3D420036028E /* OCKCareCardInstructionsTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 24EA9F9C1C9A3D420036028E /* OCKCareCardInstructionsTableViewCell.m */; }; 24EA9FBC1C9A3D420036028E /* OCKCareCardTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 24EA9F9D1C9A3D420036028E /* OCKCareCardTableViewCell.h */; }; 24EA9FBD1C9A3D420036028E /* OCKCareCardTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 24EA9F9E1C9A3D420036028E /* OCKCareCardTableViewCell.m */; }; - 24EA9FC01C9A3D420036028E /* OCKCareCardTableViewHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 24EA9FA11C9A3D420036028E /* OCKCareCardTableViewHeader.h */; }; - 24EA9FC11C9A3D420036028E /* OCKCareCardTableViewHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = 24EA9FA21C9A3D420036028E /* OCKCareCardTableViewHeader.m */; }; 24EA9FC21C9A3D420036028E /* OCKCareCardViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 24EA9FA31C9A3D420036028E /* OCKCareCardViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 24EA9FC31C9A3D420036028E /* OCKCareCardViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 24EA9FA41C9A3D420036028E /* OCKCareCardViewController.m */; }; - 24EA9FC51C9A3D420036028E /* OCKCareCardWeekView.h in Headers */ = {isa = PBXBuildFile; fileRef = 24EA9FA61C9A3D420036028E /* OCKCareCardWeekView.h */; }; - 24EA9FC61C9A3D420036028E /* OCKCareCardWeekView.m in Sources */ = {isa = PBXBuildFile; fileRef = 24EA9FA71C9A3D420036028E /* OCKCareCardWeekView.m */; }; - 24EA9FC71C9A3D420036028E /* OCKHeartButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 24EA9FA81C9A3D420036028E /* OCKHeartButton.h */; }; - 24EA9FC81C9A3D420036028E /* OCKHeartButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 24EA9FA91C9A3D420036028E /* OCKHeartButton.m */; }; - 24EA9FC91C9A3D420036028E /* OCKHeartView.h in Headers */ = {isa = PBXBuildFile; fileRef = 24EA9FAA1C9A3D420036028E /* OCKHeartView.h */; }; - 24EA9FCA1C9A3D420036028E /* OCKHeartView.m in Sources */ = {isa = PBXBuildFile; fileRef = 24EA9FAB1C9A3D420036028E /* OCKHeartView.m */; }; 24F8E4761C934FEE003B77BD /* OCKDefines_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 24F8E4741C934FEE003B77BD /* OCKDefines_Private.h */; }; 24F8E47B1C93515A003B77BD /* CareKit.strings in Resources */ = {isa = PBXBuildFile; fileRef = 24F8E47D1C93515A003B77BD /* CareKit.strings */; }; - 5A6F95081D625A590011A97A /* CareKitContactTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A6F95071D625A590011A97A /* CareKitContactTests.m */; }; - 5A9D2C931D5E432B00D0F13B /* OCKContactInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A9D2C911D5E432B00D0F13B /* OCKContactInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 5A9D2C941D5E432B00D0F13B /* OCKContactInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A9D2C921D5E432B00D0F13B /* OCKContactInfo.m */; }; + 24FD43731E56843E004439EA /* OCKConnectHeaderView.h in Headers */ = {isa = PBXBuildFile; fileRef = 24FD43711E56843E004439EA /* OCKConnectHeaderView.h */; }; + 24FD43741E56843E004439EA /* OCKConnectHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 24FD43721E56843E004439EA /* OCKConnectHeaderView.m */; }; 8605A5BE1C4F04EC00DD65FF /* CareKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 8605A5BD1C4F04EC00DD65FF /* CareKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8605A5C51C4F04EC00DD65FF /* CareKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8605A5BA1C4F04EC00DD65FF /* CareKit.framework */; }; 8605A5CA1C4F04EC00DD65FF /* CareKitCarePlanTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8605A5C91C4F04EC00DD65FF /* CareKitCarePlanTests.m */; }; @@ -136,6 +125,50 @@ 86EA9A501C9C644E00B93BB1 /* OCKHTMLPDFWriter.h in Headers */ = {isa = PBXBuildFile; fileRef = 86EA9A4C1C9C644E00B93BB1 /* OCKHTMLPDFWriter.h */; }; 86EA9A511C9C644E00B93BB1 /* OCKHTMLPDFWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = 86EA9A4D1C9C644E00B93BB1 /* OCKHTMLPDFWriter.m */; }; 86EA9A531C9C64C500B93BB1 /* CareKitPDFTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 86EA9A521C9C64C500B93BB1 /* CareKitPDFTests.m */; }; + AEB37F671DF224DA002370A6 /* OCKColor.m in Sources */ = {isa = PBXBuildFile; fileRef = AEB37F661DF224DA002370A6 /* OCKColor.m */; }; + AEB37F691DF22BEF002370A6 /* OCKColor.h in Headers */ = {isa = PBXBuildFile; fileRef = AEB37F681DF224ED002370A6 /* OCKColor.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BA0CCA4D1DA320430003EB6C /* OCKHeaderView.h in Headers */ = {isa = PBXBuildFile; fileRef = BA0CCA4B1DA320430003EB6C /* OCKHeaderView.h */; }; + BA0CCA4E1DA320430003EB6C /* OCKHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = BA0CCA4C1DA320430003EB6C /* OCKHeaderView.m */; }; + BA155DCA1EB1DFE400DB391F /* OCKPatient.h in Headers */ = {isa = PBXBuildFile; fileRef = BA155DC81EB1DFE400DB391F /* OCKPatient.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BA155DCB1EB1DFE400DB391F /* OCKPatient.m in Sources */ = {isa = PBXBuildFile; fileRef = BA155DC91EB1DFE400DB391F /* OCKPatient.m */; }; + BA274F3A1EB6F63400DD17EF /* OCKConnectMessagesViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = BA274F381EB6F63400DD17EF /* OCKConnectMessagesViewController.h */; }; + BA274F3B1EB6F63400DD17EF /* OCKConnectMessagesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BA274F391EB6F63400DD17EF /* OCKConnectMessagesViewController.m */; }; + BA274F401EB6F75300DD17EF /* OCKConnectMessageItem.h in Headers */ = {isa = PBXBuildFile; fileRef = BA274F3C1EB6F75300DD17EF /* OCKConnectMessageItem.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BA274F411EB6F75300DD17EF /* OCKConnectMessageItem.m in Sources */ = {isa = PBXBuildFile; fileRef = BA274F3D1EB6F75300DD17EF /* OCKConnectMessageItem.m */; }; + BA274F421EB6F75300DD17EF /* OCKConnectMessageTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = BA274F3E1EB6F75300DD17EF /* OCKConnectMessageTableViewCell.h */; }; + BA274F431EB6F75300DD17EF /* OCKConnectMessageTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = BA274F3F1EB6F75300DD17EF /* OCKConnectMessageTableViewCell.m */; }; + BA274F471EB703DC00DD17EF /* OCKTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = BA274F451EB703DC00DD17EF /* OCKTextView.h */; }; + BA274F481EB703DC00DD17EF /* OCKTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = BA274F461EB703DC00DD17EF /* OCKTextView.m */; }; + BA3793D81EB66BC50004A540 /* OCKPatientWidget.h in Headers */ = {isa = PBXBuildFile; fileRef = BA3793D51EB66BC50004A540 /* OCKPatientWidget.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BA3793D91EB66BC50004A540 /* OCKPatientWidget.m in Sources */ = {isa = PBXBuildFile; fileRef = BA3793D61EB66BC50004A540 /* OCKPatientWidget.m */; }; + BA3793DA1EB66BC50004A540 /* OCKPatientWidgetView.h in Headers */ = {isa = PBXBuildFile; fileRef = BA3793D71EB66BC50004A540 /* OCKPatientWidgetView.h */; }; + BA3793E61EB66D7D0004A540 /* OCKBadgePatientWidgetView.h in Headers */ = {isa = PBXBuildFile; fileRef = BA3793DC1EB66D7D0004A540 /* OCKBadgePatientWidgetView.h */; }; + BA3793E71EB66D7D0004A540 /* OCKBadgePatientWidgetView.m in Sources */ = {isa = PBXBuildFile; fileRef = BA3793DD1EB66D7D0004A540 /* OCKBadgePatientWidgetView.m */; }; + BA3793E81EB66D7D0004A540 /* OCKDefaultPatientWidgetView.h in Headers */ = {isa = PBXBuildFile; fileRef = BA3793DE1EB66D7D0004A540 /* OCKDefaultPatientWidgetView.h */; }; + BA3793E91EB66D7D0004A540 /* OCKDefaultPatientWidgetView.m in Sources */ = {isa = PBXBuildFile; fileRef = BA3793DF1EB66D7D0004A540 /* OCKDefaultPatientWidgetView.m */; }; + BA3793EA1EB66D7D0004A540 /* OCKImagePatientWidgetView.h in Headers */ = {isa = PBXBuildFile; fileRef = BA3793E01EB66D7D0004A540 /* OCKImagePatientWidgetView.h */; }; + BA3793EB1EB66D7D0004A540 /* OCKImagePatientWidgetView.m in Sources */ = {isa = PBXBuildFile; fileRef = BA3793E11EB66D7D0004A540 /* OCKImagePatientWidgetView.m */; }; + BA3793EC1EB66D7D0004A540 /* OCKPatientWidget_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = BA3793E21EB66D7D0004A540 /* OCKPatientWidget_Internal.h */; }; + BA3793ED1EB66D7D0004A540 /* OCKPatientWidgetView.m in Sources */ = {isa = PBXBuildFile; fileRef = BA3793E31EB66D7D0004A540 /* OCKPatientWidgetView.m */; }; + BA3793EE1EB66D7D0004A540 /* OCKStackedPatientWidgetView.h in Headers */ = {isa = PBXBuildFile; fileRef = BA3793E41EB66D7D0004A540 /* OCKStackedPatientWidgetView.h */; }; + BA3793EF1EB66D7D0004A540 /* OCKStackedPatientWidgetView.m in Sources */ = {isa = PBXBuildFile; fileRef = BA3793E51EB66D7D0004A540 /* OCKStackedPatientWidgetView.m */; }; + BA38457E1D9CA698007990DA /* OCKGlyph.h in Headers */ = {isa = PBXBuildFile; fileRef = BA38457C1D9CA698007990DA /* OCKGlyph.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BA38457F1D9CA698007990DA /* OCKGlyph.m in Sources */ = {isa = PBXBuildFile; fileRef = BA38457D1D9CA698007990DA /* OCKGlyph.m */; }; + BA660FF91EB1FF1100B7B1A7 /* OCKPatientHeaderView.h in Headers */ = {isa = PBXBuildFile; fileRef = BA660FF71EB1FF1100B7B1A7 /* OCKPatientHeaderView.h */; }; + BA660FFA1EB1FF1100B7B1A7 /* OCKPatientHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = BA660FF81EB1FF1100B7B1A7 /* OCKPatientHeaderView.m */; }; + BA6610001EB20DAB00B7B1A7 /* OCKCareContentsViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = BA660FFC1EB20DAB00B7B1A7 /* OCKCareContentsViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BA6610011EB20DAB00B7B1A7 /* OCKCareContentsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BA660FFD1EB20DAB00B7B1A7 /* OCKCareContentsViewController.m */; }; + BA6610031EB20DAB00B7B1A7 /* OCKReadOnlyTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = BA660FFF1EB20DAB00B7B1A7 /* OCKReadOnlyTableViewCell.m */; }; + BA8A8CA01D9DAC680078C5EA /* OCKWeekView.h in Headers */ = {isa = PBXBuildFile; fileRef = BA8A8C9E1D9DAC680078C5EA /* OCKWeekView.h */; }; + BA8A8CA11D9DAC680078C5EA /* OCKWeekView.m in Sources */ = {isa = PBXBuildFile; fileRef = BA8A8C9F1D9DAC680078C5EA /* OCKWeekView.m */; }; + BA8F07021EBE9D8800A14E5F /* OCKReadOnlyTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = BA8F07011EBE9D6400A14E5F /* OCKReadOnlyTableViewCell.h */; }; + BA94DBB61D9CA9AE002E0BFC /* OCKGlyph_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = BA94DBB41D9CA9AE002E0BFC /* OCKGlyph_Internal.h */; }; + BAD328621DB01D650021A773 /* OCKContactInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD328601DB01D650021A773 /* OCKContactInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BAD328631DB01D650021A773 /* OCKContactInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = BAD328611DB01D650021A773 /* OCKContactInfo.m */; }; + BAD328661DB024040021A773 /* OCKRingItem.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD328641DB024040021A773 /* OCKRingItem.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BAD328671DB024040021A773 /* OCKRingItem.m in Sources */ = {isa = PBXBuildFile; fileRef = BAD328651DB024040021A773 /* OCKRingItem.m */; }; + BAD3286A1DB028AF0021A773 /* OCKInsightsRingTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD328681DB028AF0021A773 /* OCKInsightsRingTableViewCell.h */; }; + BAD3286B1DB028AF0021A773 /* OCKInsightsRingTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = BAD328691DB028AF0021A773 /* OCKInsightsRingTableViewCell.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -156,6 +189,9 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 185BC3A31D81DF0F001AB0A9 /* OCKCarePlanStore v2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "OCKCarePlanStore v2.xcdatamodel"; sourceTree = ""; }; + 186D42B41D78D8B60083EF5B /* OCKCarePlanThreshold.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKCarePlanThreshold.m; sourceTree = ""; }; + 186D42B61D78D8D60083EF5B /* OCKCarePlanThreshold.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKCarePlanThreshold.h; sourceTree = ""; }; 241707A71C9A3AB7005D7123 /* OCKBarChart.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKBarChart.h; sourceTree = ""; }; 241707A81C9A3AB7005D7123 /* OCKBarChart.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKBarChart.m; sourceTree = ""; }; 241707A91C9A3AB7005D7123 /* OCKBarSeries.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKBarSeries.h; sourceTree = ""; }; @@ -178,16 +214,12 @@ 241707C21C9A3AB7005D7123 /* OCKMessageItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKMessageItem.m; sourceTree = ""; }; 242DBA821C9F7F8700529386 /* OCKSymptomTrackerTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKSymptomTrackerTableViewCell.h; sourceTree = ""; }; 242DBA831C9F7F8700529386 /* OCKSymptomTrackerTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKSymptomTrackerTableViewCell.m; sourceTree = ""; }; - 242DBA861C9F7F8700529386 /* OCKSymptomTrackerTableViewHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKSymptomTrackerTableViewHeader.h; sourceTree = ""; }; - 242DBA871C9F7F8700529386 /* OCKSymptomTrackerTableViewHeader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKSymptomTrackerTableViewHeader.m; sourceTree = ""; }; 242DBA881C9F7F8700529386 /* OCKSymptomTrackerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKSymptomTrackerViewController.h; sourceTree = ""; }; 242DBA891C9F7F8700529386 /* OCKSymptomTrackerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKSymptomTrackerViewController.m; sourceTree = ""; }; - 242DBA8A1C9F7F8700529386 /* OCKSymptomTrackerWeekView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKSymptomTrackerWeekView.h; sourceTree = ""; }; - 242DBA8B1C9F7F8700529386 /* OCKSymptomTrackerWeekView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKSymptomTrackerWeekView.m; sourceTree = ""; }; - 242DBA8C1C9F7F8700529386 /* OCKRingButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKRingButton.h; sourceTree = ""; }; - 242DBA8D1C9F7F8700529386 /* OCKRingButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKRingButton.m; sourceTree = ""; }; - 242DBA8E1C9F7F8700529386 /* OCKRingView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKRingView.h; sourceTree = ""; }; - 242DBA8F1C9F7F8700529386 /* OCKRingView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKRingView.m; sourceTree = ""; }; + 242DBA8C1C9F7F8700529386 /* OCKRingButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OCKRingButton.h; path = ../SymptomTracker/OCKRingButton.h; sourceTree = ""; }; + 242DBA8D1C9F7F8700529386 /* OCKRingButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OCKRingButton.m; path = ../SymptomTracker/OCKRingButton.m; sourceTree = ""; }; + 242DBA8E1C9F7F8700529386 /* OCKRingView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OCKRingView.h; path = ../SymptomTracker/OCKRingView.h; sourceTree = ""; }; + 242DBA8F1C9F7F8700529386 /* OCKRingView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OCKRingView.m; path = ../SymptomTracker/OCKRingView.m; sourceTree = ""; }; 244A923F1CA210F7007B42D6 /* OCKTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKTableViewCell.h; sourceTree = ""; }; 244A92401CA210F7007B42D6 /* OCKTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKTableViewCell.m; sourceTree = ""; }; 244A92411CA210F7007B42D6 /* OCKWeekLabelsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKWeekLabelsView.h; sourceTree = ""; }; @@ -224,16 +256,8 @@ 24EA9F9C1C9A3D420036028E /* OCKCareCardInstructionsTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKCareCardInstructionsTableViewCell.m; sourceTree = ""; }; 24EA9F9D1C9A3D420036028E /* OCKCareCardTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKCareCardTableViewCell.h; sourceTree = ""; }; 24EA9F9E1C9A3D420036028E /* OCKCareCardTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKCareCardTableViewCell.m; sourceTree = ""; }; - 24EA9FA11C9A3D420036028E /* OCKCareCardTableViewHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKCareCardTableViewHeader.h; sourceTree = ""; }; - 24EA9FA21C9A3D420036028E /* OCKCareCardTableViewHeader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKCareCardTableViewHeader.m; sourceTree = ""; }; 24EA9FA31C9A3D420036028E /* OCKCareCardViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKCareCardViewController.h; sourceTree = ""; }; 24EA9FA41C9A3D420036028E /* OCKCareCardViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKCareCardViewController.m; sourceTree = ""; }; - 24EA9FA61C9A3D420036028E /* OCKCareCardWeekView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKCareCardWeekView.h; sourceTree = ""; }; - 24EA9FA71C9A3D420036028E /* OCKCareCardWeekView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKCareCardWeekView.m; sourceTree = ""; }; - 24EA9FA81C9A3D420036028E /* OCKHeartButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKHeartButton.h; sourceTree = ""; }; - 24EA9FA91C9A3D420036028E /* OCKHeartButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKHeartButton.m; sourceTree = ""; }; - 24EA9FAA1C9A3D420036028E /* OCKHeartView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKHeartView.h; sourceTree = ""; }; - 24EA9FAB1C9A3D420036028E /* OCKHeartView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKHeartView.m; sourceTree = ""; }; 24F8E4741C934FEE003B77BD /* OCKDefines_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKDefines_Private.h; sourceTree = ""; }; 24F8E47C1C93515A003B77BD /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/CareKit.strings; sourceTree = ""; }; 24F8E47E1C9351B9003B77BD /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/CareKit.strings; sourceTree = ""; }; @@ -263,9 +287,8 @@ 24F8E49C1C93521C003B77BD /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/CareKit.strings; sourceTree = ""; }; 24F8E49D1C93521F003B77BD /* ms */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ms; path = ms.lproj/CareKit.strings; sourceTree = ""; }; 24F8E49E1C935224003B77BD /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/CareKit.strings; sourceTree = ""; }; - 5A6F95071D625A590011A97A /* CareKitContactTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CareKitContactTests.m; sourceTree = ""; }; - 5A9D2C911D5E432B00D0F13B /* OCKContactInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKContactInfo.h; sourceTree = ""; }; - 5A9D2C921D5E432B00D0F13B /* OCKContactInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKContactInfo.m; sourceTree = ""; }; + 24FD43711E56843E004439EA /* OCKConnectHeaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKConnectHeaderView.h; sourceTree = ""; }; + 24FD43721E56843E004439EA /* OCKConnectHeaderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKConnectHeaderView.m; sourceTree = ""; }; 8605A5BA1C4F04EC00DD65FF /* CareKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CareKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8605A5BD1C4F04EC00DD65FF /* CareKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CareKit.h; sourceTree = ""; }; 8605A5BF1C4F04EC00DD65FF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -311,6 +334,50 @@ 86EA9A4C1C9C644E00B93BB1 /* OCKHTMLPDFWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKHTMLPDFWriter.h; sourceTree = ""; }; 86EA9A4D1C9C644E00B93BB1 /* OCKHTMLPDFWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKHTMLPDFWriter.m; sourceTree = ""; }; 86EA9A521C9C64C500B93BB1 /* CareKitPDFTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CareKitPDFTests.m; sourceTree = ""; }; + AEB37F661DF224DA002370A6 /* OCKColor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKColor.m; sourceTree = ""; }; + AEB37F681DF224ED002370A6 /* OCKColor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OCKColor.h; sourceTree = ""; }; + BA0CCA4B1DA320430003EB6C /* OCKHeaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKHeaderView.h; sourceTree = ""; }; + BA0CCA4C1DA320430003EB6C /* OCKHeaderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKHeaderView.m; sourceTree = ""; }; + BA155DC81EB1DFE400DB391F /* OCKPatient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OCKPatient.h; path = Patient/OCKPatient.h; sourceTree = ""; }; + BA155DC91EB1DFE400DB391F /* OCKPatient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OCKPatient.m; path = Patient/OCKPatient.m; sourceTree = ""; }; + BA274F381EB6F63400DD17EF /* OCKConnectMessagesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKConnectMessagesViewController.h; sourceTree = ""; }; + BA274F391EB6F63400DD17EF /* OCKConnectMessagesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKConnectMessagesViewController.m; sourceTree = ""; }; + BA274F3C1EB6F75300DD17EF /* OCKConnectMessageItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKConnectMessageItem.h; sourceTree = ""; }; + BA274F3D1EB6F75300DD17EF /* OCKConnectMessageItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKConnectMessageItem.m; sourceTree = ""; }; + BA274F3E1EB6F75300DD17EF /* OCKConnectMessageTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKConnectMessageTableViewCell.h; sourceTree = ""; }; + BA274F3F1EB6F75300DD17EF /* OCKConnectMessageTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKConnectMessageTableViewCell.m; sourceTree = ""; }; + BA274F451EB703DC00DD17EF /* OCKTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKTextView.h; sourceTree = ""; }; + BA274F461EB703DC00DD17EF /* OCKTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKTextView.m; sourceTree = ""; }; + BA3793D51EB66BC50004A540 /* OCKPatientWidget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OCKPatientWidget.h; path = Patient/OCKPatientWidget.h; sourceTree = ""; }; + BA3793D61EB66BC50004A540 /* OCKPatientWidget.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OCKPatientWidget.m; path = Patient/OCKPatientWidget.m; sourceTree = ""; }; + BA3793D71EB66BC50004A540 /* OCKPatientWidgetView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OCKPatientWidgetView.h; path = Patient/OCKPatientWidgetView.h; sourceTree = ""; }; + BA3793DC1EB66D7D0004A540 /* OCKBadgePatientWidgetView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OCKBadgePatientWidgetView.h; path = Patient/OCKBadgePatientWidgetView.h; sourceTree = ""; }; + BA3793DD1EB66D7D0004A540 /* OCKBadgePatientWidgetView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OCKBadgePatientWidgetView.m; path = Patient/OCKBadgePatientWidgetView.m; sourceTree = ""; }; + BA3793DE1EB66D7D0004A540 /* OCKDefaultPatientWidgetView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OCKDefaultPatientWidgetView.h; path = Patient/OCKDefaultPatientWidgetView.h; sourceTree = ""; }; + BA3793DF1EB66D7D0004A540 /* OCKDefaultPatientWidgetView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OCKDefaultPatientWidgetView.m; path = Patient/OCKDefaultPatientWidgetView.m; sourceTree = ""; }; + BA3793E01EB66D7D0004A540 /* OCKImagePatientWidgetView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OCKImagePatientWidgetView.h; path = Patient/OCKImagePatientWidgetView.h; sourceTree = ""; }; + BA3793E11EB66D7D0004A540 /* OCKImagePatientWidgetView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OCKImagePatientWidgetView.m; path = Patient/OCKImagePatientWidgetView.m; sourceTree = ""; }; + BA3793E21EB66D7D0004A540 /* OCKPatientWidget_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OCKPatientWidget_Internal.h; path = Patient/OCKPatientWidget_Internal.h; sourceTree = ""; }; + BA3793E31EB66D7D0004A540 /* OCKPatientWidgetView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OCKPatientWidgetView.m; path = Patient/OCKPatientWidgetView.m; sourceTree = ""; }; + BA3793E41EB66D7D0004A540 /* OCKStackedPatientWidgetView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OCKStackedPatientWidgetView.h; path = Patient/OCKStackedPatientWidgetView.h; sourceTree = ""; }; + BA3793E51EB66D7D0004A540 /* OCKStackedPatientWidgetView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OCKStackedPatientWidgetView.m; path = Patient/OCKStackedPatientWidgetView.m; sourceTree = ""; }; + BA38457C1D9CA698007990DA /* OCKGlyph.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKGlyph.h; sourceTree = ""; }; + BA38457D1D9CA698007990DA /* OCKGlyph.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKGlyph.m; sourceTree = ""; }; + BA660FF71EB1FF1100B7B1A7 /* OCKPatientHeaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OCKPatientHeaderView.h; path = Patient/OCKPatientHeaderView.h; sourceTree = ""; }; + BA660FF81EB1FF1100B7B1A7 /* OCKPatientHeaderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OCKPatientHeaderView.m; path = Patient/OCKPatientHeaderView.m; sourceTree = ""; }; + BA660FFC1EB20DAB00B7B1A7 /* OCKCareContentsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OCKCareContentsViewController.h; path = CareContents/OCKCareContentsViewController.h; sourceTree = ""; }; + BA660FFD1EB20DAB00B7B1A7 /* OCKCareContentsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OCKCareContentsViewController.m; path = CareContents/OCKCareContentsViewController.m; sourceTree = ""; }; + BA660FFF1EB20DAB00B7B1A7 /* OCKReadOnlyTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OCKReadOnlyTableViewCell.m; path = CareContents/OCKReadOnlyTableViewCell.m; sourceTree = ""; }; + BA8A8C9E1D9DAC680078C5EA /* OCKWeekView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OCKWeekView.h; path = ../SymptomTracker/OCKWeekView.h; sourceTree = ""; }; + BA8A8C9F1D9DAC680078C5EA /* OCKWeekView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OCKWeekView.m; path = ../SymptomTracker/OCKWeekView.m; sourceTree = ""; }; + BA8F07011EBE9D6400A14E5F /* OCKReadOnlyTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = OCKReadOnlyTableViewCell.h; path = CareContents/OCKReadOnlyTableViewCell.h; sourceTree = ""; }; + BA94DBB41D9CA9AE002E0BFC /* OCKGlyph_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKGlyph_Internal.h; sourceTree = ""; }; + BAD328601DB01D650021A773 /* OCKContactInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKContactInfo.h; sourceTree = ""; }; + BAD328611DB01D650021A773 /* OCKContactInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKContactInfo.m; sourceTree = ""; }; + BAD328641DB024040021A773 /* OCKRingItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKRingItem.h; sourceTree = ""; }; + BAD328651DB024040021A773 /* OCKRingItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKRingItem.m; sourceTree = ""; }; + BAD328681DB028AF0021A773 /* OCKInsightsRingTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCKInsightsRingTableViewCell.h; sourceTree = ""; }; + BAD328691DB028AF0021A773 /* OCKInsightsRingTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCKInsightsRingTableViewCell.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -332,6 +399,15 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 186D42B31D78D87C0083EF5B /* Thresholds */ = { + isa = PBXGroup; + children = ( + 186D42B61D78D8D60083EF5B /* OCKCarePlanThreshold.h */, + 186D42B41D78D8B60083EF5B /* OCKCarePlanThreshold.m */, + ); + name = Thresholds; + sourceTree = ""; + }; 241707A61C9A3AB7005D7123 /* Insights */ = { isa = PBXGroup; children = ( @@ -360,6 +436,8 @@ 241707B61C9A3AB7005D7123 /* OCKInsightItem.m */, 241707C11C9A3AB7005D7123 /* OCKMessageItem.h */, 241707C21C9A3AB7005D7123 /* OCKMessageItem.m */, + BAD328641DB024040021A773 /* OCKRingItem.h */, + BAD328651DB024040021A773 /* OCKRingItem.m */, 241707AD1C9A3AB7005D7123 /* OCKChart.h */, 241707AE1C9A3AB7005D7123 /* OCKChart.m */, 241707E11C9A3B17005D7123 /* Charts */, @@ -395,6 +473,8 @@ 241707BA1C9A3AB7005D7123 /* OCKInsightsMessageTableViewCell.m */, 241707B71C9A3AB7005D7123 /* OCKInsightsChartTableViewCell.h */, 241707B81C9A3AB7005D7123 /* OCKInsightsChartTableViewCell.m */, + BAD328681DB028AF0021A773 /* OCKInsightsRingTableViewCell.h */, + BAD328691DB028AF0021A773 /* OCKInsightsRingTableViewCell.m */, ); name = "Table View Cells"; sourceTree = ""; @@ -404,8 +484,6 @@ children = ( 242DBA881C9F7F8700529386 /* OCKSymptomTrackerViewController.h */, 242DBA891C9F7F8700529386 /* OCKSymptomTrackerViewController.m */, - 242DBAA01C9F7FF600529386 /* Week View */, - 242DBA9F1C9F7FE100529386 /* Header View */, 242DBA9E1C9F7FBF00529386 /* Table View Cell */, ); path = SymptomTracker; @@ -420,37 +498,17 @@ name = "Table View Cell"; sourceTree = ""; }; - 242DBA9F1C9F7FE100529386 /* Header View */ = { - isa = PBXGroup; - children = ( - 242DBA861C9F7F8700529386 /* OCKSymptomTrackerTableViewHeader.h */, - 242DBA871C9F7F8700529386 /* OCKSymptomTrackerTableViewHeader.m */, - 242DBA8E1C9F7F8700529386 /* OCKRingView.h */, - 242DBA8F1C9F7F8700529386 /* OCKRingView.m */, - ); - name = "Header View"; - sourceTree = ""; - }; - 242DBAA01C9F7FF600529386 /* Week View */ = { - isa = PBXGroup; - children = ( - 242DBA8A1C9F7F8700529386 /* OCKSymptomTrackerWeekView.h */, - 242DBA8B1C9F7F8700529386 /* OCKSymptomTrackerWeekView.m */, - 242DBA8C1C9F7F8700529386 /* OCKRingButton.h */, - 242DBA8D1C9F7F8700529386 /* OCKRingButton.m */, - ); - name = "Week View"; - sourceTree = ""; - }; 244A923E1CA210F7007B42D6 /* Common */ = { isa = PBXGroup; children = ( + BA6610041EB20DC300B7B1A7 /* Read Only Table View Cell */, + AEB37F651DF224C3002370A6 /* Color */, + BA6FE2301DA4715F005D5405 /* RingView */, + BA0CCA4A1DA3202B0003EB6C /* HeaderView */, + BABF92921D9D9411006D5D65 /* Glyph */, + BABF92911D9D9408006D5D65 /* WeekView */, 244A923F1CA210F7007B42D6 /* OCKTableViewCell.h */, 244A92401CA210F7007B42D6 /* OCKTableViewCell.m */, - 244A92411CA210F7007B42D6 /* OCKWeekLabelsView.h */, - 244A92421CA210F7007B42D6 /* OCKWeekLabelsView.m */, - 244A92431CA210F7007B42D6 /* OCKWeekViewController.h */, - 244A92441CA210F7007B42D6 /* OCKWeekViewController.m */, 86E8038E1CB4812F00B5C021 /* OCKLabel.h */, 86E8038F1CB4812F00B5C021 /* OCKLabel.m */, ); @@ -464,8 +522,10 @@ 2489296D1C90E70100EBBE1F /* OCKConnectViewController.m */, 2489296F1C90E70100EBBE1F /* OCKContact.h */, 248929701C90E70100EBBE1F /* OCKContact.m */, - 5A9D2C911D5E432B00D0F13B /* OCKContactInfo.h */, - 5A9D2C921D5E432B00D0F13B /* OCKContactInfo.m */, + BAD328601DB01D650021A773 /* OCKContactInfo.h */, + BAD328611DB01D650021A773 /* OCKContactInfo.m */, + 24FD43711E56843E004439EA /* OCKConnectHeaderView.h */, + 24FD43721E56843E004439EA /* OCKConnectHeaderView.m */, 2489298F1C90E7E700EBBE1F /* Table View Cell */, 248929901C90E80400EBBE1F /* Detail View */, ); @@ -495,6 +555,13 @@ 248929901C90E80400EBBE1F /* Detail View */ = { isa = PBXGroup; children = ( + BA274F441EB703CD00DD17EF /* Text View */, + BA274F3C1EB6F75300DD17EF /* OCKConnectMessageItem.h */, + BA274F3D1EB6F75300DD17EF /* OCKConnectMessageItem.m */, + BA274F3E1EB6F75300DD17EF /* OCKConnectMessageTableViewCell.h */, + BA274F3F1EB6F75300DD17EF /* OCKConnectMessageTableViewCell.m */, + BA274F381EB6F63400DD17EF /* OCKConnectMessagesViewController.h */, + BA274F391EB6F63400DD17EF /* OCKConnectMessagesViewController.m */, 248929641C90E70100EBBE1F /* OCKConnectDetailViewController.h */, 248929651C90E70100EBBE1F /* OCKConnectDetailViewController.m */, 2489296A1C90E70100EBBE1F /* OCKConnectTableViewHeader.h */, @@ -512,8 +579,6 @@ children = ( 24EA9FA31C9A3D420036028E /* OCKCareCardViewController.h */, 24EA9FA41C9A3D420036028E /* OCKCareCardViewController.m */, - 24EA9FD41C9A3DEA0036028E /* Week View */, - 24EA9FD51C9A3DF00036028E /* Header View */, 24EA9FD31C9A3DDE0036028E /* Table View Cell */, 24EA9FD11C9A3DC60036028E /* Detail View */, ); @@ -543,28 +608,6 @@ name = "Table View Cell"; sourceTree = ""; }; - 24EA9FD41C9A3DEA0036028E /* Week View */ = { - isa = PBXGroup; - children = ( - 24EA9FA61C9A3D420036028E /* OCKCareCardWeekView.h */, - 24EA9FA71C9A3D420036028E /* OCKCareCardWeekView.m */, - 24EA9FA81C9A3D420036028E /* OCKHeartButton.h */, - 24EA9FA91C9A3D420036028E /* OCKHeartButton.m */, - ); - name = "Week View"; - sourceTree = ""; - }; - 24EA9FD51C9A3DF00036028E /* Header View */ = { - isa = PBXGroup; - children = ( - 24EA9FA11C9A3D420036028E /* OCKCareCardTableViewHeader.h */, - 24EA9FA21C9A3D420036028E /* OCKCareCardTableViewHeader.m */, - 24EA9FAA1C9A3D420036028E /* OCKHeartView.h */, - 24EA9FAB1C9A3D420036028E /* OCKHeartView.m */, - ); - name = "Header View"; - sourceTree = ""; - }; 24EA9FD61C9A3DFD0036028E /* Table View Cells */ = { isa = PBXGroup; children = ( @@ -609,7 +652,9 @@ children = ( 8605A5BD1C4F04EC00DD65FF /* CareKit.h */, 8677EE241C97771A00588CD6 /* CareKit_Private.h */, + BA155DC71EB1DF7E00DB391F /* Patient */, 8677EDF51C9775EB00588CD6 /* CarePlan */, + BA660FFB1EB20D9600B7B1A7 /* CareContents */, 24EA9F921C9A3D420036028E /* CareCard */, 242DBA811C9F7F8700529386 /* SymptomTracker */, 241707A61C9A3AB7005D7123 /* Insights */, @@ -629,7 +674,6 @@ children = ( 86EA9A521C9C64C500B93BB1 /* CareKitPDFTests.m */, 8605A5C91C4F04EC00DD65FF /* CareKitCarePlanTests.m */, - 5A6F95071D625A590011A97A /* CareKitContactTests.m */, 8605A5CB1C4F04EC00DD65FF /* Info.plist */, ); path = CareKitTests; @@ -647,6 +691,7 @@ 8677EDF81C9775EB00588CD6 /* NSDateComponents+CarePlanInternal.h */, 8677EE211C97762A00588CD6 /* Activities */, 8677EE221C97763E00588CD6 /* Events */, + 186D42B31D78D87C0083EF5B /* Thresholds */, ); path = CarePlan; sourceTree = ""; @@ -697,6 +742,126 @@ path = PDF; sourceTree = ""; }; + AEB37F651DF224C3002370A6 /* Color */ = { + isa = PBXGroup; + children = ( + AEB37F681DF224ED002370A6 /* OCKColor.h */, + AEB37F661DF224DA002370A6 /* OCKColor.m */, + ); + name = Color; + sourceTree = ""; + }; + BA0CCA4A1DA3202B0003EB6C /* HeaderView */ = { + isa = PBXGroup; + children = ( + BA0CCA4B1DA320430003EB6C /* OCKHeaderView.h */, + BA0CCA4C1DA320430003EB6C /* OCKHeaderView.m */, + ); + name = HeaderView; + sourceTree = ""; + }; + BA13B82C1EB380C000E84E2E /* Widget */ = { + isa = PBXGroup; + children = ( + BA3793DB1EB66C4C0004A540 /* Widget Views */, + BA3793D51EB66BC50004A540 /* OCKPatientWidget.h */, + BA3793D61EB66BC50004A540 /* OCKPatientWidget.m */, + BA3793E21EB66D7D0004A540 /* OCKPatientWidget_Internal.h */, + ); + name = Widget; + sourceTree = ""; + }; + BA155DC71EB1DF7E00DB391F /* Patient */ = { + isa = PBXGroup; + children = ( + BA13B82C1EB380C000E84E2E /* Widget */, + BA660FF71EB1FF1100B7B1A7 /* OCKPatientHeaderView.h */, + BA660FF81EB1FF1100B7B1A7 /* OCKPatientHeaderView.m */, + BA155DC81EB1DFE400DB391F /* OCKPatient.h */, + BA155DC91EB1DFE400DB391F /* OCKPatient.m */, + ); + name = Patient; + sourceTree = ""; + }; + BA274F441EB703CD00DD17EF /* Text View */ = { + isa = PBXGroup; + children = ( + BA274F451EB703DC00DD17EF /* OCKTextView.h */, + BA274F461EB703DC00DD17EF /* OCKTextView.m */, + ); + name = "Text View"; + sourceTree = ""; + }; + BA3793DB1EB66C4C0004A540 /* Widget Views */ = { + isa = PBXGroup; + children = ( + BA3793D71EB66BC50004A540 /* OCKPatientWidgetView.h */, + BA3793E31EB66D7D0004A540 /* OCKPatientWidgetView.m */, + BA3793DC1EB66D7D0004A540 /* OCKBadgePatientWidgetView.h */, + BA3793DD1EB66D7D0004A540 /* OCKBadgePatientWidgetView.m */, + BA3793DE1EB66D7D0004A540 /* OCKDefaultPatientWidgetView.h */, + BA3793DF1EB66D7D0004A540 /* OCKDefaultPatientWidgetView.m */, + BA3793E01EB66D7D0004A540 /* OCKImagePatientWidgetView.h */, + BA3793E11EB66D7D0004A540 /* OCKImagePatientWidgetView.m */, + BA3793E41EB66D7D0004A540 /* OCKStackedPatientWidgetView.h */, + BA3793E51EB66D7D0004A540 /* OCKStackedPatientWidgetView.m */, + ); + name = "Widget Views"; + sourceTree = ""; + }; + BA660FFB1EB20D9600B7B1A7 /* CareContents */ = { + isa = PBXGroup; + children = ( + BA660FFC1EB20DAB00B7B1A7 /* OCKCareContentsViewController.h */, + BA660FFD1EB20DAB00B7B1A7 /* OCKCareContentsViewController.m */, + ); + name = CareContents; + sourceTree = ""; + }; + BA6610041EB20DC300B7B1A7 /* Read Only Table View Cell */ = { + isa = PBXGroup; + children = ( + BA8F07011EBE9D6400A14E5F /* OCKReadOnlyTableViewCell.h */, + BA660FFF1EB20DAB00B7B1A7 /* OCKReadOnlyTableViewCell.m */, + ); + name = "Read Only Table View Cell"; + path = ..; + sourceTree = ""; + }; + BA6FE2301DA4715F005D5405 /* RingView */ = { + isa = PBXGroup; + children = ( + 242DBA8E1C9F7F8700529386 /* OCKRingView.h */, + 242DBA8F1C9F7F8700529386 /* OCKRingView.m */, + 242DBA8C1C9F7F8700529386 /* OCKRingButton.h */, + 242DBA8D1C9F7F8700529386 /* OCKRingButton.m */, + ); + name = RingView; + sourceTree = ""; + }; + BABF92911D9D9408006D5D65 /* WeekView */ = { + isa = PBXGroup; + children = ( + BA8A8C9E1D9DAC680078C5EA /* OCKWeekView.h */, + BA8A8C9F1D9DAC680078C5EA /* OCKWeekView.m */, + 244A92411CA210F7007B42D6 /* OCKWeekLabelsView.h */, + 244A92421CA210F7007B42D6 /* OCKWeekLabelsView.m */, + 244A92431CA210F7007B42D6 /* OCKWeekViewController.h */, + 244A92441CA210F7007B42D6 /* OCKWeekViewController.m */, + ); + name = WeekView; + sourceTree = ""; + }; + BABF92921D9D9411006D5D65 /* Glyph */ = { + isa = PBXGroup; + children = ( + BA38457C1D9CA698007990DA /* OCKGlyph.h */, + BA94DBB41D9CA9AE002E0BFC /* OCKGlyph_Internal.h */, + BA38457D1D9CA698007990DA /* OCKGlyph.m */, + ); + name = Glyph; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -706,36 +871,49 @@ files = ( 244A92491CA210F7007B42D6 /* OCKWeekViewController.h in Headers */, 24EA9FB61C9A3D420036028E /* OCKCareCardDetailHeaderView.h in Headers */, + BA660FF91EB1FF1100B7B1A7 /* OCKPatientHeaderView.h in Headers */, 248929821C90E70100EBBE1F /* OCKContactInfoTableViewCell.h in Headers */, + BAD3286A1DB028AF0021A773 /* OCKInsightsRingTableViewCell.h in Headers */, 24F8E4761C934FEE003B77BD /* OCKDefines_Private.h in Headers */, 241707CF1C9A3AB7005D7123 /* OCKGroupedBarChartView.h in Headers */, + AEB37F691DF22BEF002370A6 /* OCKColor.h in Headers */, + BA155DCA1EB1DFE400DB391F /* OCKPatient.h in Headers */, 24EA9FBC1C9A3D420036028E /* OCKCareCardTableViewCell.h in Headers */, - 24EA9FC01C9A3D420036028E /* OCKCareCardTableViewHeader.h in Headers */, 8677EE131C9775EB00588CD6 /* OCKCarePlanEvent_Internal.h in Headers */, - 24EA9FC51C9A3D420036028E /* OCKCareCardWeekView.h in Headers */, + BA274F3A1EB6F63400DD17EF /* OCKConnectMessagesViewController.h in Headers */, + BA3793DA1EB66BC50004A540 /* OCKPatientWidgetView.h in Headers */, + BA8F07021EBE9D8800A14E5F /* OCKReadOnlyTableViewCell.h in Headers */, 86E803901CB4812F00B5C021 /* OCKLabel.h in Headers */, + BA3793EA1EB66D7D0004A540 /* OCKImagePatientWidgetView.h in Headers */, + BA94DBB61D9CA9AE002E0BFC /* OCKGlyph_Internal.h in Headers */, 241707DB1C9A3AB7005D7123 /* OCKInsightsViewController.h in Headers */, 241707C51C9A3AB7005D7123 /* OCKBarSeries.h in Headers */, + BAD328661DB024040021A773 /* OCKRingItem.h in Headers */, 2489298C1C90E71700EBBE1F /* OCKDefines.h in Headers */, 244A92471CA210F7007B42D6 /* OCKWeekLabelsView.h in Headers */, - 242DBA981C9F7F8700529386 /* OCKSymptomTrackerWeekView.h in Headers */, + BA3793E61EB66D7D0004A540 /* OCKBadgePatientWidgetView.h in Headers */, + BA6610001EB20DAB00B7B1A7 /* OCKCareContentsViewController.h in Headers */, + BA38457E1D9CA698007990DA /* OCKGlyph.h in Headers */, + BA274F401EB6F75300DD17EF /* OCKConnectMessageItem.h in Headers */, 24EA9FB21C9A3D420036028E /* OCKCareCardAdditionalInfoTableViewCell.h in Headers */, 8677EE171C9775EB00588CD6 /* OCKCarePlanStore.h in Headers */, 241707DD1C9A3AB7005D7123 /* OCKMessageItem.h in Headers */, 241707D51C9A3AB7005D7123 /* OCKInsightsMessageTableViewCell.h in Headers */, 242DBA9C1C9F7F8700529386 /* OCKRingView.h in Headers */, - 5A9D2C931D5E432B00D0F13B /* OCKContactInfo.h in Headers */, 24EA9FB81C9A3D420036028E /* OCKCareCardDetailViewController.h in Headers */, 8677EE1A1C9775EB00588CD6 /* OCKCarePlanStore_Internal.h in Headers */, 2489297D1C90E70100EBBE1F /* OCKConnectViewController.h in Headers */, - 24EA9FC91C9A3D420036028E /* OCKHeartView.h in Headers */, + BA0CCA4D1DA320430003EB6C /* OCKHeaderView.h in Headers */, 248929841C90E70100EBBE1F /* OCKContactSharingTableViewCell.h in Headers */, + BA3793EE1EB66D7D0004A540 /* OCKStackedPatientWidgetView.h in Headers */, 24EA9FC21C9A3D420036028E /* OCKCareCardViewController.h in Headers */, 8677EE0E1C9775EB00588CD6 /* OCKCarePlanActivity.h in Headers */, 248929771C90E70100EBBE1F /* OCKConnectTableViewCell.h in Headers */, + BA3793EC1EB66D7D0004A540 /* OCKPatientWidget_Internal.h in Headers */, 248929751C90E70100EBBE1F /* OCKConnectDetailViewController.h in Headers */, + BA8A8CA01D9DAC680078C5EA /* OCKWeekView.h in Headers */, 242DBA901C9F7F8700529386 /* OCKSymptomTrackerTableViewCell.h in Headers */, - 24EA9FC71C9A3D420036028E /* OCKHeartButton.h in Headers */, + BA274F421EB6F75300DD17EF /* OCKConnectMessageTableViewCell.h in Headers */, 248929801C90E70100EBBE1F /* OCKContact.h in Headers */, 8677EE1E1C9775EB00588CD6 /* OCKCareSchedule_Private.h in Headers */, 244A92451CA210F7007B42D6 /* OCKTableViewCell.h in Headers */, @@ -743,15 +921,21 @@ 241707C91C9A3AB7005D7123 /* OCKChart.h in Headers */, 86EA9A4E1C9C644E00B93BB1 /* OCKDocument.h in Headers */, 8677EE111C9775EB00588CD6 /* OCKCarePlanEvent.h in Headers */, + BAD328621DB01D650021A773 /* OCKContactInfo.h in Headers */, 241707D91C9A3AB7005D7123 /* OCKInsightsTableViewHeaderView.h in Headers */, + 24FD43731E56843E004439EA /* OCKConnectHeaderView.h in Headers */, 8605A5BE1C4F04EC00DD65FF /* CareKit.h in Headers */, 241707C31C9A3AB7005D7123 /* OCKBarChart.h in Headers */, + 186D42B71D78D8D60083EF5B /* OCKCarePlanThreshold.h in Headers */, 86EA9A501C9C644E00B93BB1 /* OCKHTMLPDFWriter.h in Headers */, 8677EE1B1C9775EB00588CD6 /* OCKCareSchedule.h in Headers */, 8677EE1D1C9775EB00588CD6 /* OCKCareSchedule_Internal.h in Headers */, 8677EE101C9775EB00588CD6 /* OCKCarePlanActivity_Internal.h in Headers */, + BA274F471EB703DC00DD17EF /* OCKTextView.h in Headers */, 8677EE141C9775EB00588CD6 /* OCKCarePlanEventResult.h in Headers */, 2489297B1C90E70100EBBE1F /* OCKConnectTableViewHeader.h in Headers */, + BA3793D81EB66BC50004A540 /* OCKPatientWidget.h in Headers */, + BA3793E81EB66D7D0004A540 /* OCKDefaultPatientWidgetView.h in Headers */, 241707D11C9A3AB7005D7123 /* OCKInsightItem.h in Headers */, 24EA9FB41C9A3D420036028E /* OCKCareCardButton.h in Headers */, 242DBA961C9F7F8700529386 /* OCKSymptomTrackerViewController.h in Headers */, @@ -761,7 +945,6 @@ 241707D31C9A3AB7005D7123 /* OCKInsightsChartTableViewCell.h in Headers */, 8677EE251C97776100588CD6 /* CareKit_Private.h in Headers */, 242DBA9A1C9F7F8700529386 /* OCKRingButton.h in Headers */, - 242DBA941C9F7F8700529386 /* OCKSymptomTrackerTableViewHeader.h in Headers */, 2489298D1C90E71700EBBE1F /* OCKHelpers.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -926,48 +1109,64 @@ 248929781C90E70100EBBE1F /* OCKConnectTableViewCell.m in Sources */, 2489297C1C90E70100EBBE1F /* OCKConnectTableViewHeader.m in Sources */, 244A92461CA210F7007B42D6 /* OCKTableViewCell.m in Sources */, - 24EA9FC61C9A3D420036028E /* OCKCareCardWeekView.m in Sources */, + BA274F411EB6F75300DD17EF /* OCKConnectMessageItem.m in Sources */, 242DBA971C9F7F8700529386 /* OCKSymptomTrackerViewController.m in Sources */, + BA3793E91EB66D7D0004A540 /* OCKDefaultPatientWidgetView.m in Sources */, + BA274F3B1EB6F63400DD17EF /* OCKConnectMessagesViewController.m in Sources */, + BA3793E71EB66D7D0004A540 /* OCKBadgePatientWidgetView.m in Sources */, 8677EE0C1C9775EB00588CD6 /* NSDateComponents+CarePlan.m in Sources */, 86EA9A4F1C9C644E00B93BB1 /* OCKDocument.m in Sources */, + BA274F481EB703DC00DD17EF /* OCKTextView.m in Sources */, 244A924A1CA210F7007B42D6 /* OCKWeekViewController.m in Sources */, 241707CA1C9A3AB7005D7123 /* OCKChart.m in Sources */, - 24EA9FC81C9A3D420036028E /* OCKHeartButton.m in Sources */, + BA38457F1D9CA698007990DA /* OCKGlyph.m in Sources */, + 24FD43741E56843E004439EA /* OCKConnectHeaderView.m in Sources */, 8677EE191C9775EB00588CD6 /* OCKCarePlanStore.xcdatamodeld in Sources */, 241707D01C9A3AB7005D7123 /* OCKGroupedBarChartView.m in Sources */, + BA8A8CA11D9DAC680078C5EA /* OCKWeekView.m in Sources */, 241707C41C9A3AB7005D7123 /* OCKBarChart.m in Sources */, + BA660FFA1EB1FF1100B7B1A7 /* OCKPatientHeaderView.m in Sources */, 8677EE0F1C9775EB00588CD6 /* OCKCarePlanActivity.m in Sources */, 241707D21C9A3AB7005D7123 /* OCKInsightItem.m in Sources */, 24EA9FB51C9A3D420036028E /* OCKCareCardButton.m in Sources */, 86EA9A511C9C644E00B93BB1 /* OCKHTMLPDFWriter.m in Sources */, + BAD3286B1DB028AF0021A773 /* OCKInsightsRingTableViewCell.m in Sources */, 86E803911CB4812F00B5C021 /* OCKLabel.m in Sources */, 241707DC1C9A3AB7005D7123 /* OCKInsightsViewController.m in Sources */, 241707C61C9A3AB7005D7123 /* OCKBarSeries.m in Sources */, - 24EA9FCA1C9A3D420036028E /* OCKHeartView.m in Sources */, + BA6610031EB20DAB00B7B1A7 /* OCKReadOnlyTableViewCell.m in Sources */, 248929851C90E70100EBBE1F /* OCKContactSharingTableViewCell.m in Sources */, + BA6610011EB20DAB00B7B1A7 /* OCKCareContentsViewController.m in Sources */, + 24CE2BA91E023BA30088CAF0 /* OCKCareCardDetailViewController.m in Sources */, + BA274F431EB6F75300DD17EF /* OCKConnectMessageTableViewCell.m in Sources */, 8677EE121C9775EB00588CD6 /* OCKCarePlanEvent.m in Sources */, - 242DBA991C9F7F8700529386 /* OCKSymptomTrackerWeekView.m in Sources */, 241707D41C9A3AB7005D7123 /* OCKInsightsChartTableViewCell.m in Sources */, 24EA9FB71C9A3D420036028E /* OCKCareCardDetailHeaderView.m in Sources */, - 5A9D2C941D5E432B00D0F13B /* OCKContactInfo.m in Sources */, 248929831C90E70100EBBE1F /* OCKContactInfoTableViewCell.m in Sources */, + BA3793EB1EB66D7D0004A540 /* OCKImagePatientWidgetView.m in Sources */, 241707D61C9A3AB7005D7123 /* OCKInsightsMessageTableViewCell.m in Sources */, - 242DBA951C9F7F8700529386 /* OCKSymptomTrackerTableViewHeader.m in Sources */, + BA0CCA4E1DA320430003EB6C /* OCKHeaderView.m in Sources */, 241707DA1C9A3AB7005D7123 /* OCKInsightsTableViewHeaderView.m in Sources */, + BA3793EF1EB66D7D0004A540 /* OCKStackedPatientWidgetView.m in Sources */, 248929761C90E70100EBBE1F /* OCKConnectDetailViewController.m in Sources */, 24EA9FBB1C9A3D420036028E /* OCKCareCardInstructionsTableViewCell.m in Sources */, 248929811C90E70100EBBE1F /* OCKContact.m in Sources */, 241707DE1C9A3AB7005D7123 /* OCKMessageItem.m in Sources */, + AEB37F671DF224DA002370A6 /* OCKColor.m in Sources */, + BA155DCB1EB1DFE400DB391F /* OCKPatient.m in Sources */, + BA3793ED1EB66D7D0004A540 /* OCKPatientWidgetView.m in Sources */, + BAD328671DB024040021A773 /* OCKRingItem.m in Sources */, 24EA9FBD1C9A3D420036028E /* OCKCareCardTableViewCell.m in Sources */, 244A92481CA210F7007B42D6 /* OCKWeekLabelsView.m in Sources */, + 186D42B51D78D8B60083EF5B /* OCKCarePlanThreshold.m in Sources */, 8677EE151C9775EB00588CD6 /* OCKCarePlanEventResult.m in Sources */, 2489297E1C90E70100EBBE1F /* OCKConnectViewController.m in Sources */, 242DBA9D1C9F7F8700529386 /* OCKRingView.m in Sources */, 24EA9FB31C9A3D420036028E /* OCKCareCardAdditionalInfoTableViewCell.m in Sources */, 8677EE1C1C9775EB00588CD6 /* OCKCareSchedule.m in Sources */, - 24EA9FC11C9A3D420036028E /* OCKCareCardTableViewHeader.m in Sources */, + BA3793D91EB66BC50004A540 /* OCKPatientWidget.m in Sources */, 242DBA9B1C9F7F8700529386 /* OCKRingButton.m in Sources */, - 24EA9FB91C9A3D420036028E /* OCKCareCardDetailViewController.m in Sources */, + BAD328631DB01D650021A773 /* OCKContactInfo.m in Sources */, 2489298E1C90E71700EBBE1F /* OCKHelpers.m in Sources */, 24EA9FC31C9A3D420036028E /* OCKCareCardViewController.m in Sources */, 242DBA911C9F7F8700529386 /* OCKSymptomTrackerTableViewCell.m in Sources */, @@ -979,7 +1178,6 @@ buildActionMask = 2147483647; files = ( 86EA9A531C9C64C500B93BB1 /* CareKitPDFTests.m in Sources */, - 5A6F95081D625A590011A97A /* CareKitContactTests.m in Sources */, 8605A5CA1C4F04EC00DD65FF /* CareKitCarePlanTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1097,6 +1295,7 @@ TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 2.1; }; name = Debug; }; @@ -1147,6 +1346,7 @@ 8605A5CF1C4F04EC00DD65FF /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -1159,6 +1359,7 @@ PRODUCT_BUNDLE_IDENTIFIER = org.carekit.CareKit; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 3.0; }; name = Debug; @@ -1166,6 +1367,7 @@ 8605A5D01C4F04EC00DD65FF /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -1268,9 +1470,10 @@ 8677EE041C9775EB00588CD6 /* OCKCarePlanStore.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + 185BC3A31D81DF0F001AB0A9 /* OCKCarePlanStore v2.xcdatamodel */, 8677EE051C9775EB00588CD6 /* OCKCarePlanStore.xcdatamodel */, ); - currentVersion = 8677EE051C9775EB00588CD6 /* OCKCarePlanStore.xcdatamodel */; + currentVersion = 185BC3A31D81DF0F001AB0A9 /* OCKCarePlanStore v2.xcdatamodel */; path = OCKCarePlanStore.xcdatamodeld; sourceTree = ""; versionGroupType = wrapper.xcdatamodel; diff --git a/CareKit/Assets.xcassets/Glyphs/Accessibility.imageset/Accessibility@2x.png b/CareKit/Assets.xcassets/Glyphs/Accessibility.imageset/Accessibility@2x.png new file mode 100644 index 000000000..00651815f Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Accessibility.imageset/Accessibility@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Accessibility.imageset/Accessibility@3x.png b/CareKit/Assets.xcassets/Glyphs/Accessibility.imageset/Accessibility@3x.png new file mode 100644 index 000000000..8fd22a48f Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Accessibility.imageset/Accessibility@3x.png differ diff --git a/CareKit/Assets.xcassets/heart.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/Accessibility.imageset/Contents.json similarity index 67% rename from CareKit/Assets.xcassets/heart.imageset/Contents.json rename to CareKit/Assets.xcassets/Glyphs/Accessibility.imageset/Contents.json index b94d262ff..499e0e361 100644 --- a/CareKit/Assets.xcassets/heart.imageset/Contents.json +++ b/CareKit/Assets.xcassets/Glyphs/Accessibility.imageset/Contents.json @@ -2,17 +2,16 @@ "images" : [ { "idiom" : "universal", - "filename" : "Heart-Big-Fill@1x.png", "scale" : "1x" }, { "idiom" : "universal", - "filename" : "Heart-Big-Fill@2x.png", + "filename" : "Accessibility@2x.png", "scale" : "2x" }, { "idiom" : "universal", - "filename" : "Heart-Big-Fill@3x.png", + "filename" : "Accessibility@3x.png", "scale" : "3x" } ], diff --git a/CareKit/Assets.xcassets/Glyphs/ActiveLife.imageset/ActiveLife@2x.png b/CareKit/Assets.xcassets/Glyphs/ActiveLife.imageset/ActiveLife@2x.png new file mode 100644 index 000000000..3699fb35f Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/ActiveLife.imageset/ActiveLife@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/ActiveLife.imageset/ActiveLife@3x.png.png b/CareKit/Assets.xcassets/Glyphs/ActiveLife.imageset/ActiveLife@3x.png.png new file mode 100644 index 000000000..8e0137aa4 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/ActiveLife.imageset/ActiveLife@3x.png.png differ diff --git a/CareKit/Assets.xcassets/heart-small.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/ActiveLife.imageset/Contents.json similarity index 67% rename from CareKit/Assets.xcassets/heart-small.imageset/Contents.json rename to CareKit/Assets.xcassets/Glyphs/ActiveLife.imageset/Contents.json index d87f853f0..aa4bf71b1 100644 --- a/CareKit/Assets.xcassets/heart-small.imageset/Contents.json +++ b/CareKit/Assets.xcassets/Glyphs/ActiveLife.imageset/Contents.json @@ -2,17 +2,16 @@ "images" : [ { "idiom" : "universal", - "filename" : "HeartSmallFill@1x.png", "scale" : "1x" }, { "idiom" : "universal", - "filename" : "HeartSmallFill@2x.png", + "filename" : "ActiveLife@2x.png", "scale" : "2x" }, { "idiom" : "universal", - "filename" : "HeartSmallFill@3x.png", + "filename" : "ActiveLife@3x.png.png", "scale" : "3x" } ], diff --git a/CareKit/Assets.xcassets/Glyphs/AdultLearning.imageset/Adult-Learning@2x.png b/CareKit/Assets.xcassets/Glyphs/AdultLearning.imageset/Adult-Learning@2x.png new file mode 100644 index 000000000..b29c82d48 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/AdultLearning.imageset/Adult-Learning@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/AdultLearning.imageset/Adult-Learning@3x.png b/CareKit/Assets.xcassets/Glyphs/AdultLearning.imageset/Adult-Learning@3x.png new file mode 100644 index 000000000..32e141878 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/AdultLearning.imageset/Adult-Learning@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/AdultLearning.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/AdultLearning.imageset/Contents.json new file mode 100644 index 000000000..793b1ab67 --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/AdultLearning.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Adult-Learning@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Adult-Learning@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/Awareness.imageset/Awareness@2x.png b/CareKit/Assets.xcassets/Glyphs/Awareness.imageset/Awareness@2x.png new file mode 100644 index 000000000..d67d6240c Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Awareness.imageset/Awareness@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Awareness.imageset/Awareness@3x.png b/CareKit/Assets.xcassets/Glyphs/Awareness.imageset/Awareness@3x.png new file mode 100644 index 000000000..4c2a11355 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Awareness.imageset/Awareness@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Awareness.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/Awareness.imageset/Contents.json new file mode 100644 index 000000000..0166f1370 --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/Awareness.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Awareness@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Awareness@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/Blood.imageset/Blood@2x.png b/CareKit/Assets.xcassets/Glyphs/Blood.imageset/Blood@2x.png new file mode 100644 index 000000000..bf7548134 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Blood.imageset/Blood@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Blood.imageset/Blood@3x.png b/CareKit/Assets.xcassets/Glyphs/Blood.imageset/Blood@3x.png new file mode 100644 index 000000000..f4d0bb86a Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Blood.imageset/Blood@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Blood.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/Blood.imageset/Contents.json new file mode 100644 index 000000000..7446e4e26 --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/Blood.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Blood@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Blood@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/BloodPressure.imageset/BloodPressure@2x.png b/CareKit/Assets.xcassets/Glyphs/BloodPressure.imageset/BloodPressure@2x.png new file mode 100644 index 000000000..6247efc8a Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/BloodPressure.imageset/BloodPressure@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/BloodPressure.imageset/BloodPressure@3x.png b/CareKit/Assets.xcassets/Glyphs/BloodPressure.imageset/BloodPressure@3x.png new file mode 100644 index 000000000..b3a0f460e Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/BloodPressure.imageset/BloodPressure@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/BloodPressure.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/BloodPressure.imageset/Contents.json new file mode 100644 index 000000000..7fb72dc0c --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/BloodPressure.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "BloodPressure@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "BloodPressure@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/Cardio.imageset/Cardio@2x.png b/CareKit/Assets.xcassets/Glyphs/Cardio.imageset/Cardio@2x.png new file mode 100644 index 000000000..ef6407cf9 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Cardio.imageset/Cardio@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Cardio.imageset/Cardio@3x.png b/CareKit/Assets.xcassets/Glyphs/Cardio.imageset/Cardio@3x.png new file mode 100644 index 000000000..6fad73389 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Cardio.imageset/Cardio@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Cardio.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/Cardio.imageset/Contents.json new file mode 100644 index 000000000..ed87151f4 --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/Cardio.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Cardio@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Cardio@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/ChildLearning.imageset/ChildLearning@2x.png b/CareKit/Assets.xcassets/Glyphs/ChildLearning.imageset/ChildLearning@2x.png new file mode 100644 index 000000000..305fe5299 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/ChildLearning.imageset/ChildLearning@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/ChildLearning.imageset/ChildLearning@3x.png b/CareKit/Assets.xcassets/Glyphs/ChildLearning.imageset/ChildLearning@3x.png new file mode 100644 index 000000000..b3453a45d Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/ChildLearning.imageset/ChildLearning@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/ChildLearning.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/ChildLearning.imageset/Contents.json new file mode 100644 index 000000000..7fa1f5a62 --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/ChildLearning.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ChildLearning@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ChildLearning@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/Contents.json b/CareKit/Assets.xcassets/Glyphs/Contents.json new file mode 100644 index 000000000..da4a164c9 --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/DentalHealth.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/DentalHealth.imageset/Contents.json new file mode 100644 index 000000000..fde954d90 --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/DentalHealth.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "DentalHealth@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "DentalHealth@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/DentalHealth.imageset/DentalHealth@2x.png b/CareKit/Assets.xcassets/Glyphs/DentalHealth.imageset/DentalHealth@2x.png new file mode 100644 index 000000000..96c412b36 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/DentalHealth.imageset/DentalHealth@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/DentalHealth.imageset/DentalHealth@3x.png b/CareKit/Assets.xcassets/Glyphs/DentalHealth.imageset/DentalHealth@3x.png new file mode 100644 index 000000000..54a2f9589 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/DentalHealth.imageset/DentalHealth@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/FemaleHealth.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/FemaleHealth.imageset/Contents.json new file mode 100644 index 000000000..08c5d5a30 --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/FemaleHealth.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "FemaleHealth@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "FemaleHealth@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/FemaleHealth.imageset/FemaleHealth@2x.png b/CareKit/Assets.xcassets/Glyphs/FemaleHealth.imageset/FemaleHealth@2x.png new file mode 100644 index 000000000..322683f10 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/FemaleHealth.imageset/FemaleHealth@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/FemaleHealth.imageset/FemaleHealth@3x.png b/CareKit/Assets.xcassets/Glyphs/FemaleHealth.imageset/FemaleHealth@3x.png new file mode 100644 index 000000000..a1561f656 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/FemaleHealth.imageset/FemaleHealth@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Hearing.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/Hearing.imageset/Contents.json new file mode 100644 index 000000000..0f52a933e --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/Hearing.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Hearing@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Hearing@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/Hearing.imageset/Hearing@2x.png b/CareKit/Assets.xcassets/Glyphs/Hearing.imageset/Hearing@2x.png new file mode 100644 index 000000000..202cdb96a Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Hearing.imageset/Hearing@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Hearing.imageset/Hearing@3x.png b/CareKit/Assets.xcassets/Glyphs/Hearing.imageset/Hearing@3x.png new file mode 100644 index 000000000..90d5c887e Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Hearing.imageset/Hearing@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Heart.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/Heart.imageset/Contents.json new file mode 100644 index 000000000..001610c99 --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/Heart.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "heart_inactive.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/Heart.imageset/heart_inactive.png b/CareKit/Assets.xcassets/Glyphs/Heart.imageset/heart_inactive.png new file mode 100644 index 000000000..ef6407cf9 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Heart.imageset/heart_inactive.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/HomeCare.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/HomeCare.imageset/Contents.json new file mode 100644 index 000000000..bcbd71ad7 --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/HomeCare.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "HomeCare@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "HomeCare@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/HomeCare.imageset/HomeCare@2x.png b/CareKit/Assets.xcassets/Glyphs/HomeCare.imageset/HomeCare@2x.png new file mode 100644 index 000000000..ff68298cb Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/HomeCare.imageset/HomeCare@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/HomeCare.imageset/HomeCare@3x.png b/CareKit/Assets.xcassets/Glyphs/HomeCare.imageset/HomeCare@3x.png new file mode 100644 index 000000000..b312d3f04 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/HomeCare.imageset/HomeCare@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/InfantCare.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/InfantCare.imageset/Contents.json new file mode 100644 index 000000000..067a4062e --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/InfantCare.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "InfantCare@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "InfantCare@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/InfantCare.imageset/InfantCare@2x.png b/CareKit/Assets.xcassets/Glyphs/InfantCare.imageset/InfantCare@2x.png new file mode 100644 index 000000000..c5b0ce50e Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/InfantCare.imageset/InfantCare@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/InfantCare.imageset/InfantCare@3x.png b/CareKit/Assets.xcassets/Glyphs/InfantCare.imageset/InfantCare@3x.png new file mode 100644 index 000000000..35c2b3bb3 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/InfantCare.imageset/InfantCare@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Laboratory.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/Laboratory.imageset/Contents.json new file mode 100644 index 000000000..d48a1d96f --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/Laboratory.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Laboratory@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Laboratory@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/Laboratory.imageset/Laboratory@2x.png b/CareKit/Assets.xcassets/Glyphs/Laboratory.imageset/Laboratory@2x.png new file mode 100644 index 000000000..b7cdb9999 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Laboratory.imageset/Laboratory@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Laboratory.imageset/Laboratory@3x.png b/CareKit/Assets.xcassets/Glyphs/Laboratory.imageset/Laboratory@3x.png new file mode 100644 index 000000000..11bfea5b8 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Laboratory.imageset/Laboratory@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/MaleHealth.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/MaleHealth.imageset/Contents.json new file mode 100644 index 000000000..02dfee2fb --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/MaleHealth.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "MaleHealth@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "MaleHealth@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/MaleHealth.imageset/MaleHealth@2x.png b/CareKit/Assets.xcassets/Glyphs/MaleHealth.imageset/MaleHealth@2x.png new file mode 100644 index 000000000..13c03d397 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/MaleHealth.imageset/MaleHealth@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/MaleHealth.imageset/MaleHealth@3x.png b/CareKit/Assets.xcassets/Glyphs/MaleHealth.imageset/MaleHealth@3x.png new file mode 100644 index 000000000..b17ef16bf Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/MaleHealth.imageset/MaleHealth@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/MaternalHealth.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/MaternalHealth.imageset/Contents.json new file mode 100644 index 000000000..9e5920c23 --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/MaternalHealth.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "MaternalHealth@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "MaternalHealth@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/MaternalHealth.imageset/MaternalHealth@2x.png b/CareKit/Assets.xcassets/Glyphs/MaternalHealth.imageset/MaternalHealth@2x.png new file mode 100644 index 000000000..da7cabb86 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/MaternalHealth.imageset/MaternalHealth@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/MaternalHealth.imageset/MaternalHealth@3x.png b/CareKit/Assets.xcassets/Glyphs/MaternalHealth.imageset/MaternalHealth@3x.png new file mode 100644 index 000000000..ad68dcb57 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/MaternalHealth.imageset/MaternalHealth@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Medication.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/Medication.imageset/Contents.json new file mode 100644 index 000000000..6ee10a283 --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/Medication.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Medication@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Medication@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/Medication.imageset/Medication@2x.png b/CareKit/Assets.xcassets/Glyphs/Medication.imageset/Medication@2x.png new file mode 100644 index 000000000..e6ede13bb Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Medication.imageset/Medication@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Medication.imageset/Medication@3x.png b/CareKit/Assets.xcassets/Glyphs/Medication.imageset/Medication@3x.png new file mode 100644 index 000000000..3eb10742b Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Medication.imageset/Medication@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/MentalHealth.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/MentalHealth.imageset/Contents.json new file mode 100644 index 000000000..46321779f --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/MentalHealth.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "MentalHealth@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "MentalHealth@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/MentalHealth.imageset/MentalHealth@2x.png b/CareKit/Assets.xcassets/Glyphs/MentalHealth.imageset/MentalHealth@2x.png new file mode 100644 index 000000000..fc6abc7f5 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/MentalHealth.imageset/MentalHealth@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/MentalHealth.imageset/MentalHealth@3x.png b/CareKit/Assets.xcassets/Glyphs/MentalHealth.imageset/MentalHealth@3x.png new file mode 100644 index 000000000..b8d08015c Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/MentalHealth.imageset/MentalHealth@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Neuro.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/Neuro.imageset/Contents.json new file mode 100644 index 000000000..f539053a7 --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/Neuro.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Neuro@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Neuro@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/Neuro.imageset/Neuro@2x.png b/CareKit/Assets.xcassets/Glyphs/Neuro.imageset/Neuro@2x.png new file mode 100644 index 000000000..0944f634c Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Neuro.imageset/Neuro@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Neuro.imageset/Neuro@3x.png b/CareKit/Assets.xcassets/Glyphs/Neuro.imageset/Neuro@3x.png new file mode 100644 index 000000000..1c673e275 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Neuro.imageset/Neuro@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Nutrition.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/Nutrition.imageset/Contents.json new file mode 100644 index 000000000..214bf7819 --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/Nutrition.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Nutrition@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Nutrition@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/Nutrition.imageset/Nutrition@2x.png b/CareKit/Assets.xcassets/Glyphs/Nutrition.imageset/Nutrition@2x.png new file mode 100644 index 000000000..cb1578eed Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Nutrition.imageset/Nutrition@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Nutrition.imageset/Nutrition@3x.png b/CareKit/Assets.xcassets/Glyphs/Nutrition.imageset/Nutrition@3x.png new file mode 100644 index 000000000..467a23f6f Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Nutrition.imageset/Nutrition@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Optometry.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/Optometry.imageset/Contents.json new file mode 100644 index 000000000..5a0acd4f2 --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/Optometry.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Optometry@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Optometry@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/Optometry.imageset/Optometry@2x.png b/CareKit/Assets.xcassets/Glyphs/Optometry.imageset/Optometry@2x.png new file mode 100644 index 000000000..b01c7ca3f Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Optometry.imageset/Optometry@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Optometry.imageset/Optometry@3x.png b/CareKit/Assets.xcassets/Glyphs/Optometry.imageset/Optometry@3x.png new file mode 100644 index 000000000..e419922ad Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Optometry.imageset/Optometry@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Pediatrics.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/Pediatrics.imageset/Contents.json new file mode 100644 index 000000000..5d0c7b08a --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/Pediatrics.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Pediatrics@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Pediatrics@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/Pediatrics.imageset/Pediatrics@2x.png b/CareKit/Assets.xcassets/Glyphs/Pediatrics.imageset/Pediatrics@2x.png new file mode 100644 index 000000000..ca1138ba1 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Pediatrics.imageset/Pediatrics@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Pediatrics.imageset/Pediatrics@3x.png b/CareKit/Assets.xcassets/Glyphs/Pediatrics.imageset/Pediatrics@3x.png new file mode 100644 index 000000000..3150d63c8 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Pediatrics.imageset/Pediatrics@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/PhysicalTherapy.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/PhysicalTherapy.imageset/Contents.json new file mode 100644 index 000000000..ed4f4096a --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/PhysicalTherapy.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "PhysicalTherapy@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "PhysicalTherapy@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/PhysicalTherapy.imageset/PhysicalTherapy@2x.png b/CareKit/Assets.xcassets/Glyphs/PhysicalTherapy.imageset/PhysicalTherapy@2x.png new file mode 100644 index 000000000..7169097f1 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/PhysicalTherapy.imageset/PhysicalTherapy@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/PhysicalTherapy.imageset/PhysicalTherapy@3x.png b/CareKit/Assets.xcassets/Glyphs/PhysicalTherapy.imageset/PhysicalTherapy@3x.png new file mode 100644 index 000000000..4fbaa9ea7 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/PhysicalTherapy.imageset/PhysicalTherapy@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Podiatry.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/Podiatry.imageset/Contents.json new file mode 100644 index 000000000..b8900f8ec --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/Podiatry.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Podiatry@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Podiatry@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/Podiatry.imageset/Podiatry@2x.png b/CareKit/Assets.xcassets/Glyphs/Podiatry.imageset/Podiatry@2x.png new file mode 100644 index 000000000..e1def8504 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Podiatry.imageset/Podiatry@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Podiatry.imageset/Podiatry@3x.png b/CareKit/Assets.xcassets/Glyphs/Podiatry.imageset/Podiatry@3x.png new file mode 100644 index 000000000..c28114c75 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Podiatry.imageset/Podiatry@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/RespiratoryHealth.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/RespiratoryHealth.imageset/Contents.json new file mode 100644 index 000000000..51e12f090 --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/RespiratoryHealth.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "RespiratoryHealth@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "RespiratoryHealth@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/RespiratoryHealth.imageset/RespiratoryHealth@2x.png b/CareKit/Assets.xcassets/Glyphs/RespiratoryHealth.imageset/RespiratoryHealth@2x.png new file mode 100644 index 000000000..922312509 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/RespiratoryHealth.imageset/RespiratoryHealth@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/RespiratoryHealth.imageset/RespiratoryHealth@3x.png b/CareKit/Assets.xcassets/Glyphs/RespiratoryHealth.imageset/RespiratoryHealth@3x.png new file mode 100644 index 000000000..6fd171789 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/RespiratoryHealth.imageset/RespiratoryHealth@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Scale.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/Scale.imageset/Contents.json new file mode 100644 index 000000000..ed9d0f609 --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/Scale.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Scale@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Scale@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/Scale.imageset/Scale@2x.png b/CareKit/Assets.xcassets/Glyphs/Scale.imageset/Scale@2x.png new file mode 100644 index 000000000..46ce69ad5 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Scale.imageset/Scale@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Scale.imageset/Scale@3x.png b/CareKit/Assets.xcassets/Glyphs/Scale.imageset/Scale@3x.png new file mode 100644 index 000000000..e0dfee07f Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Scale.imageset/Scale@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Stethoscope.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/Stethoscope.imageset/Contents.json new file mode 100644 index 000000000..5609fa5db --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/Stethoscope.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Stethoscope@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Stethoscope@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/Stethoscope.imageset/Stethoscope@2x.png b/CareKit/Assets.xcassets/Glyphs/Stethoscope.imageset/Stethoscope@2x.png new file mode 100644 index 000000000..105d2c051 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Stethoscope.imageset/Stethoscope@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Stethoscope.imageset/Stethoscope@3x.png b/CareKit/Assets.xcassets/Glyphs/Stethoscope.imageset/Stethoscope@3x.png new file mode 100644 index 000000000..71f1f219e Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Stethoscope.imageset/Stethoscope@3x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Syringe.imageset/Contents.json b/CareKit/Assets.xcassets/Glyphs/Syringe.imageset/Contents.json new file mode 100644 index 000000000..1d5b2576c --- /dev/null +++ b/CareKit/Assets.xcassets/Glyphs/Syringe.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Syringe@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Syringe@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Glyphs/Syringe.imageset/Syringe@2x.png b/CareKit/Assets.xcassets/Glyphs/Syringe.imageset/Syringe@2x.png new file mode 100644 index 000000000..98db9f6c5 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Syringe.imageset/Syringe@2x.png differ diff --git a/CareKit/Assets.xcassets/Glyphs/Syringe.imageset/Syringe@3x.png b/CareKit/Assets.xcassets/Glyphs/Syringe.imageset/Syringe@3x.png new file mode 100644 index 000000000..0547e5c80 Binary files /dev/null and b/CareKit/Assets.xcassets/Glyphs/Syringe.imageset/Syringe@3x.png differ diff --git a/CareKit/Assets.xcassets/Pixel.imageset/Contents.json b/CareKit/Assets.xcassets/Pixel.imageset/Contents.json new file mode 100644 index 000000000..3dc7b0dc4 --- /dev/null +++ b/CareKit/Assets.xcassets/Pixel.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Pixel.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Pixel.imageset/Pixel.png b/CareKit/Assets.xcassets/Pixel.imageset/Pixel.png new file mode 100644 index 000000000..ba85ffc7e Binary files /dev/null and b/CareKit/Assets.xcassets/Pixel.imageset/Pixel.png differ diff --git a/CareKit/Assets.xcassets/Stars/Contents.json b/CareKit/Assets.xcassets/Stars/Contents.json new file mode 100644 index 000000000..da4a164c9 --- /dev/null +++ b/CareKit/Assets.xcassets/Stars/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Stars/SmallStar.imageset/Contents.json b/CareKit/Assets.xcassets/Stars/SmallStar.imageset/Contents.json new file mode 100644 index 000000000..62cc05cc8 --- /dev/null +++ b/CareKit/Assets.xcassets/Stars/SmallStar.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "SmallStar@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "SmallStar@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/Stars/SmallStar.imageset/SmallStar@2x.png b/CareKit/Assets.xcassets/Stars/SmallStar.imageset/SmallStar@2x.png new file mode 100644 index 000000000..421f28612 Binary files /dev/null and b/CareKit/Assets.xcassets/Stars/SmallStar.imageset/SmallStar@2x.png differ diff --git a/CareKit/Assets.xcassets/Stars/SmallStar.imageset/SmallStar@3x.png b/CareKit/Assets.xcassets/Stars/SmallStar.imageset/SmallStar@3x.png new file mode 100644 index 000000000..510c7f6f6 Binary files /dev/null and b/CareKit/Assets.xcassets/Stars/SmallStar.imageset/SmallStar@3x.png differ diff --git a/CareKit/Assets.xcassets/TransparentPixel.imageset/Contents.json b/CareKit/Assets.xcassets/TransparentPixel.imageset/Contents.json new file mode 100644 index 000000000..4c4d55b6f --- /dev/null +++ b/CareKit/Assets.xcassets/TransparentPixel.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "TransparentPixel.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/TransparentPixel.imageset/TransparentPixel.png b/CareKit/Assets.xcassets/TransparentPixel.imageset/TransparentPixel.png new file mode 100644 index 000000000..d935eccca Binary files /dev/null and b/CareKit/Assets.xcassets/TransparentPixel.imageset/TransparentPixel.png differ diff --git a/CareKit/Assets.xcassets/checkmark.imageset/Contents.json b/CareKit/Assets.xcassets/checkmark.imageset/Contents.json new file mode 100644 index 000000000..9b8b5b1b2 --- /dev/null +++ b/CareKit/Assets.xcassets/checkmark.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "checkmark@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "checkmark@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/checkmark.imageset/checkmark@2x.png b/CareKit/Assets.xcassets/checkmark.imageset/checkmark@2x.png new file mode 100644 index 000000000..9e7a2e424 Binary files /dev/null and b/CareKit/Assets.xcassets/checkmark.imageset/checkmark@2x.png differ diff --git a/CareKit/Assets.xcassets/checkmark.imageset/checkmark@3x.png b/CareKit/Assets.xcassets/checkmark.imageset/checkmark@3x.png new file mode 100644 index 000000000..4f3ff8772 Binary files /dev/null and b/CareKit/Assets.xcassets/checkmark.imageset/checkmark@3x.png differ diff --git a/CareKit/Assets.xcassets/down-arrow.imageset/Arrow-Down-MonthView-iPad@1x.png b/CareKit/Assets.xcassets/down-arrow.imageset/Arrow-Down-MonthView-iPad@1x.png new file mode 100644 index 000000000..9798d625f Binary files /dev/null and b/CareKit/Assets.xcassets/down-arrow.imageset/Arrow-Down-MonthView-iPad@1x.png differ diff --git a/CareKit/Assets.xcassets/down-arrow.imageset/Arrow-Down-MonthView-iPad@2x.png b/CareKit/Assets.xcassets/down-arrow.imageset/Arrow-Down-MonthView-iPad@2x.png new file mode 100644 index 000000000..892f59d9c Binary files /dev/null and b/CareKit/Assets.xcassets/down-arrow.imageset/Arrow-Down-MonthView-iPad@2x.png differ diff --git a/CareKit/Assets.xcassets/down-arrow.imageset/Contents.json b/CareKit/Assets.xcassets/down-arrow.imageset/Contents.json new file mode 100644 index 000000000..f5832df28 --- /dev/null +++ b/CareKit/Assets.xcassets/down-arrow.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Arrow-Down-MonthView-iPad@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Arrow-Down-MonthView-iPad@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/heart-small.imageset/HeartSmallFill@1x.png b/CareKit/Assets.xcassets/heart-small.imageset/HeartSmallFill@1x.png deleted file mode 100644 index 5547b9181..000000000 Binary files a/CareKit/Assets.xcassets/heart-small.imageset/HeartSmallFill@1x.png and /dev/null differ diff --git a/CareKit/Assets.xcassets/heart-small.imageset/HeartSmallFill@2x.png b/CareKit/Assets.xcassets/heart-small.imageset/HeartSmallFill@2x.png deleted file mode 100644 index f1774da86..000000000 Binary files a/CareKit/Assets.xcassets/heart-small.imageset/HeartSmallFill@2x.png and /dev/null differ diff --git a/CareKit/Assets.xcassets/heart-small.imageset/HeartSmallFill@3x.png b/CareKit/Assets.xcassets/heart-small.imageset/HeartSmallFill@3x.png deleted file mode 100644 index 5b34ead6d..000000000 Binary files a/CareKit/Assets.xcassets/heart-small.imageset/HeartSmallFill@3x.png and /dev/null differ diff --git a/CareKit/Assets.xcassets/heart.imageset/Heart-Big-Fill@1x.png b/CareKit/Assets.xcassets/heart.imageset/Heart-Big-Fill@1x.png deleted file mode 100644 index b3b7d6234..000000000 Binary files a/CareKit/Assets.xcassets/heart.imageset/Heart-Big-Fill@1x.png and /dev/null differ diff --git a/CareKit/Assets.xcassets/heart.imageset/Heart-Big-Fill@2x.png b/CareKit/Assets.xcassets/heart.imageset/Heart-Big-Fill@2x.png deleted file mode 100644 index eeeaef218..000000000 Binary files a/CareKit/Assets.xcassets/heart.imageset/Heart-Big-Fill@2x.png and /dev/null differ diff --git a/CareKit/Assets.xcassets/heart.imageset/Heart-Big-Fill@3x.png b/CareKit/Assets.xcassets/heart.imageset/Heart-Big-Fill@3x.png deleted file mode 100644 index db1c32f5c..000000000 Binary files a/CareKit/Assets.xcassets/heart.imageset/Heart-Big-Fill@3x.png and /dev/null differ diff --git a/CareKit/Assets.xcassets/star.imageset/BigStar@2x.png b/CareKit/Assets.xcassets/star.imageset/BigStar@2x.png new file mode 100644 index 000000000..6b2d27d8e Binary files /dev/null and b/CareKit/Assets.xcassets/star.imageset/BigStar@2x.png differ diff --git a/CareKit/Assets.xcassets/star.imageset/BigStar@3x.png b/CareKit/Assets.xcassets/star.imageset/BigStar@3x.png new file mode 100644 index 000000000..da832a22b Binary files /dev/null and b/CareKit/Assets.xcassets/star.imageset/BigStar@3x.png differ diff --git a/CareKit/Assets.xcassets/star.imageset/Contents.json b/CareKit/Assets.xcassets/star.imageset/Contents.json new file mode 100644 index 000000000..05d50bcc1 --- /dev/null +++ b/CareKit/Assets.xcassets/star.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "BigStar@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "BigStar@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/Assets.xcassets/up-arrow.imageset/Arrow-Up-MonthView-iPad@1x.png b/CareKit/Assets.xcassets/up-arrow.imageset/Arrow-Up-MonthView-iPad@1x.png new file mode 100644 index 000000000..a670a74cb Binary files /dev/null and b/CareKit/Assets.xcassets/up-arrow.imageset/Arrow-Up-MonthView-iPad@1x.png differ diff --git a/CareKit/Assets.xcassets/up-arrow.imageset/Arrow-Up-MonthView-iPad@2x.png b/CareKit/Assets.xcassets/up-arrow.imageset/Arrow-Up-MonthView-iPad@2x.png new file mode 100644 index 000000000..59041de46 Binary files /dev/null and b/CareKit/Assets.xcassets/up-arrow.imageset/Arrow-Up-MonthView-iPad@2x.png differ diff --git a/CareKit/Assets.xcassets/up-arrow.imageset/Contents.json b/CareKit/Assets.xcassets/up-arrow.imageset/Contents.json new file mode 100644 index 000000000..491ae42f0 --- /dev/null +++ b/CareKit/Assets.xcassets/up-arrow.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Arrow-Up-MonthView-iPad@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Arrow-Up-MonthView-iPad@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKit/CareCard/OCKCareCardAdditionalInfoTableViewCell.m b/CareKit/CareCard/OCKCareCardAdditionalInfoTableViewCell.m index 9259fd1bd..190918c87 100644 --- a/CareKit/CareCard/OCKCareCardAdditionalInfoTableViewCell.m +++ b/CareKit/CareCard/OCKCareCardAdditionalInfoTableViewCell.m @@ -117,5 +117,4 @@ - (void)setUpConstraints { [NSLayoutConstraint activateConstraints:_constraints]; } - @end diff --git a/CareKit/CareCard/OCKCareCardButton.m b/CareKit/CareCard/OCKCareCardButton.m index 925919085..2cb986dc8 100644 --- a/CareKit/CareCard/OCKCareCardButton.m +++ b/CareKit/CareCard/OCKCareCardButton.m @@ -44,7 +44,7 @@ - (void)drawRect:(CGRect)rect { _circleLayer = [CAShapeLayer layer]; _circleLayer.strokeColor = self.tintColor.CGColor; _circleLayer.fillColor = [UIColor clearColor].CGColor; - [self updateFillColorForSelection:(self.isSelected || self.isHighlighted)]; + [self updateFillColorForSelection:self.isSelected]; _circleLayer.lineWidth = 2.5; _circleLayer.path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, ButtonSize, ButtonSize)].CGPath; _circleLayer.fillRule = kCAFillRuleNonZero; @@ -55,18 +55,27 @@ - (void)drawRect:(CGRect)rect { } } -- (void)setHighlighted:(BOOL)highlighted { - [self updateFillColorForSelection:highlighted]; - [super setHighlighted:highlighted]; -} - - (void)setSelected:(BOOL)selected { [self updateFillColorForSelection:selected]; [super setSelected:selected]; } - (void)updateFillColorForSelection:(BOOL)selection { - _circleLayer.fillColor = (selection) ? self.tintColor.CGColor : [UIColor whiteColor].CGColor; + if (selection) { + [_circleLayer addAnimation:[self animFillColorWithDur:0.15 startCol:[UIColor whiteColor] endColor:self.tintColor] forKey:@"animKey"]; + } else { + _circleLayer.fillColor = [UIColor whiteColor].CGColor; + } +} + +- (CABasicAnimation *)animFillColorWithDur:(CGFloat)dur startCol:(UIColor *)start endColor:(UIColor *)end { + CABasicAnimation *animFill = [CABasicAnimation animationWithKeyPath:@"fillColor"]; + [animFill setDuration:dur]; + [animFill setFromValue:(id)start.CGColor]; + [animFill setToValue:(id)end.CGColor]; + [animFill setRemovedOnCompletion:NO]; + [animFill setFillMode:kCAFillModeBoth]; + return animFill; } @end diff --git a/CareKit/CareCard/OCKCareCardDetailHeaderView.h b/CareKit/CareCard/OCKCareCardDetailHeaderView.h index f9a1b976a..d208cf13e 100644 --- a/CareKit/CareCard/OCKCareCardDetailHeaderView.h +++ b/CareKit/CareCard/OCKCareCardDetailHeaderView.h @@ -36,6 +36,4 @@ @property (nonatomic) OCKCarePlanActivity *intervention; -@property (nonatomic) BOOL showEdgeIndicator; - @end diff --git a/CareKit/CareCard/OCKCareCardDetailHeaderView.m b/CareKit/CareCard/OCKCareCardDetailHeaderView.m index ed18acaa4..3da2b08df 100644 --- a/CareKit/CareCard/OCKCareCardDetailHeaderView.m +++ b/CareKit/CareCard/OCKCareCardDetailHeaderView.m @@ -42,7 +42,6 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE @implementation OCKCareCardDetailHeaderView { OCKLabel *_titleLabel; OCKLabel *_textLabel; - UIView *_bottomEdge; NSMutableArray *_constraints; } @@ -55,7 +54,18 @@ - (instancetype)initWithFrame:(CGRect)frame { } - (void)prepareView { - self.backgroundColor = [UIColor whiteColor]; + if (!UIAccessibilityIsReduceTransparencyEnabled()) { + self.backgroundColor = [UIColor groupTableViewBackgroundColor]; + + UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleProminent]; + UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect]; + blurEffectView.frame = self.bounds; + blurEffectView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [self addSubview:blurEffectView]; + } + else { + self.backgroundColor = [UIColor whiteColor]; + } if (!_titleLabel) { _titleLabel = [OCKLabel new]; @@ -76,11 +86,6 @@ - (void)prepareView { [self addSubview:_textLabel]; } - if (!_bottomEdge) { - _bottomEdge = [UIView new]; - [self addSubview:_bottomEdge]; - } - [self updateView]; [self setUpConstraints]; } @@ -89,7 +94,6 @@ - (void)updateView { self.tintColor = _intervention.tintColor; _titleLabel.text = _intervention.title; _textLabel.text = _intervention.text; - _bottomEdge.backgroundColor = self.tintColor; } - (void)setUpConstraints { @@ -99,7 +103,6 @@ - (void)setUpConstraints { _titleLabel.translatesAutoresizingMaskIntoConstraints = NO; _textLabel.translatesAutoresizingMaskIntoConstraints = NO; - _bottomEdge.translatesAutoresizingMaskIntoConstraints = NO; [_constraints addObjectsFromArray:@[ [NSLayoutConstraint constraintWithItem:_titleLabel @@ -150,28 +153,7 @@ - (void)setUpConstraints { toItem:_textLabel attribute:NSLayoutAttributeBottom multiplier:1.0 - constant:BottomMargin], - [NSLayoutConstraint constraintWithItem:_bottomEdge - attribute:NSLayoutAttributeBottom - relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeBottom - multiplier:1.0 - constant:0.0], - [NSLayoutConstraint constraintWithItem:_bottomEdge - attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute - multiplier:1.0 - constant:3.0], - [NSLayoutConstraint constraintWithItem:_bottomEdge - attribute:NSLayoutAttributeWidth - relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeWidth - multiplier:1.0 - constant:0.0] + constant:BottomMargin] ]]; [NSLayoutConstraint activateConstraints:_constraints]; @@ -188,17 +170,22 @@ - (void)layoutSubviews { _titleLabel.preferredMaxLayoutWidth = _titleLabel.bounds.size.width; _textLabel.preferredMaxLayoutWidth = _textLabel.bounds.size.width; } - -- (void)setShowEdgeIndicator:(BOOL)showEdgeIndicator { - _showEdgeIndicator = showEdgeIndicator; - _bottomEdge.hidden = !_showEdgeIndicator; -} - (void)tintColorDidChange { [super tintColorDidChange]; [self updateView]; } +- (void)willMoveToWindow:(UIWindow *)newWindow { + [super willMoveToWindow:newWindow]; + + self.layer.shadowOffset = CGSizeMake(0, 1 / [UIScreen mainScreen].scale); + self.layer.shadowRadius = 0; + + self.layer.shadowColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:1].CGColor; + self.layer.shadowOpacity = 0.25; +} + #pragma mark - Accessibility diff --git a/CareKit/CareCard/OCKCareCardDetailViewController.h b/CareKit/CareCard/OCKCareCardDetailViewController.h index f430e58e4..bcc0a1922 100644 --- a/CareKit/CareCard/OCKCareCardDetailViewController.h +++ b/CareKit/CareCard/OCKCareCardDetailViewController.h @@ -34,14 +34,13 @@ NS_ASSUME_NONNULL_BEGIN -@interface OCKCareCardDetailViewController : UITableViewController +OCK_CLASS_AVAILABLE +@interface OCKCareCardDetailViewController : UIViewController - (instancetype)initWithIntervention:(OCKCarePlanActivity *)intervention; @property (nonatomic, readonly) OCKCarePlanActivity *intervention; -@property (nonatomic) BOOL showEdgeIndicator; - @end NS_ASSUME_NONNULL_END diff --git a/CareKit/CareCard/OCKCareCardDetailViewController.m b/CareKit/CareCard/OCKCareCardDetailViewController.m index be886c643..22b307bda 100644 --- a/CareKit/CareCard/OCKCareCardDetailViewController.m +++ b/CareKit/CareCard/OCKCareCardDetailViewController.m @@ -43,13 +43,14 @@ @implementation OCKCareCardDetailViewController { NSMutableArray *_sectionTitles; NSString *_instructionsSectionTitle; NSString *_additionalInfoSectionTitle; + UITableView *_tableView; + NSMutableArray *_constraints; } - (instancetype)initWithIntervention:(OCKCarePlanActivity *)intervention { - self = [super initWithStyle:UITableViewStyleGrouped]; + self = [super init]; if (self) { _intervention = intervention; - self.showEdgeIndicator = NO; } return self; } @@ -58,40 +59,93 @@ - (void)viewDidLoad { [super viewDidLoad]; [self prepareView]; - - self.tableView.estimatedRowHeight = 44.0; - self.tableView.rowHeight = UITableViewAutomaticDimension; - self.tableView.tableFooterView = [UIView new]; } - (void)prepareView { + self.view.backgroundColor = [UIColor groupTableViewBackgroundColor]; + if (!_headerView) { _headerView = [[OCKCareCardDetailHeaderView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, HeaderViewHeight)]; + [self.view addSubview:_headerView]; } - _headerView.showEdgeIndicator = _showEdgeIndicator; _headerView.intervention = _intervention; - self.tableView.tableHeaderView = _headerView; + if (!_tableView) { + _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped]; + _tableView.delegate = self; + _tableView.dataSource = self; + [self.view addSubview:_tableView]; + } + _tableView.estimatedRowHeight = 44.0; + _tableView.rowHeight = UITableViewAutomaticDimension; + _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; [self createTableViewDataArray]; + + [self setUpConstraints]; } -- (void)viewDidLayoutSubviews { - [super viewDidLayoutSubviews]; +- (void)setUpConstraints { + [NSLayoutConstraint deactivateConstraints:_constraints]; - CGFloat height = [_headerView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height; - CGRect headerViewFrame = _headerView.frame; + _constraints = [NSMutableArray new]; - if (height != headerViewFrame.size.height) { - headerViewFrame.size.height = height; - _headerView.frame = headerViewFrame; - self.tableView.tableHeaderView = _headerView; - } -} - -- (void)setShowEdgeIndicator:(BOOL)showEdgeIndicator { - _showEdgeIndicator = showEdgeIndicator; - _headerView.showEdgeIndicator = _showEdgeIndicator; + _headerView.translatesAutoresizingMaskIntoConstraints = NO; + _tableView.translatesAutoresizingMaskIntoConstraints = NO; + + [_constraints addObjectsFromArray:@[ + [NSLayoutConstraint constraintWithItem:_headerView + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.topLayoutGuide + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_headerView + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_headerView + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_headerView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:_tableView + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:-1.0], + [NSLayoutConstraint constraintWithItem:_tableView + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_tableView + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_tableView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:0.0] + ]]; + + [NSLayoutConstraint activateConstraints:_constraints]; } @@ -144,6 +198,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N reuseIdentifier:InstructionsCellIdentifier]; } cell.intervention = _intervention; + cell.layer.masksToBounds = YES; return cell; } else if ([sectionTitle isEqualToString:_additionalInfoSectionTitle]) { static NSString *AdditionalInfoCellIdentifier = @"AdditionalInfoCell"; @@ -153,6 +208,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N reuseIdentifier:AdditionalInfoCellIdentifier]; } cell.intervention = _intervention; + cell.layer.masksToBounds = YES; return cell; } return nil; diff --git a/CareKit/CareCard/OCKCareCardInstructionsTableViewCell.m b/CareKit/CareCard/OCKCareCardInstructionsTableViewCell.m index 96238d7a9..c6278b734 100644 --- a/CareKit/CareCard/OCKCareCardInstructionsTableViewCell.m +++ b/CareKit/CareCard/OCKCareCardInstructionsTableViewCell.m @@ -113,5 +113,4 @@ - (void)setUpConstraints { [NSLayoutConstraint activateConstraints:_constraints]; } - @end diff --git a/CareKit/CareCard/OCKCareCardTableViewCell.h b/CareKit/CareCard/OCKCareCardTableViewCell.h index 00e294e06..29a7cc9c6 100644 --- a/CareKit/CareCard/OCKCareCardTableViewCell.h +++ b/CareKit/CareCard/OCKCareCardTableViewCell.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -43,6 +43,10 @@ NS_ASSUME_NONNULL_BEGIN - (void)careCardTableViewCell:(OCKCareCardTableViewCell *)cell didUpdateFrequencyofInterventionEvent:(OCKCarePlanEvent *)event; +@optional + +- (void)careCardTableViewCell:(OCKCareCardTableViewCell *)cell didSelectInterventionActivity:(OCKCarePlanActivity *)activity; + @end diff --git a/CareKit/CareCard/OCKCareCardTableViewCell.m b/CareKit/CareCard/OCKCareCardTableViewCell.m index 583e5577e..60f53c1b7 100644 --- a/CareKit/CareCard/OCKCareCardTableViewCell.m +++ b/CareKit/CareCard/OCKCareCardTableViewCell.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -57,6 +57,7 @@ @implementation OCKCareCardTableViewCell { OCKLabel *_textLabel; UIView *_leadingEdge; NSArray *_frequencyButtons; + UIButton *_button; OCKCarePlanActivity *_intervention; NSMutableArray *_constraints; } @@ -110,6 +111,12 @@ - (void)prepareView { } _frequencyButtons = [buttons copy]; + if (!_button) { + _button = [UIButton new]; + [_button addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside]; + [self addSubview:_button]; + } + [self updateView]; [self setUpConstraints]; [self updateAccessibilityInfo]; @@ -127,6 +134,7 @@ - (void)setUpConstraints { _titleLabel.translatesAutoresizingMaskIntoConstraints = NO; _textLabel.translatesAutoresizingMaskIntoConstraints = NO; + _button.translatesAutoresizingMaskIntoConstraints = NO; [_titleLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal]; @@ -175,7 +183,35 @@ - (void)setUpConstraints { toItem:_textLabel attribute:NSLayoutAttributeTrailing multiplier:1.0 - constant:TrailingMargin] + constant:TrailingMargin], + [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:_button + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:_button + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:_button + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_button + attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:30.0 + TrailingMargin] ]]; for (int i = 0; i < _frequencyButtons.count; i++) { @@ -286,6 +322,13 @@ - (void)toggleFrequencyButton:(id)sender { } +- (void)buttonTapped:(id)sender { + if (self.delegate && + [self.delegate respondsToSelector:@selector(careCardTableViewCell:didSelectInterventionActivity:)]) { + [self.delegate careCardTableViewCell:self didSelectInterventionActivity:self.interventionEvents.firstObject.activity]; + } +} + #pragma mark - Accessibility diff --git a/CareKit/CareCard/OCKCareCardViewController.h b/CareKit/CareCard/OCKCareCardViewController.h index 06b1c81fb..ec0e5f522 100644 --- a/CareKit/CareCard/OCKCareCardViewController.h +++ b/CareKit/CareCard/OCKCareCardViewController.h @@ -1,6 +1,6 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. - Copyright (c) 2016, Erik Hornberger. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. + Copyright (c) 2017, Erik Hornberger. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -150,34 +150,18 @@ OCK_CLASS_AVAILABLE */ @property (nonatomic, readonly, nonnull) UITableView *tableView; -/** - The image that will be used to mask the fill shape in the header view. - - In order to provide a custom maskImage, you must have a regular size and small size. - For example, in the assets catalog, there are "heart" and a "heart-small" assets. - Both assets must be provided in order to properly render the interface. - - If no image is specified, the "heart" image will be loaded from the assets catalog. - */ -@property (nonatomic, null_resettable) UIImage *maskImage; - /** The image that will be used to mask the fill shape in the week view. In order to provide a custom maskImage, you must have a regular size and small size. For example, in the assets catalog, there are "heart" and a "heart-small" assets. Both assets must be provided in order to properly render the interface. - - If no image is specified, the "heart-small" image will be loaded from the assets catalog. - */ -@property (nonatomic, null_resettable) UIImage *smallMaskImage; -/** The tint color that will be used to fill the shape. If tint color is not specified, a default red color will be used. */ -@property (nonatomic, null_resettable) UIColor *maskImageTintColor; +@property (nonatomic, null_resettable) UIColor *glyphTintColor; /** The string that will be used as the Care Card header title. @@ -187,11 +171,15 @@ OCK_CLASS_AVAILABLE @property (nonatomic, null_resettable) NSString *headerTitle; /** - A boolean to show the edge indicators. - - The default value is NO. + The glyph type for the header view (see OCKGlyphType). + */ +@property (nonatomic) OCKGlyphType glyphType; + +/** + Image name string if using a custom image. Cannot access image name once image has been created + and we need a way to access that to send the custom image name string to the watch */ -@property (nonatomic) BOOL showEdgeIndicators; +@property (nonatomic, copy) NSString *customGlyphImageName; @end diff --git a/CareKit/CareCard/OCKCareCardViewController.m b/CareKit/CareCard/OCKCareCardViewController.m index a1adef1b0..6a1acc895 100644 --- a/CareKit/CareCard/OCKCareCardViewController.m +++ b/CareKit/CareCard/OCKCareCardViewController.m @@ -1,7 +1,7 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. - Copyright (c) 2016, Troy Tsubota. All rights reserved. - Copyright (c) 2016, Erik Hornberger. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. + Copyright (c) 2017, Troy Tsubota. All rights reserved. + Copyright (c) 2017, Erik Hornberger. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -30,24 +30,23 @@ 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 "OCKCareCardViewController.h" +#import "OCKWeekView.h" #import "OCKCareCardDetailViewController.h" #import "OCKWeekViewController.h" -#import "OCKCareCardWeekView.h" #import "NSDateComponents+CarePlanInternal.h" -#import "OCKCareCardTableViewHeader.h" +#import "OCKHeaderView.h" #import "OCKCareCardTableViewCell.h" -#import "OCKWeekViewController.h" -#import "OCKHeartView.h" #import "OCKWeekLabelsView.h" #import "OCKCarePlanStore_Internal.h" #import "OCKHelpers.h" #import "OCKDefines_Private.h" +#import "OCKGlyph_Internal.h" #define RedColor() OCKColorFromRGB(0xEF445B); + @interface OCKCareCardViewController() @property (nonatomic) NSDateComponents *selectedDate; @@ -58,11 +57,15 @@ @interface OCKCareCardViewController() *> *_events; NSMutableArray *_weekValues; - OCKCareCardTableViewHeader *_headerView; + OCKHeaderView *_headerView; UIPageViewController *_pageViewController; OCKWeekViewController *_weekViewController; NSCalendar *_calendar; NSMutableArray *_constraints; + NSMutableArray *_sectionTitles; + NSMutableArray *> *> *_tableViewData; + NSString *_otherString; + NSString *_optionalString; } - (instancetype)init { @@ -74,25 +77,27 @@ - (instancetype)initWithCarePlanStore:(OCKCarePlanStore *)store { self = [super init]; if (self) { _store = store; - self.maskImage = nil; - self.smallMaskImage = nil; - self.maskImageTintColor = nil; - _showEdgeIndicators = NO; _calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian]; + _glyphTintColor = nil; } return self; } - (void)viewDidLoad { [super viewDidLoad]; + _otherString = OCKLocalizedString(@"ACTIVITY_TYPE_OTHER_SECTION_HEADER", nil); + _optionalString = OCKLocalizedString(@"ACTIVITY_TYPE_OPTIONAL_SECTION_HEADER", nil); + self.view.backgroundColor = [UIColor groupTableViewBackgroundColor]; self.store.careCardUIDelegate = self; + [self setGlyphTintColor: _glyphTintColor]; + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:OCKLocalizedString(@"TODAY_BUTTON_TITLE", nil) style:UIBarButtonItemStylePlain target:self action:@selector(showToday:)]; - self.navigationItem.rightBarButtonItem.tintColor = self.maskImageTintColor; + self.navigationItem.rightBarButtonItem.tintColor = self.glyphTintColor; _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; _tableView.dataSource = self; @@ -105,12 +110,12 @@ - (void)viewDidLoad { _tableView.estimatedRowHeight = 90.0; _tableView.rowHeight = UITableViewAutomaticDimension; - _tableView.estimatedSectionHeaderHeight = 100.0; - _tableView.sectionHeaderHeight = UITableViewAutomaticDimension; + _tableView.tableFooterView = [UIView new]; + _tableView.estimatedSectionHeaderHeight = 0; + _tableView.estimatedSectionFooterHeight = 0; - if ([self respondsToSelector:@selector(registerForPreviewingWithDelegate:sourceView:)]) { - [self registerForPreviewingWithDelegate:self sourceView:_tableView]; - } + self.navigationController.navigationBar.translucent = NO; + [self.navigationController.navigationBar setBarTintColor:[UIColor colorWithRed:245.0/255.0 green:244.0/255.0 blue:246.0/255.0 alpha:1.0]]; } - (void)viewDidAppear:(BOOL)animated { @@ -118,7 +123,7 @@ - (void)viewDidAppear:(BOOL)animated { NSAssert(self.navigationController, @"OCKCareCardViewController must be embedded in a navigation controller."); - _weekViewController.careCardWeekView.delegate = self; + _weekViewController.weekView.delegate = self; } - (void)showToday:(id)sender { @@ -128,15 +133,21 @@ - (void)showToday:(id)sender { - (void)prepareView { if (!_headerView) { - _headerView = [[OCKCareCardTableViewHeader alloc] initWithFrame:CGRectZero]; + _headerView = [[OCKHeaderView alloc] initWithFrame:CGRectZero]; + [self.view addSubview:_headerView]; } if ([_headerTitle length] == 0) { _headerTitle = OCKLocalizedString(@"CARE_CARD_HEADER_TITLE", nil); } _headerView.title = _headerTitle; - _headerView.heartView.maskImage = self.maskImage; - _headerView.tintColor = self.maskImageTintColor; - + _headerView.tintColor = self.glyphTintColor; + if (self.glyphType == OCKGlyphTypeCustom) { + UIImage *glyphImage = [self createCustomImageName:self.customGlyphImageName]; + _headerView.glyphImage = glyphImage; + } + _headerView.isCareCard = YES; + _headerView.glyphType = self.glyphType; + if (!_pageViewController) { _pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal @@ -144,17 +155,31 @@ - (void)prepareView { _pageViewController.dataSource = self; _pageViewController.delegate = self; + if (!UIAccessibilityIsReduceTransparencyEnabled()) { + _pageViewController.view.backgroundColor = [UIColor groupTableViewBackgroundColor]; + + UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleProminent]; + UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect]; + blurEffectView.frame = _pageViewController.view.bounds; + blurEffectView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [_pageViewController.view insertSubview:blurEffectView atIndex:_pageViewController.view.subviews.count-1]; + } + else { + _pageViewController.view.backgroundColor = [UIColor whiteColor]; + } + OCKWeekViewController *weekController = [OCKWeekViewController new]; - weekController.careCardWeekView.delegate = _weekViewController.careCardWeekView.delegate; - weekController.careCardWeekView.smallMaskImage = self.smallMaskImage; - weekController.careCardWeekView.tintColor = self.maskImageTintColor; + weekController.weekView.delegate = _weekViewController.weekView.delegate; + weekController.weekView.ringTintColor = self.glyphTintColor; + weekController.weekView.isCareCard = YES; + weekController.weekView.glyphType = self.glyphType; _weekViewController = weekController; [_pageViewController setViewControllers:@[weekController] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil]; + [self.view addSubview:_pageViewController.view]; } - _tableView.tableHeaderView = _pageViewController.view; - _tableView.tableFooterView = [UIView new]; + _tableView.showsVerticalScrollIndicator = NO; [self setUpConstraints]; } @@ -166,12 +191,56 @@ - (void)setUpConstraints { _tableView.translatesAutoresizingMaskIntoConstraints = NO; _pageViewController.view.translatesAutoresizingMaskIntoConstraints = NO; + _headerView.translatesAutoresizingMaskIntoConstraints = NO; + [_constraints addObjectsFromArray:@[ - [NSLayoutConstraint constraintWithItem:_tableView + [NSLayoutConstraint constraintWithItem:_pageViewController.view attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.topLayoutGuide + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_pageViewController.view + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:_headerView + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:10.0], + [NSLayoutConstraint constraintWithItem:_pageViewController.view + attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_pageViewController.view + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_pageViewController.view + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:65.0], + [NSLayoutConstraint constraintWithItem:_headerView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:140.0], + [NSLayoutConstraint constraintWithItem:_headerView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:_tableView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0], @@ -196,59 +265,42 @@ - (void)setUpConstraints { attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0.0], - [NSLayoutConstraint constraintWithItem:_pageViewController.view - attribute:NSLayoutAttributeWidth + [NSLayoutConstraint constraintWithItem:_headerView + attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view - attribute:NSLayoutAttributeWidth + attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0.0], - [NSLayoutConstraint constraintWithItem:_pageViewController.view - attribute:NSLayoutAttributeHeight + [NSLayoutConstraint constraintWithItem:_headerView + attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute + toItem:self.view + attribute:NSLayoutAttributeTrailing multiplier:1.0 - constant:60.0] + constant:0.0] ]]; [NSLayoutConstraint activateConstraints:_constraints]; } - (void)setSelectedDate:(NSDateComponents *)selectedDate { - NSDateComponents *today = [self today]; - _selectedDate = [selectedDate isLaterThan:today] ? today : selectedDate; + _selectedDate = selectedDate; - _weekViewController.careCardWeekView.selectedIndex = self.selectedDate.weekday - 1; + _weekViewController.weekView.isToday = [[self today] isEqualToDate:selectedDate]; + _weekViewController.weekView.selectedIndex = self.selectedDate.weekday - 1; [self fetchEvents]; } -- (void)setMaskImage:(UIImage *)maskImage { - _maskImage = maskImage; - if (!_maskImage) { - _maskImage = [UIImage imageNamed:@"heart" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil]; +- (void)setGlyphTintColor:(UIColor *)glyphTintColor { + _glyphTintColor = glyphTintColor; + if (!_glyphTintColor) { + _glyphTintColor = [OCKGlyph defaultColorForGlyph:self.glyphType]; } - _headerView.heartView.maskImage = _maskImage; -} - -- (void)setSmallMaskImage:(UIImage *)smallMaskImage { - _smallMaskImage = smallMaskImage; - if (!_smallMaskImage) { - _smallMaskImage = [UIImage imageNamed:@"heart-small" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil]; - } - _weekViewController.careCardWeekView.smallMaskImage = _smallMaskImage; -} - -- (void)setMaskImageTintColor:(UIColor *)maskImageTintColor { - _maskImageTintColor = maskImageTintColor; - if (!_maskImageTintColor) { - _maskImageTintColor = RedColor(); - } - - _weekViewController.careCardWeekView.tintColor = _maskImageTintColor; - _headerView.tintColor = _maskImageTintColor; - self.navigationItem.rightBarButtonItem.tintColor = _maskImageTintColor; + _weekViewController.weekView.tintColor = _glyphTintColor; + _headerView.tintColor = _glyphTintColor; + self.navigationItem.rightBarButtonItem.tintColor = _glyphTintColor; } - (void)setHeaderTitle:(NSString *)headerTitle { @@ -260,18 +312,13 @@ - (void)setHeaderTitle:(NSString *)headerTitle { } } -- (void)setShowEdgeIndicators:(BOOL)showEdgeIndicators { - _showEdgeIndicators = showEdgeIndicators; - [_tableView reloadData]; -} - #pragma mark - Helpers - (void)fetchEvents { [self.store eventsOnDate:self.selectedDate type:OCKCarePlanActivityTypeIntervention - completion:^(NSArray *> * _Nonnull eventsGroupedByActivity, NSError * _Nonnull error) { + completion:^(NSArray *> *eventsGroupedByActivity, NSError *error) { NSAssert(!error, error.localizedDescription); dispatch_async(dispatch_get_main_queue(), ^{ _events = [NSMutableArray new]; @@ -284,6 +331,8 @@ - (void)fetchEvents { [self.delegate careCardViewController:self willDisplayEvents:[_events copy] dateComponents:_selectedDate]; } + [self createGroupedEventDictionaryForEvents:_events]; + [self updateHeaderView]; [self updateWeekView]; [_tableView reloadData]; @@ -295,23 +344,27 @@ - (void)updateHeaderView { _headerView.date = [NSDateFormatter localizedStringFromDate:[_calendar dateFromComponents:self.selectedDate] dateStyle:NSDateFormatterLongStyle timeStyle:NSDateFormatterNoStyle]; - NSInteger totalEvents = 0; - NSInteger completedEvents = 0; - for (NSArray *events in _events) { - totalEvents += events.count; - for (OCKCarePlanEvent *event in events) { - if (event.state == OCKCarePlanEventStateCompleted) { - completedEvents++; - } - } - } - - float value = (totalEvents > 0) ? (float)completedEvents/totalEvents : 1; - _headerView.value = value; - - NSInteger selectedIndex = _weekViewController.careCardWeekView.selectedIndex; - [_weekValues replaceObjectAtIndex:selectedIndex withObject:@(value)]; - _weekViewController.careCardWeekView.values = _weekValues; + NSMutableArray *values = [NSMutableArray new]; + + [self.store dailyCompletionStatusWithType:OCKCarePlanActivityTypeIntervention + startDate:self.selectedDate + endDate:self.selectedDate + handler:^(NSDateComponents *date, NSUInteger completedEvents, NSUInteger totalEvents) { + if (totalEvents == 0) { + [values addObject:@(1)]; + } else { + [values addObject:@((float)completedEvents/totalEvents)]; + } + } completion:^(BOOL completed, NSError *error) { + NSAssert(!error, error.localizedDescription); + dispatch_async(dispatch_get_main_queue(), ^{ + NSInteger selectedIndex = _weekViewController.weekView.selectedIndex; + [_weekValues replaceObjectAtIndex:selectedIndex withObject:values.firstObject]; + _weekViewController.weekView.values = _weekValues; + + _headerView.value = [values.firstObject doubleValue]; + }); + }]; } - (void)updateWeekView { @@ -329,18 +382,16 @@ - (void)updateWeekView { [self.store dailyCompletionStatusWithType:OCKCarePlanActivityTypeIntervention startDate:[NSDateComponents ock_componentsWithDate:startOfWeek calendar:_calendar] endDate:[NSDateComponents ock_componentsWithDate:endOfWeek calendar:_calendar] - handler:^(NSDateComponents * _Nonnull date, NSUInteger completedEvents, NSUInteger totalEvents) { - if ([date isLaterThan:[self today]]) { - [values addObject:@(0)]; - } else if (totalEvents == 0) { + handler:^(NSDateComponents *date, NSUInteger completedEvents, NSUInteger totalEvents) { + if (totalEvents == 0) { [values addObject:@(1)]; } else { [values addObject:@((float)completedEvents/totalEvents)]; } - } completion:^(BOOL completed, NSError * _Nullable error) { + } completion:^(BOOL completed, NSError *error) { NSAssert(!error, error.localizedDescription); dispatch_async(dispatch_get_main_queue(), ^{ - _weekViewController.careCardWeekView.values = values; + _weekViewController.weekView.values = values; _weekValues = [values mutableCopy]; }); }]; @@ -363,33 +414,95 @@ - (NSDateComponents *)today { - (UIViewController *)detailViewControllerForActivity:(OCKCarePlanActivity *)activity { OCKCareCardDetailViewController *detailViewController = [[OCKCareCardDetailViewController alloc] initWithIntervention:activity]; - detailViewController.showEdgeIndicator = self.showEdgeIndicators; return detailViewController; } - (OCKCarePlanActivity *)activityForIndexPath:(NSIndexPath *)indexPath { - return _events[indexPath.row].firstObject.activity; + return _tableViewData[indexPath.section][indexPath.row].firstObject.activity; } - (BOOL)delegateCustomizesRowSelection { return self.delegate && [self.delegate respondsToSelector:@selector(careCardViewController:didSelectRowWithInterventionActivity:)]; } +- (UIImage *)createCustomImageName:(NSString*)customImageName { + UIImage *customImageToReturn; + if (customImageName != nil) { + NSBundle *bundle = [NSBundle mainBundle]; + customImageToReturn = [UIImage imageNamed: customImageName inBundle:bundle compatibleWithTraitCollection:nil]; + } else { + OCKGlyphType defaultGlyph = OCKGlyphTypeHeart; + customImageToReturn = [[OCKGlyph glyphImageForType:defaultGlyph] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + } + + return customImageToReturn; +} + +- (void)createGroupedEventDictionaryForEvents:(NSArray *> *)events { + NSMutableDictionary *groupedEvents = [NSMutableDictionary new]; + + for (NSArray *activityEvents in events) { + OCKCarePlanEvent *firstEvent = activityEvents.firstObject; + NSString *groupIdentifier = firstEvent.activity.groupIdentifier ? firstEvent.activity.groupIdentifier : _otherString; + + if (firstEvent.activity.optional) { + groupIdentifier = _optionalString; + } + + if (groupedEvents[groupIdentifier]) { + NSMutableArray *objects = [groupedEvents[groupIdentifier] mutableCopy]; + [objects addObject:activityEvents]; + groupedEvents[groupIdentifier] = objects; + } else { + NSMutableArray *objects = [[NSMutableArray alloc] initWithArray:activityEvents]; + groupedEvents[groupIdentifier] = @[objects]; + } + } + + NSMutableArray *sortedKeys = [[groupedEvents.allKeys sortedArrayUsingSelector:@selector(compare:)] mutableCopy]; + if ([sortedKeys containsObject:_otherString]) { + [sortedKeys removeObject:_otherString]; + [sortedKeys addObject:_otherString]; + } + + if ([sortedKeys containsObject:_optionalString]) { + [sortedKeys removeObject:_optionalString]; + [sortedKeys addObject:_optionalString]; + } + + _sectionTitles = [sortedKeys copy]; + + NSMutableArray *array = [NSMutableArray new]; + for (NSString *key in _sectionTitles) { + NSMutableArray *groupArray = [NSMutableArray new]; + NSArray *groupedEventsArray = groupedEvents[key]; + + NSMutableDictionary *activitiesDictionary = [NSMutableDictionary new]; + for (NSArray *events in groupedEventsArray) { + NSString *activityTitle = events.firstObject.activity.title; + activitiesDictionary[activityTitle] = events; + } + + NSArray *sortedActivitiesKeys = [activitiesDictionary.allKeys sortedArrayUsingSelector:@selector(compare:)]; + for (NSString *activityKey in sortedActivitiesKeys) { + [groupArray addObject:activitiesDictionary[activityKey]]; + } + + [array addObject:groupArray]; + } + + _tableViewData = [array mutableCopy]; +} + #pragma mark - OCKWeekViewDelegate - (void)weekViewSelectionDidChange:(UIView *)weekView { - OCKCareCardWeekView *careCardWeekView = (OCKCareCardWeekView *)weekView; - NSDateComponents *selectedDate = [self dateFromSelectedIndex:careCardWeekView.selectedIndex]; + OCKWeekView *currentWeekView = (OCKWeekView *)weekView; + NSDateComponents *selectedDate = [self dateFromSelectedIndex:currentWeekView.selectedIndex]; self.selectedDate = selectedDate; } -- (BOOL)weekViewCanSelectDayAtIndex:(NSUInteger)index { - NSDateComponents *today = [self today]; - NSDateComponents *selectedDate = [self dateFromSelectedIndex:index]; - return ![selectedDate isLaterThan:today]; -} - #pragma mark - OCKCareCardCellDelegate @@ -420,29 +533,49 @@ - (void)careCardTableViewCell:(OCKCareCardTableViewCell *)cell didUpdateFrequenc NSMutableArray *events = [cell.interventionEvents mutableCopy]; [events replaceObjectAtIndex:event.occurrenceIndexOfDay withObject:event]; cell.interventionEvents = events; - cell.showEdgeIndicator = cell.showEdgeIndicator; }); }]; } } +- (void)careCardTableViewCell:(OCKCareCardTableViewCell *)cell didSelectInterventionActivity:(OCKCarePlanActivity *)activity { + NSIndexPath *indexPath = [_tableView indexPathForCell:cell]; + OCKCarePlanActivity *selectedActivity = [self activityForIndexPath:indexPath]; + _lastSelectedInterventionActivity = selectedActivity; + + if ([self delegateCustomizesRowSelection]) { + [self.delegate careCardViewController:self didSelectRowWithInterventionActivity:selectedActivity]; + } else { + [self.navigationController pushViewController:[self detailViewControllerForActivity:selectedActivity] animated:YES]; + } +} + #pragma mark - OCKCarePlanStoreDelegate - (void)carePlanStore:(OCKCarePlanStore *)store didReceiveUpdateOfEvent:(OCKCarePlanEvent *)event { - for (NSMutableArray *events in _events) { - if ([events.firstObject.activity.identifier isEqualToString:event.activity.identifier]) { - if (events[event.occurrenceIndexOfDay].numberOfDaysSinceStart == event.numberOfDaysSinceStart) { - [events replaceObjectAtIndex:event.occurrenceIndexOfDay withObject:event]; - [self updateHeaderView]; - - NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[_events indexOfObject:events] inSection:0]; - OCKCareCardTableViewCell *cell = [_tableView cellForRowAtIndexPath:indexPath]; - cell.interventionEvents = events; - cell.showEdgeIndicator = cell.showEdgeIndicator; + for (int i = 0; i < _tableViewData.count; i++) { + NSMutableArray *> *groupedEvents = _tableViewData[i]; + + for (int j = 0; j < groupedEvents.count; j++) { + NSMutableArray *events = groupedEvents[j]; + + if ([events.firstObject.activity.identifier isEqualToString:event.activity.identifier]) { + if (events[event.occurrenceIndexOfDay].numberOfDaysSinceStart == event.numberOfDaysSinceStart) { + [events replaceObjectAtIndex:event.occurrenceIndexOfDay withObject:event]; + _tableViewData[i][j] = events; + + [self updateHeaderView]; + + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:j inSection:i]; + OCKCareCardTableViewCell *cell = [_tableView cellForRowAtIndexPath:indexPath]; + cell.interventionEvents = events; + } + break; } - break; + } + } if ([event.date isInSameWeekAsDate: self.selectedDate]) { @@ -460,7 +593,7 @@ - (void)carePlanStoreActivityListDidChange:(OCKCarePlanStore *)store { - (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed { if (completed) { OCKWeekViewController *controller = (OCKWeekViewController *)pageViewController.viewControllers.firstObject; - controller.careCardWeekView.delegate = _weekViewController.careCardWeekView.delegate; + controller.weekView.delegate = _weekViewController.weekView.delegate; NSDateComponents *components = [NSDateComponents new]; components.day = (controller.weekIndex > _weekViewController.weekIndex) ? 7 : -7; @@ -477,44 +610,45 @@ - (void)pageViewController:(UIPageViewController *)pageViewController didFinishA - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController { OCKWeekViewController *controller = [OCKWeekViewController new]; controller.weekIndex = ((OCKWeekViewController *)viewController).weekIndex - 1; - controller.careCardWeekView.smallMaskImage = self.smallMaskImage; - controller.careCardWeekView.tintColor = self.maskImageTintColor; + controller.weekView.tintColor = self.glyphTintColor; + controller.weekView.isCareCard = YES; + controller.weekView.glyphType = self.glyphType; return controller; } - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController { OCKWeekViewController *controller = [OCKWeekViewController new]; controller.weekIndex = ((OCKWeekViewController *)viewController).weekIndex + 1; - controller.careCardWeekView.smallMaskImage = self.smallMaskImage; - controller.careCardWeekView.tintColor = self.maskImageTintColor; - return (![self.selectedDate isInSameWeekAsDate:[self today]]) ? controller : nil; + controller.weekView.tintColor = self.glyphTintColor; + controller.weekView.isCareCard = YES; + controller.weekView.glyphType = self.glyphType; + return controller; } #pragma mark - UITableViewDelegate -- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { - return _headerView; +- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { + NSString *sectionTitle = _sectionTitles[section]; + if ([sectionTitle isEqualToString:_otherString] && (_sectionTitles.count == 1 || (_sectionTitles.count == 2 && [_sectionTitles containsObject:_optionalString]))) { + sectionTitle = @""; + } + return sectionTitle; } -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - OCKCarePlanActivity *selectedActivity = [self activityForIndexPath:indexPath]; - _lastSelectedInterventionActivity = selectedActivity; - - if ([self delegateCustomizesRowSelection]) { - [self.delegate careCardViewController:self didSelectRowWithInterventionActivity:selectedActivity]; - } else { - [self.navigationController pushViewController:[self detailViewControllerForActivity:selectedActivity] animated:YES]; - } - - [tableView deselectRowAtIndexPath:indexPath animated:NO]; +- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath { + return NO; } #pragma mark - UITableViewDataSource +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return _tableViewData.count; +} + - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return _events.count; + return _tableViewData[section].count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { @@ -524,12 +658,12 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N cell = [[OCKCareCardTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; } - cell.interventionEvents = _events[indexPath.row]; + cell.interventionEvents = _tableViewData[indexPath.section][indexPath.row]; cell.delegate = self; - cell.showEdgeIndicator = self.showEdgeIndicators; return cell; } + #pragma mark - UIViewControllerPreviewingDelegate - (UIViewController *)previewingContext:(id )previewingContext viewControllerForLocation:(CGPoint)location { diff --git a/CareKit/CareCard/OCKHeartView.m b/CareKit/CareCard/OCKHeartView.m deleted file mode 100644 index 37305e8f3..000000000 --- a/CareKit/CareCard/OCKHeartView.m +++ /dev/null @@ -1,133 +0,0 @@ -/* - Copyright (c) 2016, Apple Inc. All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - - 3. Neither the name of the copyright holder(s) nor the names of any contributors - may be used to endorse or promote products derived from this software without - specific prior written permission. No license is granted to the trademarks of - the copyright holders even if such marks are included in this software. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - -#import "OCKHeartView.h" - - -@implementation OCKHeartView { - UIView *_maskView; - UIView *_fillView; - UIImageView *_imageView; - UIImageView *_maskImageView; - UIImage *_maskImage; -} - -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - _animationEnabled = YES; - _value = 0; - } - return self; -} - -- (void)prepareView { - [_maskView removeFromSuperview]; - [_fillView removeFromSuperview]; - [_imageView removeFromSuperview]; - [_maskImageView removeFromSuperview]; - - _maskView = [[UIView alloc] init]; - _maskImageView = [[UIImageView alloc] initWithImage:_maskImage]; - _maskImageView.contentMode = UIViewContentModeScaleAspectFit; - [_maskView addSubview:_maskImageView]; - self.maskView = _maskView; - self.clipsToBounds = YES; - - _imageView = [[UIImageView alloc] initWithImage:_maskImage]; - _imageView.contentMode = UIViewContentModeScaleAspectFit; - [self insertSubview:_imageView belowSubview:_maskView]; - - _fillView = [[UIView alloc] init]; - _fillView.backgroundColor = self.tintColor; - [self addSubview:_fillView]; -} - -- (void)layoutSubviews { - [super layoutSubviews]; - - CGSize maskImageSize = _maskImage.size; - - CGFloat widthRatio = self.bounds.size.width / maskImageSize.width; - CGFloat heightRatio = self.bounds.size.height / maskImageSize.height; - CGFloat scale = MIN(widthRatio, heightRatio); - CGFloat imageWidth = scale * maskImageSize.width; - CGFloat imageHeight = scale * maskImageSize.height; - - _maskView.frame = CGRectMake(0.0, 0.0, imageWidth, imageHeight); - _maskView.center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); - _maskImageView.frame = _maskView.bounds; - _imageView.frame = _maskView.frame; - - if(_fillView.layer.animationKeys.count == 0) { - [self updateFillViewFrame]; - } -} - -- (void)animateFill { - if (self.animationEnabled) { - [UIView animateWithDuration:1.25 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{ - [self updateFillViewFrame]; - } completion:^(BOOL finished) { - }]; - } else { - [self updateFillViewFrame]; - } -} - -- (void)updateFillViewFrame { - CGRect fillViewFrame = _maskView.frame; - fillViewFrame.origin.y = CGRectGetMaxY(_maskView.frame); - fillViewFrame.size.height = -_value * CGRectGetHeight(_maskView.frame); - _fillView.frame = fillViewFrame; -} - - -- (void)setValue:(double)value { - if (_value != value) { - _value = value; - [self animateFill]; - } -} - -- (void)setMaskImage:(UIImage *)maskImage { - _maskImage = maskImage; - if(_maskImage) { - [self prepareView]; - } -} - -- (void)tintColorDidChange { - [super tintColorDidChange]; - _fillView.backgroundColor = self.tintColor; -} - -@end diff --git a/CareKit/CareContents/OCKCareContentsViewController.h b/CareKit/CareContents/OCKCareContentsViewController.h new file mode 100644 index 000000000..c637d78f2 --- /dev/null +++ b/CareKit/CareContents/OCKCareContentsViewController.h @@ -0,0 +1,211 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import + + +NS_ASSUME_NONNULL_BEGIN + +@class OCKCarePlanStore, OCKCareContentsViewController; + +/** + An object that adopts the `OCKCareContentsViewControllerDelegate` protocol can use it to modify or update the events before they are displayed. + */ +@protocol OCKCareContentsViewControllerDelegate + +@required + +/** + Tells the delegate when the user selected an assessment event. + + @param viewController The view controller providing the callback. + @param assessmentEvent The assessment event that the user selected. + */ +- (void)careContentsViewController:(OCKCareContentsViewController *)viewController didSelectRowWithAssessmentEvent:(OCKCarePlanEvent *)assessmentEvent; + +@optional + +/** + Tells the delegate when the user tapped an intervention event. + + If the user must perform some activity in order to complete the intervention event, + then this method can be implemented to show a custom view controller. + + If the completion status of the event is dependent on the presented activity, the developer can implement + the `careContentsViewController:shouldHandleEventCompletionForActivity` to control the completion status of the event. + + @param viewController The view controller providing the callback. + @param interventionEvent The intervention event that the user selected. + */ +- (void)careContentsViewController:(OCKCareContentsViewController *)viewController didSelectButtonWithInterventionEvent:(OCKCarePlanEvent *)interventionEvent; + +/** + Tells the delegate when the user selected an intervention activity. + + This can be implemented to show a custom detail view controller. + If not implemented, a default detail view controller will be presented. + + @param viewController The view controller providing the callback. + @param interventionActivity The intervention activity that the user selected. + */ +- (void)careContentsViewController:(OCKCareContentsViewController *)viewController didSelectRowWithInterventionActivity:(OCKCarePlanActivity *)interventionActivity; + +/** + Tells the delegate when the user selected a readonly activity. + + This can be implemented to show a custom detail view controller. + If not implemented, a default detail view controller will be presented. + + @param viewController The view controller providing the callback. + @param readOnlyActivity The readonly activity that the user selected. + */ +- (void)careContentsViewController:(OCKCareContentsViewController *)viewController didSelectRowWithReadOnlyActivity:(OCKCarePlanActivity *)readOnlyActivity; + +/** + Asks the delegate if care view controller should automatically mark the state of an intervention activity when + the user selects and deselects the intervention circle button. If this method is not implemented, care view controller + handles all event completion by default. + + If returned NO, the `careContentsViewController:didSelectButtonWithInterventionEvent` method can be implemeted to provide + custom logic for completion. + + @param viewController The view controller providing the callback. + @param interventionActivity The intervention activity that the user selected. + */ +- (BOOL)careContentsViewController:(OCKCareContentsViewController *)viewController shouldHandleEventCompletionForInterventionActivity:(OCKCarePlanActivity *)interventionActivity; + +/** + Tells the delegate when a new set of events is fetched from the care plan store. + + This is invoked when the date changes or when the care plan store's `carePlanStoreActivityListDidChange` delegate method is called. + This provides a good opportunity to update the store such as fetching data from HealthKit. + + @param viewController The view controller providing the callback. + @param events An array containing the fetched set of intervention events grouped by activity. + @param dateComponents The date components for which the events will be displayed. + */ +- (void)careContentsViewController:(OCKCareContentsViewController *)viewController willDisplayEvents:(NSArray*>*)events dateComponents:(NSDateComponents *)dateComponents; + +@end + + +/** + The `OCKCareContentsViewController` class is a view controller that displays the activities and events from an `OCKCarePlanStore` that are of + intervention type (see `OCKCarePlanActivityTypeIntervention`), assessment type (see `OCKCarePlanActivityTypeAssessment`), and read only + intervention and assessment types (see `OCKCarePlanActivityTypeReadOnly`). + + Activities can include a detail view. Therefore, it must be embedded inside a `UINavigationController`. + */ +OCK_CLASS_AVAILABLE +@interface OCKCareContentsViewController : UIViewController + +- (instancetype)init NS_UNAVAILABLE; + +/** + Returns an initialized care view controller using the specified store. + + @param store A care plan store. + + @return An initialized care view controller. + */ +- (instancetype)initWithCarePlanStore:(OCKCarePlanStore *)store; + +/** + The care plan store that provides the content for the care card. + + The care view displays activites and events of type intervention, assessment, intervention read-only, + and assessment read-only (see `OCKCarePlanActivityType`). + */ +@property (nonatomic, readonly) OCKCarePlanStore *store; + + +/** + The last activity selected by the user. + + This value is nil if no activity has been selected yet. + */ +@property (nonatomic, readonly, nullable) OCKCarePlanActivity *lastSelectedActivity; + + +/** + The last event selected by the user. + + This value is nil if no event has been selected yet. + */ +@property (nonatomic, readonly, nullable) OCKCarePlanEvent *lastSelectedEvent; + + +/** + The image that will be used to mask the fill shape in the week view. + + In order to provide a custom maskImage, you must have a regular size and small size. + For example, in the assets catalog, there are "heart" and a "heart-small" assets. + Both assets must be provided in order to properly render the interface. + + The tint color that will be used to fill the shape. + + If tint color is not specified, a default red color will be used. + */ +@property (nonatomic, null_resettable) UIColor *glyphTintColor; + + +/** + The glyph type for the header view (see OCKGlyphType). + */ +@property (nonatomic) OCKGlyphType glyphType; + +/** + Image name string if using a custom image. Cannot access image name once image has been created + and we need a way to access that to send the custom image name string to the watch + */ +@property (nonatomic, copy) NSString *customGlyphImageName; + + +/** + The delegate can be used to modify or update the internvention events before they are displayed. + + See the `OCKCareContentsViewControllerDelegate` protocol. + */ +@property (nonatomic, weak, nullable) id delegate; + +/** + The section header title for all the Optional activities. Default is `Optional` if nil. + */ +@property (nonatomic, nullable) NSString *optionalSectionHeader; + +/** + The section header title for all the ReadOnly activities. Default is `Read Only` if nil. + */ +@property (nonatomic, nullable) NSString *readOnlySectionHeader; + +@end + +NS_ASSUME_NONNULL_END diff --git a/CareKit/CareContents/OCKCareContentsViewController.m b/CareKit/CareContents/OCKCareContentsViewController.m new file mode 100644 index 000000000..374f16e3b --- /dev/null +++ b/CareKit/CareContents/OCKCareContentsViewController.m @@ -0,0 +1,824 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import "OCKCareContentsViewController.h" +#import "OCKWeekView.h" +#import "OCKCareCardDetailViewController.h" +#import "OCKWeekViewController.h" +#import "NSDateComponents+CarePlanInternal.h" +#import "OCKHeaderView.h" +#import "OCKCareCardTableViewCell.h" +#import "OCKSymptomTrackerTableViewCell.h" +#import "OCKReadOnlyTableViewCell.h" +#import "OCKWeekLabelsView.h" +#import "OCKCarePlanStore_Internal.h" +#import "OCKHelpers.h" +#import "OCKDefines_Private.h" +#import "OCKGlyph_Internal.h" + + +#define RedColor() OCKColorFromRGB(0xEF445B); + + +@interface OCKCareContentsViewController() + +@property (nonatomic) NSDateComponents *selectedDate; + +@end + + +@implementation OCKCareContentsViewController { + UITableView *_tableView; + NSMutableArray *> *_events; + NSMutableArray *_weekValues; + OCKHeaderView *_headerView; + UIPageViewController *_pageViewController; + OCKWeekViewController *_weekViewController; + NSCalendar *_calendar; + NSMutableArray *_constraints; + NSMutableArray *_sectionTitles; + NSMutableArray *> *> *_tableViewData; + NSMutableDictionary *_allEvents; + + NSString *_otherString; + NSString *_optionalString; + NSString *_readOnlyString; +} + +- (instancetype)init { + OCKThrowMethodUnavailableException(); + return nil; +} + +- (instancetype)initWithCarePlanStore:(OCKCarePlanStore *)store { + self = [super init]; + if (self) { + _store = store; + _calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian]; + _glyphTintColor = nil; + } + return self; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + _otherString = OCKLocalizedString(@"ACTIVITY_TYPE_OTHER_SECTION_HEADER", nil); + _optionalString = OCKLocalizedString(@"ACTIVITY_TYPE_OPTIONAL_SECTION_HEADER", nil); + _readOnlyString = OCKLocalizedString(@"ACTIVITY_TYPE_READ_ONLY_SECTION_HEADER", nil); + if (self.optionalSectionHeader && ![self.optionalSectionHeader isEqual: @""]) { + _optionalString = _optionalSectionHeader; + } + if (self.readOnlySectionHeader && ![self.readOnlySectionHeader isEqual: @""]) { + _readOnlyString = _readOnlySectionHeader; + } + self.view.backgroundColor = [UIColor groupTableViewBackgroundColor]; + self.store.symptomTrackerUIDelegate = self; + self.store.careCardUIDelegate = self; + [self setGlyphTintColor: _glyphTintColor]; + NSDictionary *_initialDictionary = @{ @(OCKCarePlanActivityTypeAssessment): [NSMutableArray new], + @(OCKCarePlanActivityTypeIntervention): [NSMutableArray new], + @(OCKCarePlanActivityTypeReadOnly): [NSMutableArray new] }; + _allEvents = [_initialDictionary mutableCopy]; + + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:OCKLocalizedString(@"TODAY_BUTTON_TITLE", nil) + style:UIBarButtonItemStylePlain + target:self + action:@selector(showToday:)]; + self.navigationItem.rightBarButtonItem.tintColor = self.glyphTintColor; + + _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped]; + _tableView.dataSource = self; + _tableView.delegate = self; + [self.view addSubview:_tableView]; + + [self prepareView]; + self.selectedDate = [NSDateComponents ock_componentsWithDate:[NSDate date] calendar:_calendar]; + _tableView.estimatedRowHeight = 90.0; + _tableView.rowHeight = UITableViewAutomaticDimension; + _tableView.tableFooterView = [UIView new]; + _tableView.estimatedSectionHeaderHeight = 0; + _tableView.estimatedSectionFooterHeight = 0; + + self.navigationController.navigationBar.translucent = NO; + [self.navigationController.navigationBar setBarTintColor:[UIColor colorWithRed:245.0/255.0 green:244.0/255.0 blue:246.0/255.0 alpha:1.0]]; + +} + +- (void)viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + NSAssert(self.navigationController, @"OCKCareViewController must be embedded in a navigation controller."); + _weekViewController.weekView.delegate = self; +} + +- (void)showToday:(id)sender { + self.selectedDate = [NSDateComponents ock_componentsWithDate:[NSDate date] calendar:_calendar]; + [_tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:NSNotFound inSection:0] atScrollPosition:UITableViewScrollPositionMiddle animated:YES]; +} + +- (void)prepareView { + if (!_headerView) { + _headerView = [[OCKHeaderView alloc] initWithFrame:CGRectZero]; + [self.view addSubview:_headerView]; + } + + _headerView.tintColor = self.glyphTintColor; + if (self.glyphType == OCKGlyphTypeCustom) { + UIImage *glyphImage = [self createCustomImageName:self.customGlyphImageName]; + _headerView.glyphImage = glyphImage; + } + _headerView.isCareCard = YES; + _headerView.glyphType = self.glyphType; + + if (!_pageViewController) { + _pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll + navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal + options:nil]; + _pageViewController.dataSource = self; + _pageViewController.delegate = self; + + if (!UIAccessibilityIsReduceTransparencyEnabled()) { + _pageViewController.view.backgroundColor = [UIColor groupTableViewBackgroundColor]; + + UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleProminent]; + UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect]; + blurEffectView.frame = _pageViewController.view.bounds; + blurEffectView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [_pageViewController.view insertSubview:blurEffectView atIndex:_pageViewController.view.subviews.count-1]; + } + else { + _pageViewController.view.backgroundColor = [UIColor whiteColor]; + } + + OCKWeekViewController *weekController = [OCKWeekViewController new]; + weekController.weekView.delegate = _weekViewController.weekView.delegate; + weekController.weekView.ringTintColor = self.glyphTintColor; + weekController.weekView.isCareCard = YES; + weekController.weekView.glyphType = self.glyphType; + _weekViewController = weekController; + + [_pageViewController setViewControllers:@[weekController] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil]; + [self.view addSubview:_pageViewController.view]; + } + + _tableView.showsVerticalScrollIndicator = NO; + + [self setUpConstraints]; +} + +- (void)setUpConstraints { + [NSLayoutConstraint deactivateConstraints:_constraints]; + + _constraints = [NSMutableArray new]; + + _tableView.translatesAutoresizingMaskIntoConstraints = NO; + _pageViewController.view.translatesAutoresizingMaskIntoConstraints = NO; + _headerView.translatesAutoresizingMaskIntoConstraints = NO; + + + [_constraints addObjectsFromArray:@[ + [NSLayoutConstraint constraintWithItem:_pageViewController.view + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.topLayoutGuide + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_pageViewController.view + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:_headerView + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:10.0], + [NSLayoutConstraint constraintWithItem:_pageViewController.view + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_pageViewController.view + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_pageViewController.view + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:65.0], + [NSLayoutConstraint constraintWithItem:_headerView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:140.0], + [NSLayoutConstraint constraintWithItem:_headerView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:_tableView + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_tableView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_tableView + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_tableView + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_headerView + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_headerView + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:0.0] + ]]; + + [NSLayoutConstraint activateConstraints:_constraints]; +} + +- (void)setSelectedDate:(NSDateComponents *)selectedDate { + _selectedDate = selectedDate; + + _weekViewController.weekView.isToday = [[self today] isEqualToDate:selectedDate]; + _weekViewController.weekView.selectedIndex = self.selectedDate.weekday - 1; + + [self fetchEvents]; +} + +- (void)setGlyphTintColor:(UIColor *)glyphTintColor { + _glyphTintColor = glyphTintColor; + if (!_glyphTintColor) { + _glyphTintColor = [OCKGlyph defaultColorForGlyph:self.glyphType]; + } + _weekViewController.weekView.tintColor = _glyphTintColor; + _headerView.tintColor = _glyphTintColor; + self.navigationItem.rightBarButtonItem.tintColor = _glyphTintColor; +} + + +#pragma mark - Helpers + +- (void)fetchEvents { + dispatch_async(dispatch_get_main_queue(), ^{ + [self fetchEventsOfType:OCKCarePlanActivityTypeIntervention]; + [self fetchEventsOfType:OCKCarePlanActivityTypeAssessment]; + [self fetchEventsOfType:OCKCarePlanActivityTypeReadOnly]; + }); +} + +- (void)fetchEventsOfType:(OCKCarePlanActivityType)type { + [self.store eventsOnDate:self.selectedDate + type:type + completion:^(NSArray *> *eventsGroupedByActivity, NSError *error) { + NSAssert(!error, error.localizedDescription); + dispatch_async(dispatch_get_main_queue(), ^{ + _events = [NSMutableArray new]; + for (NSArray *events in eventsGroupedByActivity) { + [_events addObject:[events mutableCopy]]; + } + [_allEvents setObject:_events forKey:@(type)]; + + if (self.delegate && + [self.delegate respondsToSelector:@selector(careContentsViewController:willDisplayEvents:dateComponents:)]) { + [self.delegate careContentsViewController:self willDisplayEvents:[_events copy] dateComponents:_selectedDate]; + } + [self createGroupedEventDictionaryForEvents]; + if (type == OCKCarePlanActivityTypeIntervention || type == OCKCarePlanActivityTypeAssessment) { + [self updateHeaderView]; + [self updateWeekView]; + } + [_tableView reloadData]; + }); + }]; + +} + +- (void)updateHeaderView { + _headerView.date = [NSDateFormatter localizedStringFromDate:[_calendar dateFromComponents:self.selectedDate] + dateStyle:NSDateFormatterLongStyle + timeStyle:NSDateFormatterNoStyle]; + NSMutableArray *values = [NSMutableArray new]; + __block NSUInteger completedEvents; + __block NSUInteger totalEvents; + + __weak __typeof__(self) weakSelf = self; + + [self.store dailyCompletionStatusWithType:OCKCarePlanActivityTypeIntervention + startDate:self.selectedDate + endDate:self.selectedDate + handler:^(NSDateComponents *date, NSUInteger completedInterventionEvents, NSUInteger totalInterventionEvents) { + + completedEvents = completedInterventionEvents; + totalEvents = totalInterventionEvents; + + } completion:^(BOOL completed, NSError *error) { + NSAssert(!error, error.localizedDescription); + dispatch_async(dispatch_get_main_queue(), ^{ + __typeof__(self) strongSelf = weakSelf; + [strongSelf.store dailyCompletionStatusWithType:OCKCarePlanActivityTypeAssessment + startDate:strongSelf.selectedDate + endDate:strongSelf.selectedDate + handler:^(NSDateComponents *date, NSUInteger completedAssessmentEvents, NSUInteger totalAssessmentEvents) { + completedEvents = completedEvents + completedAssessmentEvents; + totalEvents = totalEvents + totalAssessmentEvents; + if (totalEvents == 0) { + [values addObject:@(1)]; + } else { + [values addObject:@((float)completedEvents/totalEvents)]; + } + + } completion:^(BOOL completed, NSError *error) { + NSAssert(!error, error.localizedDescription); + dispatch_async(dispatch_get_main_queue(), ^{ + NSInteger selectedIndex = _weekViewController.weekView.selectedIndex; + [_weekValues replaceObjectAtIndex:selectedIndex withObject:values.firstObject]; + _weekViewController.weekView.values = _weekValues; + _headerView.value = [values.firstObject doubleValue]; + }); + }]; + }); + }]; +} + +- (void)updateWeekView { + NSDate *selectedDate = [_calendar dateFromComponents:self.selectedDate]; + NSDate *startOfWeek; + NSTimeInterval interval; + [_calendar rangeOfUnit:NSCalendarUnitWeekOfMonth + startDate:&startOfWeek + interval:&interval + forDate:selectedDate]; + NSDate *endOfWeek = [startOfWeek dateByAddingTimeInterval:interval-1]; + + NSMutableArray *values = [NSMutableArray new]; + NSMutableArray *interventionCompleted = [NSMutableArray new]; + NSMutableArray *interventionTotal = [NSMutableArray new]; + NSMutableArray *assessmentCompleted = [NSMutableArray new]; + NSMutableArray *assessmentTotal = [NSMutableArray new]; + __weak __typeof__(self) weakSelf = self; + [self.store dailyCompletionStatusWithType:OCKCarePlanActivityTypeIntervention + startDate:[NSDateComponents ock_componentsWithDate:startOfWeek calendar:_calendar] + endDate:[NSDateComponents ock_componentsWithDate:endOfWeek calendar:_calendar] + handler:^(NSDateComponents *date, NSUInteger completedEvents, NSUInteger totalEvents) { + [interventionCompleted addObject:@((float)completedEvents)]; + [interventionTotal addObject:@((float)totalEvents)]; + } completion:^(BOOL completed, NSError *error) { + NSAssert(!error, error.localizedDescription); + dispatch_async(dispatch_get_main_queue(), ^{ + __typeof__(self) strongSelf = weakSelf; + [strongSelf.store dailyCompletionStatusWithType:OCKCarePlanActivityTypeAssessment + startDate:[NSDateComponents ock_componentsWithDate:startOfWeek calendar:_calendar] + endDate:[NSDateComponents ock_componentsWithDate:endOfWeek calendar:_calendar] + handler:^(NSDateComponents *date, NSUInteger completedEvents, NSUInteger totalEvents) { + [assessmentCompleted addObject:@((float)completedEvents)]; + [assessmentTotal addObject:@((float)totalEvents)]; + } completion:^(BOOL completed, NSError *error) { + NSAssert(!error, error.localizedDescription); + dispatch_async(dispatch_get_main_queue(), ^{ + for (int i = 0; i<7; i++) { + if (([interventionTotal[i] floatValue] + [assessmentTotal[i] floatValue]) == 0) { + [values addObject:@(1)]; + } else { + float completed = [interventionCompleted[i] floatValue] + [assessmentCompleted[i] floatValue]; + float total = [interventionTotal[i] floatValue] + [assessmentTotal[i] floatValue]; + [values addObject:@(completed/total)]; + } + } + _weekViewController.weekView.values = values; + _weekValues = [values mutableCopy]; + }); + }]; + }); + }]; +} + +- (UIImage *)createCustomImageName:(NSString*)customImageName { + UIImage *customImageToReturn; + if (customImageName != nil) { + NSBundle *bundle = [NSBundle mainBundle]; + customImageToReturn = [UIImage imageNamed: customImageName inBundle:bundle compatibleWithTraitCollection:nil]; + } else { + OCKGlyphType defaultGlyph = OCKGlyphTypeHeart; + customImageToReturn = [[OCKGlyph glyphImageForType:defaultGlyph] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + } + return customImageToReturn; +} + +- (NSDateComponents *)dateFromSelectedIndex:(NSInteger)index { + NSDateComponents *newComponents = [NSDateComponents new]; + newComponents.year = self.selectedDate.year; + newComponents.month = self.selectedDate.month; + newComponents.weekOfMonth = self.selectedDate.weekOfMonth; + newComponents.weekday = index + 1; + + NSDate *newDate = [_calendar dateFromComponents:newComponents]; + return [NSDateComponents ock_componentsWithDate:newDate calendar:_calendar]; +} + +- (NSDateComponents *)today { + return [NSDateComponents ock_componentsWithDate:[NSDate date] calendar:_calendar]; +} + +- (void)createGroupedEventDictionaryForEvents { + + NSArray *interventions = _allEvents[@(OCKCarePlanActivityTypeIntervention)]; + NSArray *assessments = _allEvents[@(OCKCarePlanActivityTypeAssessment)]; + NSArray *readOnly = _allEvents[@(OCKCarePlanActivityTypeReadOnly)]; + + NSArray *> *events = [NSArray arrayWithArray:[interventions arrayByAddingObjectsFromArray:[assessments arrayByAddingObjectsFromArray:readOnly]]]; + NSMutableDictionary *groupedEvents = [NSMutableDictionary new]; + + for (NSArray *activityEvents in events) { + OCKCarePlanEvent *firstEvent = activityEvents.firstObject; + NSString *groupIdentifier = firstEvent.activity.groupIdentifier ? firstEvent.activity.groupIdentifier : _otherString; + + if (firstEvent.activity.optional) { + groupIdentifier = firstEvent.activity.type == OCKCarePlanActivityTypeReadOnly ? _readOnlyString : _optionalString; + } + + if (groupedEvents[groupIdentifier]) { + NSMutableArray *objects = [groupedEvents[groupIdentifier] mutableCopy]; + [objects addObject:activityEvents]; + groupedEvents[groupIdentifier] = objects; + } else { + NSMutableArray *objects = [activityEvents mutableCopy]; + groupedEvents[groupIdentifier] = @[objects]; + } + } + + NSMutableArray *sortedKeys = [[groupedEvents.allKeys sortedArrayUsingSelector:@selector(compare:)] mutableCopy]; + if ([sortedKeys containsObject:_otherString]) { + [sortedKeys removeObject:_otherString]; + [sortedKeys addObject:_otherString]; + } + + if ([sortedKeys containsObject:_optionalString]) { + [sortedKeys removeObject:_optionalString]; + [sortedKeys addObject:_optionalString]; + } + + if ([sortedKeys containsObject:_readOnlyString]) { + [sortedKeys removeObject:_readOnlyString]; + [sortedKeys addObject:_readOnlyString]; + } + + _sectionTitles = [sortedKeys copy]; + + NSMutableArray *array = [NSMutableArray new]; + for (NSString *key in _sectionTitles) { + NSMutableArray *groupArray = [NSMutableArray new]; + NSArray *groupedEventsArray = groupedEvents[key]; + + NSMutableDictionary *activitiesDictionary = [NSMutableDictionary new]; + for (NSArray *events in groupedEventsArray) { + NSString *activityTitle = events.firstObject.activity.title; + activitiesDictionary[activityTitle] = events; + } + + NSArray *sortedActivitiesKeys = [activitiesDictionary.allKeys sortedArrayUsingSelector:@selector(compare:)]; + for (NSString *activityKey in sortedActivitiesKeys) { + [groupArray addObject:activitiesDictionary[activityKey]]; + } + [array addObject:groupArray]; + } + _tableViewData = [array mutableCopy]; +} + + +#pragma mark - OCKWeekViewDelegate + +- (void)weekViewSelectionDidChange:(UIView *)weekView { + OCKWeekView *currentWeekView = (OCKWeekView *)weekView; + NSDateComponents *selectedDate = [self dateFromSelectedIndex:currentWeekView.selectedIndex]; + self.selectedDate = selectedDate; +} + + +#pragma mark - OCKCarePlanStoreDelegate + +- (void)carePlanStore:(OCKCarePlanStore *)store didReceiveUpdateOfEvent:(OCKCarePlanEvent *)event { + for (int i = 0; i < _tableViewData.count; i++) { + NSMutableArray *> *groupedEvents = _tableViewData[i]; + + for (int j = 0; j < groupedEvents.count; j++) { + NSMutableArray *events = groupedEvents[j]; + + if ([events.firstObject.activity.identifier isEqualToString:event.activity.identifier]) { + if (events[event.occurrenceIndexOfDay].numberOfDaysSinceStart == event.numberOfDaysSinceStart) { + [events replaceObjectAtIndex:event.occurrenceIndexOfDay withObject:event]; + _tableViewData[i][j] = events; + [self updateHeaderView]; + + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:j inSection:i]; + OCKCarePlanActivityType type = _tableViewData[indexPath.section][indexPath.row].firstObject.activity.type; + if ( type == OCKCarePlanActivityTypeIntervention) { + OCKCareCardTableViewCell *cell = [_tableView cellForRowAtIndexPath:indexPath]; + cell.interventionEvents = events; + } else if (type == OCKCarePlanActivityTypeAssessment) { + OCKSymptomTrackerTableViewCell *cell = [_tableView cellForRowAtIndexPath:indexPath]; + cell.assessmentEvent = events.firstObject; + } + } + break; + } + } + } + + if ([event.date isInSameWeekAsDate: self.selectedDate]) { + [self updateWeekView]; + } +} + +- (void)carePlanStoreActivityListDidChange:(OCKCarePlanStore *)store { + [self fetchEvents]; +} + + +#pragma mark - UIPageViewControllerDelegate + +- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed { + if (completed) { + OCKWeekViewController *controller = (OCKWeekViewController *)pageViewController.viewControllers.firstObject; + controller.weekView.delegate = _weekViewController.weekView.delegate; + + NSDateComponents *components = [NSDateComponents new]; + components.day = (controller.weekIndex > _weekViewController.weekIndex) ? 7 : -7; + NSDate *newDate = [_calendar dateByAddingComponents:components toDate:[_calendar dateFromComponents:self.selectedDate] options:0]; + + _weekViewController = controller; + self.selectedDate = [NSDateComponents ock_componentsWithDate:newDate calendar:_calendar]; + } +} + + +#pragma mark - UIPageViewControllerDataSource + +- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController { + OCKWeekViewController *controller = [OCKWeekViewController new]; + controller.weekIndex = ((OCKWeekViewController *)viewController).weekIndex - 1; + controller.weekView.tintColor = self.glyphTintColor; + controller.weekView.isCareCard = YES; + controller.weekView.glyphType = self.glyphType; + return controller; +} + +- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController { + OCKWeekViewController *controller = [OCKWeekViewController new]; + controller.weekIndex = ((OCKWeekViewController *)viewController).weekIndex + 1; + controller.weekView.tintColor = self.glyphTintColor; + controller.weekView.isCareCard = YES; + controller.weekView.glyphType = self.glyphType; + return controller; +} + + +#pragma mark - UITableViewDelegate + +- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { + NSString *sectionTitle = _sectionTitles[section]; + if ([sectionTitle isEqualToString:_otherString] && (_sectionTitles.count == 1 || (_sectionTitles.count == 2 && [_sectionTitles containsObject:_optionalString]))) { + sectionTitle = @""; + } + return sectionTitle; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + OCKCarePlanEvent *selectedEvent = _tableViewData[indexPath.section][indexPath.row].firstObject; + _lastSelectedEvent = selectedEvent; + _lastSelectedActivity = selectedEvent.activity; + + OCKCarePlanActivityType type = selectedEvent.activity.type; + + if (type == OCKCarePlanActivityTypeAssessment) { + if (_delegate && + [_delegate respondsToSelector:@selector(careContentsViewController:didSelectRowWithAssessmentEvent:)]) { + [_delegate careContentsViewController:self didSelectRowWithAssessmentEvent:selectedEvent]; + } + } + else if (type == OCKCarePlanActivityTypeReadOnly) { + if ([self delegateCustomizesRowReadOnlySelection]) { + [self.delegate careContentsViewController:self didSelectRowWithReadOnlyActivity:selectedEvent.activity]; + } else { + [self.navigationController pushViewController:[self detailViewControllerForActivity:selectedEvent.activity] animated:YES]; + } + } + + [tableView deselectRowAtIndexPath:indexPath animated:NO]; +} + +- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath { + if (_tableViewData[indexPath.section][indexPath.row].firstObject.activity.type == OCKCarePlanActivityTypeIntervention) { + return NO; + } else { + return YES; + } +} + + +#pragma mark - UITableViewDataSource + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return _tableViewData.count; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + return _tableViewData[section].count; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + NSArray *events = _tableViewData[indexPath.section][indexPath.row]; + + OCKCarePlanEvent *event = events.firstObject; + OCKCarePlanActivity *activity = event.activity; + OCKCarePlanActivityType type = activity.type; + + if (type == OCKCarePlanActivityTypeAssessment) { + + static NSString *CellIdentifier = @"SymptomTrackerCell"; + OCKSymptomTrackerTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; + if (!cell) { + cell = [[OCKSymptomTrackerTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault + reuseIdentifier:CellIdentifier]; + } + + cell.assessmentEvent = event; + + return cell; + } + else if (type == OCKCarePlanActivityTypeReadOnly) { + + static NSString *CellIdentifier = @"ReadOnlyCell"; + OCKReadOnlyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; + if (!cell) { + cell = [[OCKReadOnlyTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault + reuseIdentifier:CellIdentifier]; + } + + cell.readOnlyEvent = event; + + return cell; + } + else { + + static NSString *CellIdentifier = @"CareCardCell"; + OCKCareCardTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; + if (!cell) { + cell = [[OCKCareCardTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault + reuseIdentifier:CellIdentifier]; + } + + cell.interventionEvents = events; + cell.delegate = self; + + return cell; + } + + return nil; +} + +- (UIViewController *)detailViewControllerForActivity:(OCKCarePlanActivity *)activity { + OCKCareCardDetailViewController *detailViewController = [[OCKCareCardDetailViewController alloc] initWithIntervention:activity]; + return detailViewController; +} + +- (OCKCarePlanActivity *)activityForIndexPath:(NSIndexPath *)indexPath { + return _tableViewData[indexPath.section][indexPath.row].firstObject.activity; +} + +- (BOOL)delegateCustomizesRowSelection { + return self.delegate && [self.delegate respondsToSelector:@selector(careContentsViewController:didSelectRowWithInterventionActivity:)]; +} + +- (BOOL)delegateCustomizesRowReadOnlySelection { + return self.delegate && [self.delegate respondsToSelector:@selector(careContentsViewController:didSelectRowWithReadOnlyActivity:)]; +} + + +#pragma mark - OCKCareCardCellDelegate + +- (void)careCardTableViewCell:(OCKCareCardTableViewCell *)cell didUpdateFrequencyofInterventionEvent:(OCKCarePlanEvent *)event { + _lastSelectedEvent = event; + _lastSelectedActivity = event.activity; + + if (self.delegate && + [self.delegate respondsToSelector:@selector(careContentsViewController:didSelectButtonWithInterventionEvent:)]) { + [self.delegate careContentsViewController:self didSelectButtonWithInterventionEvent:event]; + } + + BOOL shouldHandleEventCompletion = YES; + if (self.delegate && + [self.delegate respondsToSelector:@selector(careContentsViewController:shouldHandleEventCompletionForInterventionActivity:)]) { + shouldHandleEventCompletion = [self.delegate careContentsViewController:self shouldHandleEventCompletionForInterventionActivity:event.activity]; + } + + if (shouldHandleEventCompletion) { + OCKCarePlanEventState state = (event.state == OCKCarePlanEventStateCompleted) ? OCKCarePlanEventStateNotCompleted : OCKCarePlanEventStateCompleted; + + [self.store updateEvent:event + withResult:nil + state:state + completion:^(BOOL success, OCKCarePlanEvent * _Nonnull event, NSError * _Nonnull error) { + NSAssert(success, error.localizedDescription); + dispatch_async(dispatch_get_main_queue(), ^{ + NSMutableArray *events = [cell.interventionEvents mutableCopy]; + [events replaceObjectAtIndex:event.occurrenceIndexOfDay withObject:event]; + cell.interventionEvents = events; + }); + }]; + } +} + +- (void)careCardTableViewCell:(OCKCareCardTableViewCell *)cell didSelectInterventionActivity:(OCKCarePlanActivity *)activity { + NSIndexPath *indexPath = [_tableView indexPathForCell:cell]; + OCKCarePlanActivity *selectedActivity = [self activityForIndexPath:indexPath]; + + _lastSelectedEvent = nil; + _lastSelectedActivity = selectedActivity; + + if ([self delegateCustomizesRowSelection]) { + [self.delegate careContentsViewController:self didSelectRowWithInterventionActivity:selectedActivity]; + } else { + [self.navigationController pushViewController:[self detailViewControllerForActivity:selectedActivity] animated:YES]; + } +} + + +#pragma mark - UIViewControllerPreviewingDelegate + +- (UIViewController *)previewingContext:(id )previewingContext viewControllerForLocation:(CGPoint)location { + NSIndexPath *indexPath = [_tableView indexPathForRowAtPoint:location]; + CGRect headerFrame = [_tableView headerViewForSection:0].frame; + + if (indexPath && + !CGRectContainsPoint(headerFrame, location) && + ![self delegateCustomizesRowSelection]) { + CGRect cellFrame = [_tableView cellForRowAtIndexPath:indexPath].frame; + previewingContext.sourceRect = cellFrame; + return [self detailViewControllerForActivity:[self activityForIndexPath:indexPath]]; + } + + return nil; +} + +- (void)previewingContext:(id )previewingContext commitViewController:(UIViewController *)viewControllerToCommit { + [self.navigationController pushViewController:viewControllerToCommit animated:YES]; +} + +@end diff --git a/CareKit/SymptomTracker/OCKSymptomTrackerWeekView.h b/CareKit/CareContents/OCKReadOnlyTableViewCell.h similarity index 84% rename from CareKit/SymptomTracker/OCKSymptomTrackerWeekView.h rename to CareKit/CareContents/OCKReadOnlyTableViewCell.h index 7fe908a6c..fde32079e 100644 --- a/CareKit/SymptomTracker/OCKSymptomTrackerWeekView.h +++ b/CareKit/CareContents/OCKReadOnlyTableViewCell.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -30,19 +30,16 @@ #import +#import "OCKTableViewCell.h" NS_ASSUME_NONNULL_BEGIN -@protocol OCKWeekViewDelegate; +@class OCKReadOnlyTableViewCell; -@interface OCKSymptomTrackerWeekView : UIView +@interface OCKReadOnlyTableViewCell : OCKTableViewCell -@property (nonatomic, weak, nullable) id delegate; - -@property (nonatomic, copy) NSArray *values; - -@property (nonatomic) NSInteger selectedIndex; +@property (nonatomic, copy) OCKCarePlanEvent *readOnlyEvent; @end diff --git a/CareKit/CareCard/OCKCareCardWeekView.m b/CareKit/CareContents/OCKReadOnlyTableViewCell.m similarity index 55% rename from CareKit/CareCard/OCKCareCardWeekView.m rename to CareKit/CareContents/OCKReadOnlyTableViewCell.m index bdf7576b3..108e09b1c 100644 --- a/CareKit/CareCard/OCKCareCardWeekView.m +++ b/CareKit/CareContents/OCKReadOnlyTableViewCell.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -29,194 +29,135 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE */ -#import "OCKCareCardWeekView.h" -#import "OCKWeekLabelsView.h" -#import "OCKHeartView.h" -#import "OCKHeartButton.h" -#import "OCKWeekViewController.h" +#import "OCKReadOnlyTableViewCell.h" +#import "OCKCarePlanActivity.h" +#import "OCKCarePlanActivity_Internal.h" +#import "OCKCarePlanEvent.h" #import "OCKHelpers.h" #import "OCKDefines_Private.h" +#import "OCKLabel.h" -static const CGFloat HeartButtonSize = 20.0; -static const CGFloat TopMargin = 15.0; -static const CGFloat LeadingMargin = 15.0; -static const CGFloat TrailingMargin = 15.0; +static const CGFloat TopMargin = 20.0; +static const CGFloat BottomMargin = 20.0; +static const CGFloat HorizontalMargin = 5.0; -@implementation OCKCareCardWeekView { - OCKWeekLabelsView *_weekView; - NSMutableArray *_heartButtons; +@interface OCKReadOnlyTableViewCell () + +@end + + +@implementation OCKReadOnlyTableViewCell { + OCKLabel *_titleLabel; + OCKLabel *_textLabel; NSMutableArray *_constraints; - UIStackView *_stackView; } -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - self.backgroundColor = [UIColor whiteColor]; - [self prepareView]; - } - return self; +- (void)setReadOnlyEvent:(OCKCarePlanEvent *)readOnlyEvent { + _readOnlyEvent = readOnlyEvent; + self.tintColor = _readOnlyEvent.activity.tintColor; + [self prepareView]; + } - (void)prepareView { - if (!_weekView) { - _weekView = [[OCKWeekLabelsView alloc] initWithFrame:CGRectZero]; - [self addSubview:_weekView]; - } + [super prepareView]; + + self.accessoryType = UITableViewCellAccessoryDisclosureIndicator; - if (!_heartButtons) { - _heartButtons = [NSMutableArray new]; - for (int i = 0; i < 7; i++) { - OCKHeartButton *heartButton = [[OCKHeartButton alloc] initWithFrame:CGRectMake(0, 0, HeartButtonSize, HeartButtonSize)]; - - OCKHeartView *heartView = [[OCKHeartView alloc] initWithFrame:CGRectMake(0, 0, HeartButtonSize + 10, HeartButtonSize + 10)]; - heartView.userInteractionEnabled = NO; - heartView.maskImage = self.smallMaskImage; - heartView.tintColor = self.tintColor; - - heartButton.heartView = heartView; - [heartButton addTarget:self - action:@selector(updateDayOfWeek:) - forControlEvents:UIControlEventTouchDown]; - - UILabel *dayLabel = (UILabel *)_weekView.weekLabels[i]; - heartButton.accessibilityLabel = [dayLabel accessibilityLabel]; - - [_heartButtons addObject:heartButton]; - } + if (!_titleLabel) { + _titleLabel = [OCKLabel new]; + _titleLabel.textStyle = UIFontTextStyleHeadline; + [self addSubview:_titleLabel]; } - if (!_stackView) { - _stackView = [[UIStackView alloc] initWithArrangedSubviews:_heartButtons]; - _stackView.distribution = UIStackViewDistributionEqualSpacing; - [self addSubview:_stackView]; + if (!_textLabel) { + _textLabel = [OCKLabel new]; + _textLabel.textColor = [UIColor lightGrayColor]; + _textLabel.textStyle = UIFontTextStyleSubheadline; + [self addSubview:_textLabel]; } + [self updateView]; [self setUpConstraints]; } +- (void)updateView { + _titleLabel.text = _readOnlyEvent.activity.title; + _textLabel.text = _readOnlyEvent.activity.text; +} + - (void)setUpConstraints { [NSLayoutConstraint deactivateConstraints:_constraints]; _constraints = [NSMutableArray new]; - _weekView.translatesAutoresizingMaskIntoConstraints = NO; - _stackView.translatesAutoresizingMaskIntoConstraints = NO; + _titleLabel.translatesAutoresizingMaskIntoConstraints = NO; + _textLabel.translatesAutoresizingMaskIntoConstraints = NO; + + [_titleLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal]; + + CGFloat LeadingMargin = self.separatorInset.left; + CGFloat TrailingMargin = (self.separatorInset.right > 0) ? self.separatorInset.right : 25; [_constraints addObjectsFromArray:@[ - [NSLayoutConstraint constraintWithItem:_weekView + [NSLayoutConstraint constraintWithItem:_titleLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 - constant:-TopMargin], - [NSLayoutConstraint constraintWithItem:_weekView + constant:TopMargin], + [NSLayoutConstraint constraintWithItem:_titleLabel attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeading multiplier:1.0 constant:LeadingMargin], - [NSLayoutConstraint constraintWithItem:_weekView - attribute:NSLayoutAttributeTrailing + [NSLayoutConstraint constraintWithItem:_textLabel + attribute:NSLayoutAttributeBaseline relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeTrailing + toItem:_titleLabel + attribute:NSLayoutAttributeBaseline multiplier:1.0 - constant:-TrailingMargin], - [NSLayoutConstraint constraintWithItem:_stackView + constant:0.0], + [NSLayoutConstraint constraintWithItem:_textLabel attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeLeading + toItem:_titleLabel + attribute:NSLayoutAttributeTrailing multiplier:1.0 - constant:LeadingMargin], - [NSLayoutConstraint constraintWithItem:_stackView + constant:HorizontalMargin], + [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeTrailing - relatedBy:NSLayoutRelationEqual - toItem:self + relatedBy:NSLayoutRelationGreaterThanOrEqual + toItem:_textLabel attribute:NSLayoutAttributeTrailing multiplier:1.0 - constant:-TrailingMargin], - [NSLayoutConstraint constraintWithItem:_stackView - attribute:NSLayoutAttributeTop - relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeTop + constant:TrailingMargin], + [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationGreaterThanOrEqual + toItem:_titleLabel + attribute:NSLayoutAttributeBottom multiplier:1.0 - constant:0.0], - [NSLayoutConstraint constraintWithItem:_stackView + constant:BottomMargin], + [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeBottom - relatedBy:NSLayoutRelationEqual - toItem:self + relatedBy:NSLayoutRelationGreaterThanOrEqual + toItem:_textLabel attribute:NSLayoutAttributeBottom multiplier:1.0 - constant:0.0] + constant:BottomMargin] ]]; [NSLayoutConstraint activateConstraints:_constraints]; } -- (void)setValues:(NSArray *)values { - _values = values; - - for (int i = 0; i < _values.count; i++) { - double value = [_values[i] doubleValue]; - _heartButtons[i].heartView.value = value; - - NSString *progress = [OCKPercentFormatter(0, 0) stringFromNumber:[NSNumber numberWithDouble:value]]; - _heartButtons[i].accessibilityValue = [NSString stringWithFormat:OCKLocalizedString(@"AX_WEEK_BUTTON_PROGRESS", nil), progress]; - - if (self.delegate && - [self.delegate respondsToSelector:@selector(weekViewCanSelectDayAtIndex:)]) { - if (![self.delegate weekViewCanSelectDayAtIndex:(NSUInteger)i]) { - _heartButtons[i].accessibilityTraits |= UIAccessibilityTraitNotEnabled; - } - } - } -} - -- (void)setSelectedIndex:(NSInteger)selectedIndex { - _selectedIndex = selectedIndex; - [_weekView highlightDay:selectedIndex]; - for (int i = 0; i < [_heartButtons count]; i++) { - UIAccessibilityTraits axTraits = UIAccessibilityTraitButton | (i == selectedIndex ? UIAccessibilityTraitSelected : 0); - [_heartButtons[i] setAccessibilityTraits:axTraits]; - } -} - -- (void)setSmallMaskImage:(UIImage *)smallMaskImage { - _smallMaskImage = smallMaskImage; - for (OCKHeartButton *button in _heartButtons) { - button.heartView.maskImage = _smallMaskImage; - } - [self prepareView]; -} - -- (void)updateDayOfWeek:(id)sender { - OCKHeartButton *button = (OCKHeartButton *)sender; - NSInteger dayOfWeek = [_heartButtons indexOfObject:button]; - self.selectedIndex = dayOfWeek; - - if (self.delegate && - [self.delegate respondsToSelector:@selector(weekViewSelectionDidChange:)]) { - [self.delegate weekViewSelectionDidChange:self]; - } -} - - (void)layoutSubviews { [super layoutSubviews]; [self setUpConstraints]; } -- (void)tintColorDidChange { - [super tintColorDidChange]; - for (OCKHeartButton *button in _heartButtons) { - button.heartView.tintColor = self.tintColor; - } - _weekView.tintColor = self.tintColor; -} - @end diff --git a/CareKit/CareKit.h b/CareKit/CareKit.h index 7f52bcd7c..bd25ad341 100644 --- a/CareKit/CareKit.h +++ b/CareKit/CareKit.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -39,11 +39,22 @@ #import #import #import +#import + +// Glyph +#import + +// Patient +#import +#import // Care Card #import #import +// Care Content +#import + // Symptom Tracker #import @@ -53,13 +64,18 @@ #import #import #import +#import #import #import // Connect #import #import +#import #import // PDF #import + +// Colors +#import diff --git a/CareKit/CareKit_Private.h b/CareKit/CareKit_Private.h index 717bc4562..28bc58bdd 100644 --- a/CareKit/CareKit_Private.h +++ b/CareKit/CareKit_Private.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/CarePlan/NSDateComponents+CarePlan.h b/CareKit/CarePlan/NSDateComponents+CarePlan.h index 5edefbd7e..d89bc835e 100644 --- a/CareKit/CarePlan/NSDateComponents+CarePlan.h +++ b/CareKit/CarePlan/NSDateComponents+CarePlan.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/CarePlan/NSDateComponents+CarePlan.m b/CareKit/CarePlan/NSDateComponents+CarePlan.m index 964e3d4ea..b74e235a7 100644 --- a/CareKit/CarePlan/NSDateComponents+CarePlan.m +++ b/CareKit/CarePlan/NSDateComponents+CarePlan.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/CarePlan/NSDateComponents+CarePlanInternal.h b/CareKit/CarePlan/NSDateComponents+CarePlanInternal.h index 889681157..6aba15f62 100644 --- a/CareKit/CarePlan/NSDateComponents+CarePlanInternal.h +++ b/CareKit/CarePlan/NSDateComponents+CarePlanInternal.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/CarePlan/OCKCarePlanActivity.h b/CareKit/CarePlan/OCKCarePlanActivity.h index aef691f48..1ae75b28f 100644 --- a/CareKit/CarePlan/OCKCarePlanActivity.h +++ b/CareKit/CarePlan/OCKCarePlanActivity.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -31,6 +31,7 @@ #import #import +#import #import @@ -49,7 +50,9 @@ typedef NS_ENUM(NSInteger, OCKCarePlanActivityType) { /** Do something related to the treatment. */ OCKCarePlanActivityTypeIntervention, /** Perform a task in the app. */ - OCKCarePlanActivityTypeAssessment + OCKCarePlanActivityTypeAssessment, + /** ReadOnly content for intervention and assessment. */ + OCKCarePlanActivityTypeReadOnly }; @@ -77,6 +80,8 @@ OCK_CLASS_AVAILABLE @param imageURL Image for the intervention activity. @param schedule The schedule for the intervention activity. @param userInfo Save any additional objects that comply with the NSCoding protocol. + @param optional Whether or not the activity is optional. + @return Initialized OCKCarePlanActivity instance. */ @@ -88,7 +93,8 @@ OCK_CLASS_AVAILABLE instructions:(nullable NSString *)instructions imageURL:(nullable NSURL *)imageURL schedule:(OCKCareSchedule *)schedule - userInfo:(nullable NSDictionary *)userInfo; + userInfo:(nullable NSDictionary *)userInfo + optional:(BOOL)optional; /** Convienience initializer for the assessment activity type. @@ -103,6 +109,8 @@ OCK_CLASS_AVAILABLE @param resultResettable Whether or not to allow the user to retake the assessment. @param schedule The schedule for the assessment activity. @param userInfo Save any additional objects that comply with the NSCoding protocol. + @param thresholds An array of array of thresholds to apply to numeric result values. + @param optional Whether or not the activity is optional. @return Initialized OCKCarePlanActivity instance. */ @@ -113,7 +121,47 @@ OCK_CLASS_AVAILABLE tintColor:(nullable UIColor *)tintColor resultResettable:(BOOL)resultResettable schedule:(OCKCareSchedule *)schedule - userInfo:(nullable NSDictionary *)userInfo; + userInfo:(nullable NSDictionary *)userInfo + thresholds:(nullable NSArray *> *)thresholds + optional:(BOOL)optional; + + ++ (instancetype)assessmentWithIdentifier:(NSString *)identifier + groupIdentifier:(nullable NSString *)groupIdentifier + title:(NSString *)title + text:(nullable NSString *)text + tintColor:(nullable UIColor *)tintColor + resultResettable:(BOOL)resultResettable + schedule:(OCKCareSchedule *)schedule + userInfo:(nullable NSDictionary *)userInfo + optional:(BOOL)optional; + +/** + Convienience initializer for read only activity type. + This initializer covers necessary attributes for building read only activity. + + @param identifier Unique identifier string. + @param groupIdentifier Group identifier string. + You can use the identifier to group similar activities, but they will all be grouped under Read Only section in the table view. + @param title The title for the read only activity. + @param text A descriptive text for the read only activity. + @param instructions Additional instructions for the read only activity. + @param imageURL Image for the read only activity. + @param schedule The schedule for the read only activity. + @param userInfo Save any additional objects that comply with the NSCoding protocol. + + + @return Initialized OCKCarePlanActivity instance. + */ + ++ (instancetype)readOnlyWithIdentifier:(NSString *)identifier + groupIdentifier:(nullable NSString *)groupIdentifier + title:(NSString *)title + text:(nullable NSString *)text + instructions:(nullable NSString *)instructions + imageURL:(nullable NSURL *)imageURL + schedule:(OCKCareSchedule *)schedule + userInfo:(nullable NSDictionary *)userInfo; /** Default initialzer for OCKCarePlanActivity. @@ -130,6 +178,8 @@ OCK_CLASS_AVAILABLE @param schedule The schedule for the activity. @param resultResettable Whether or not to allow the user to retake the assessment. @param userInfo Save any addtional NSCoding complianced objects. + @param thresholds An array of array of thresholds to apply to numeric result values. + @param optional Whether or not the activity is optional. @return Initialized OCKCarePlanActivity instance. */ @@ -143,7 +193,23 @@ OCK_CLASS_AVAILABLE imageURL:(nullable NSURL *)imageURL schedule:(OCKCareSchedule *)schedule resultResettable:(BOOL)resultResettable - userInfo:(nullable NSDictionary> *)userInfo NS_DESIGNATED_INITIALIZER; + userInfo:(nullable NSDictionary> *)userInfo + thresholds:(nullable NSArray *> *)thresholds + optional:(BOOL)optional NS_DESIGNATED_INITIALIZER; + + + +- (instancetype)initWithIdentifier:(NSString *)identifier + groupIdentifier:(nullable NSString *)groupIdentifier + type:(OCKCarePlanActivityType)type + title:(NSString *)title + text:(nullable NSString *)text + tintColor:(nullable UIColor *)tintColor + instructions:(nullable NSString *)instructions + imageURL:(nullable NSURL *)imageURL + schedule:(OCKCareSchedule *)schedule + resultResettable:(BOOL)resultResettable + userInfo:(nullable NSDictionary> *)userInfo; /** Unique identifier string. @@ -215,6 +281,23 @@ OCK_CLASS_AVAILABLE */ @property (nonatomic, copy, readonly, nullable) NSDictionary> *userInfo; +/** + An optional array of array of thresholds pertianing to the values of associated results objects. + These thresholds are checked against any numeric result values when an event's evaluateNumericThresholds() + method is called. + Each array of thresholds corresponds to one of the values in the result's value array. + Because of this, thresholds can have either 1 or 2 sub-arrays. + */ +@property (nonatomic, copy, readonly, nullable) NSArray *> *thresholds; + +/** + Whether or not the activity is optional. + + An optional activity does not count towards total completion. + Default value is NO. + */ +@property (nonatomic, readonly) BOOL optional; + @end NS_ASSUME_NONNULL_END diff --git a/CareKit/CarePlan/OCKCarePlanActivity.m b/CareKit/CarePlan/OCKCarePlanActivity.m index 6c5d2c659..1a06fb749 100644 --- a/CareKit/CarePlan/OCKCarePlanActivity.m +++ b/CareKit/CarePlan/OCKCarePlanActivity.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -51,11 +51,20 @@ - (instancetype)initWithIdentifier:(NSString *)identifier imageURL:(NSURL *)imageURL schedule:(OCKCareSchedule *)schedule resultResettable:(BOOL)resultResettable - userInfo:(NSDictionary *)userInfo { - + userInfo:(NSDictionary> *)userInfo + thresholds:(NSArray *> *)thresholds + optional:(BOOL)optional { OCKThrowInvalidArgumentExceptionIfNil(identifier); OCKThrowInvalidArgumentExceptionIfNil(schedule); + if ((thresholds) && (thresholds.count > 2)) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Thresholds array cannot have more than 2 elements." userInfo:nil]; + } + + if ((type == OCKCarePlanActivityTypeReadOnly) && (!optional)) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Activity type ReadOnly has to be optional." userInfo:nil]; + } + self = [super init]; if (self) { _identifier = [identifier copy]; @@ -69,10 +78,63 @@ - (instancetype)initWithIdentifier:(NSString *)identifier _schedule = schedule; _resultResettable = resultResettable; _userInfo = [userInfo copy]; + _thresholds = thresholds; + _optional = optional; } return self; } +- (instancetype)initWithIdentifier:(NSString *)identifier + groupIdentifier:(NSString *)groupIdentifier + type:(OCKCarePlanActivityType)type + title:(NSString *)title + text:(NSString *)text + tintColor:(UIColor *)tintColor + instructions:(NSString *)instructions + imageURL:(NSURL *)imageURL + schedule:(OCKCareSchedule *)schedule + resultResettable:(BOOL)resultResettable + userInfo:(NSDictionary *)userInfo { + return [self initWithIdentifier:identifier + groupIdentifier:groupIdentifier + type:type + title:title + text:text + tintColor:tintColor + instructions:instructions + imageURL:imageURL + schedule:schedule + resultResettable:resultResettable + userInfo:userInfo + thresholds:nil + optional:NO]; +} + ++ (instancetype)assessmentWithIdentifier:(NSString *)identifier + groupIdentifier:(NSString *)groupIdentifier + title:(NSString *)title + text:(NSString *)text + tintColor:(UIColor *)tintColor + resultResettable:(BOOL)resultResettable + schedule:(OCKCareSchedule *)schedule + userInfo:(NSDictionary *)userInfo + thresholds:(NSArray *> *)thresholds + optional:(BOOL)optional { + return [[self alloc] initWithIdentifier:identifier + groupIdentifier:groupIdentifier + type:OCKCarePlanActivityTypeAssessment + title:title + text:text + tintColor:tintColor + instructions:nil + imageURL:nil + schedule:schedule + resultResettable:resultResettable + userInfo:userInfo + thresholds:thresholds + optional:optional]; +} + + (instancetype)assessmentWithIdentifier:(NSString *)identifier groupIdentifier:(NSString *)groupIdentifier title:(NSString *)title @@ -80,7 +142,8 @@ + (instancetype)assessmentWithIdentifier:(NSString *)identifier tintColor:(UIColor *)tintColor resultResettable:(BOOL)resultResettable schedule:(OCKCareSchedule *)schedule - userInfo:(NSDictionary *)userInfo { + userInfo:(NSDictionary *)userInfo + optional:(BOOL)optional { return [[self alloc] initWithIdentifier:identifier groupIdentifier:groupIdentifier @@ -92,7 +155,9 @@ + (instancetype)assessmentWithIdentifier:(NSString *)identifier imageURL:nil schedule:schedule resultResettable:resultResettable - userInfo:userInfo]; + userInfo:userInfo + thresholds:nil + optional:optional]; } + (instancetype)interventionWithIdentifier:(NSString *)identifier @@ -103,7 +168,8 @@ + (instancetype)interventionWithIdentifier:(NSString *)identifier instructions:(NSString *)instructions imageURL:(NSURL *)imageURL schedule:(OCKCareSchedule *)schedule - userInfo:(NSDictionary *)userInfo { + userInfo:(NSDictionary *)userInfo + optional:(BOOL)optional { return [[self alloc] initWithIdentifier:identifier groupIdentifier:groupIdentifier @@ -115,7 +181,33 @@ + (instancetype)interventionWithIdentifier:(NSString *)identifier imageURL:imageURL schedule:schedule resultResettable:YES - userInfo:userInfo]; + userInfo:userInfo + thresholds:nil + optional:optional]; +} + ++ (instancetype)readOnlyWithIdentifier:(NSString *)identifier + groupIdentifier:(nullable NSString *)groupIdentifier + title:(NSString *)title + text:(nullable NSString *)text + instructions:(nullable NSString *)instructions + imageURL:(nullable NSURL *)imageURL + schedule:(OCKCareSchedule *)schedule + userInfo:(nullable NSDictionary *)userInfo { + + return [[self alloc] initWithIdentifier:identifier + groupIdentifier:groupIdentifier + type:OCKCarePlanActivityTypeReadOnly + title:title + text:text + tintColor:nil + instructions:instructions + imageURL:imageURL + schedule:schedule + resultResettable:NO + userInfo:userInfo + thresholds:nil + optional:YES]; } - (instancetype)initWithCoreDataObject:(OCKCDCarePlanActivity *)cdObject { @@ -131,7 +223,9 @@ - (instancetype)initWithCoreDataObject:(OCKCDCarePlanActivity *)cdObject { imageURL:OCKURLFromBookmarkData(cdObject.imageURL) schedule:cdObject.schedule resultResettable:cdObject.resultResettable.boolValue - userInfo:cdObject.userInfo]; + userInfo:cdObject.userInfo + thresholds:cdObject.thresholds + optional:cdObject.optional.boolValue]; return self; } @@ -154,6 +248,8 @@ - (instancetype)initWithCoder:(NSCoder *)coder { OCK_DECODE_URL_BOOKMARK(coder, imageURL); OCK_DECODE_BOOL(coder, resultResettable); OCK_DECODE_OBJ_CLASS(coder, userInfo, NSDictionary); + OCK_DECODE_OBJ_CLASS(coder, thresholds, NSArray *>); + OCK_DECODE_BOOL(coder, optional); } return self; } @@ -170,6 +266,8 @@ - (void)encodeWithCoder:(NSCoder *)coder { OCK_ENCODE_URL_BOOKMARK(coder, imageURL); OCK_ENCODE_BOOL(coder, resultResettable); OCK_ENCODE_OBJ(coder, userInfo); + OCK_ENCODE_OBJ(coder, thresholds); + OCK_ENCODE_BOOL(coder, optional); } - (BOOL)isEqual:(id)object { @@ -187,8 +285,9 @@ - (BOOL)isEqual:(id)object { OCKEqualObjects(self.groupIdentifier, castObject.groupIdentifier) && OCKEqualObjects(self.imageURL, castObject.imageURL) && (self.resultResettable == castObject.resultResettable) && - OCKEqualObjects(self.userInfo, castObject.userInfo) - ); + OCKEqualObjects(self.userInfo, castObject.userInfo) && + OCKEqualObjects(self.thresholds, castObject.thresholds) && + (self.optional == castObject.optional)); } - (instancetype)copyWithZone:(NSZone *)zone { @@ -204,9 +303,15 @@ - (instancetype)copyWithZone:(NSZone *)zone { item->_imageURL = _imageURL; item->_resultResettable = _resultResettable; item->_userInfo = _userInfo; + item->_thresholds = [_thresholds copy]; + item->_optional = _optional; return item; } +- (NSUInteger)hash { + return [self.identifier hash]; +} + @end @@ -230,6 +335,8 @@ - (instancetype)initWithEntity:(NSEntityDescription *)entity self.type = @(item.type); self.userInfo = item.userInfo; self.resultResettable = @(item.resultResettable); + self.thresholds = item.thresholds; + self.optional = @(item.optional); } return self; } @@ -249,5 +356,7 @@ - (NSString *)description { @dynamic type; @dynamic resultResettable; @dynamic userInfo; +@dynamic thresholds; +@dynamic optional; @end diff --git a/CareKit/CarePlan/OCKCarePlanActivity_Internal.h b/CareKit/CarePlan/OCKCarePlanActivity_Internal.h index 75e61d795..2da7bf94d 100644 --- a/CareKit/CarePlan/OCKCarePlanActivity_Internal.h +++ b/CareKit/CarePlan/OCKCarePlanActivity_Internal.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -68,6 +68,8 @@ insertIntoManagedObjectContext:(nullable NSManagedObjectContext *)context @property (nullable, nonatomic, retain) NSNumber *type; @property (nullable, nonatomic, retain) NSNumber *resultResettable; @property (nullable, nonatomic, retain) NSDictionary *userInfo; +@property (nullable, nonatomic, retain) NSArray *> *thresholds; +@property (nullable, nonatomic, retain) NSNumber *optional; @end diff --git a/CareKit/CarePlan/OCKCarePlanEvent.h b/CareKit/CarePlan/OCKCarePlanEvent.h index ce9724b49..b19e15cad 100644 --- a/CareKit/CarePlan/OCKCarePlanEvent.h +++ b/CareKit/CarePlan/OCKCarePlanEvent.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -61,7 +61,7 @@ typedef NS_ENUM(NSInteger, OCKCarePlanEventState) { Use OCKCarePlanStore API to update an event's state and change its result. */ OCK_CLASS_AVAILABLE -@interface OCKCarePlanEvent : NSObject +@interface OCKCarePlanEvent : NSObject - (instancetype)init NS_UNAVAILABLE; @@ -100,6 +100,11 @@ OCK_CLASS_AVAILABLE */ @property (nonatomic, readonly, nullable) OCKCarePlanEventResult *result; +/** + Check numeric thresholds on a given event. + */ +- (NSArray *> *)evaluateNumericThresholds; + @end NS_ASSUME_NONNULL_END diff --git a/CareKit/CarePlan/OCKCarePlanEvent.m b/CareKit/CarePlan/OCKCarePlanEvent.m index fd292fd05..753193fbf 100644 --- a/CareKit/CarePlan/OCKCarePlanEvent.m +++ b/CareKit/CarePlan/OCKCarePlanEvent.m @@ -1,5 +1,6 @@ + /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -99,6 +100,56 @@ - (NSString *)description { @(self.occurrenceIndexOfDay)]; } +- (NSArray *> *)evaluateNumericThresholds { + + NSArray *values = self.result.values; + NSArray *> *thresholds = self.activity.thresholds; + + NSMutableArray *> *triggeredThresholds = [NSMutableArray new]; + + if (values && thresholds) { + if (values.count != thresholds.count) { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Results object must have the same number of values as the event's thresholds." userInfo:nil]; + } + + for (int valueIndex = 0; valueIndex < values.count; valueIndex++) { + NSIndexSet *triggeredIndices = [thresholds[valueIndex] indexesOfObjectsPassingTest:^BOOL(OCKCarePlanThreshold * _Nonnull threshold, NSUInteger idx, BOOL * _Nonnull stop) { + return [threshold evaluateThresholdForValue:values[valueIndex]]; + }]; + + NSArray *newThresholds = [thresholds[valueIndex] objectsAtIndexes:triggeredIndices]; + + [triggeredThresholds addObject:newThresholds]; + } + } + + return [triggeredThresholds copy]; +} + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)coder { + self = [super init]; + if (self) { + OCK_DECODE_INTEGER(coder, occurrenceIndexOfDay); + OCK_DECODE_INTEGER(coder, numberOfDaysSinceStart); + OCK_DECODE_ENUM(coder, state); + OCK_DECODE_OBJ_CLASS(coder, activity, OCKCarePlanActivity); + OCK_DECODE_OBJ_CLASS(coder, result, OCKCarePlanEventResult); + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + OCK_ENCODE_INTEGER(coder, occurrenceIndexOfDay); + OCK_ENCODE_INTEGER(coder, numberOfDaysSinceStart); + OCK_ENCODE_ENUM(coder, state); + OCK_ENCODE_OBJ(coder, activity); + OCK_ENCODE_OBJ(coder, result); +} + @end @@ -126,6 +177,10 @@ - (instancetype)initWithEntity:(NSEntityDescription *)entity - (void)updateWithState:(OCKCarePlanEventState)state result:(OCKCDCarePlanEventResult *)result { self.state = @(state); + if (result.values && self.activity.thresholds && (self.activity.thresholds.count != result.values.count)) { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Results object must have the same number of values as the event's thresholds." userInfo:nil]; + } + if (result && self.result) { [self.result updateWithResult:result]; } else { diff --git a/CareKit/CarePlan/OCKCarePlanEventResult.h b/CareKit/CarePlan/OCKCarePlanEventResult.h index 4c1b82e0e..b556b79f5 100644 --- a/CareKit/CarePlan/OCKCarePlanEventResult.h +++ b/CareKit/CarePlan/OCKCarePlanEventResult.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -40,7 +40,7 @@ NS_ASSUME_NONNULL_BEGIN Create an instance of this class and attach it to an event using the OCKCarePlanStore API. */ OCK_CLASS_AVAILABLE -@interface OCKCarePlanEventResult : NSObject +@interface OCKCarePlanEventResult : NSObject - (instancetype)init NS_UNAVAILABLE; @@ -51,9 +51,17 @@ OCK_CLASS_AVAILABLE @param valueString Value string to be displayed to the user. @param unitString Unit string to be displayed to the user. @param userInfo Dictionary to save any additional objects that comply with the NSCoding protocol. + @param values The array of numeric values associated with the result. + These values can be used to evaluate thresholds. @return Initialized instance. */ + +- (instancetype)initWithValueString:(NSString *)valueString + unitString:(NSString *)unitString + userInfo:(nullable NSDictionary> *)userInfo + values:(nullable NSArray *)values; + - (instancetype)initWithValueString:(NSString *)valueString unitString:(nullable NSString *)unitString userInfo:(nullable NSDictionary> *)userInfo; @@ -73,6 +81,11 @@ OCK_CLASS_AVAILABLE */ @property (nonatomic, copy, readonly, nullable) NSString *unitString; +/** + An array of values associated with the result to use for threshold checking (max length 2). + */ +@property (nonatomic, copy, readonly, nullable) NSArray *values; + /** Use this dictionary to store objects that comply with the NSCoding protocol. */ diff --git a/CareKit/CarePlan/OCKCarePlanEventResult.m b/CareKit/CarePlan/OCKCarePlanEventResult.m index c7d1b0819..bf1d24ca2 100644 --- a/CareKit/CarePlan/OCKCarePlanEventResult.m +++ b/CareKit/CarePlan/OCKCarePlanEventResult.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -36,6 +36,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE @implementation OCKCarePlanEventResult { NSString *_valueString; NSString *_unitString; + NSArray *_values; HKSampleType *_sampleType; NSUUID *_sampleUUID; @@ -53,19 +54,34 @@ - (instancetype)init { } - (instancetype)initWithValueString:(NSString *)valueString - unitString:(nullable NSString *)unitString - userInfo:(nullable NSDictionary *)userInfo { + unitString:(NSString *)unitString + userInfo:(nullable NSDictionary> *)userInfo + values:(nullable NSArray *)values { OCKThrowInvalidArgumentExceptionIfNil(valueString); + if ((values) && (values.count > 2)) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Values cannot have more than 2 elements." userInfo:nil]; + } + self = [super init]; if (self) { _valueString = valueString; _unitString = unitString; _userInfo = userInfo; _creationDate = [NSDate date]; + _values = values; } return self; } +- (instancetype)initWithValueString:(NSString *)valueString + unitString:(nullable NSString *)unitString + userInfo:(nullable NSDictionary *)userInfo { + return [self initWithValueString:valueString + unitString:unitString + userInfo:userInfo + values:nil]; +} + - (instancetype)initWithSample:(HKSample *)sample quantityStringFormatter:(nullable NSNumberFormatter *)quantityStringFormatter displayUnit:(nullable HKUnit *)displayUnit @@ -237,7 +253,7 @@ - (NSString *)valueString { HKUnit *unit = [self preferredUnit]; double doubleValue = [sample.quantity doubleValueForUnit:unit]; string = [self stringForDoubleValue:doubleValue]; - }else if ([_sample isKindOfClass:[HKCorrelation class]]) { + } else if ([_sample isKindOfClass:[HKCorrelation class]]) { HKCorrelation *correlation = (HKCorrelation *)_sample; HKQuantityType *systolicType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodPressureSystolic]; HKQuantityType *diastolicType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodPressureDiastolic]; @@ -269,6 +285,27 @@ - (NSString *)unitString { return nil; } +- (NSArray *)values { + if (_values) { + return _values; + } else if (_sample) { + if ([_sample isKindOfClass:[HKQuantitySample class]]) { + HKQuantitySample *sample = (HKQuantitySample *)_sample; + return @[@([sample.quantity doubleValueForUnit:[self preferredUnit]])]; + } else if ([_sample isKindOfClass:[HKCorrelation class]]) { + HKCorrelation *correlation = (HKCorrelation *)_sample; + HKQuantityType *systolicType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodPressureSystolic]; + HKQuantityType *diastolicType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodPressureDiastolic]; + + HKQuantitySample *systolicSample = [correlation objectsForType:systolicType].anyObject; + HKQuantitySample *diastolicSample = [correlation objectsForType:diastolicType].anyObject; + return @[@([diastolicSample.quantity doubleValueForUnit:[self preferredUnit]]), + @([systolicSample.quantity doubleValueForUnit:[self preferredUnit]])]; + } + } + return nil; +} + - (instancetype)initWithCoreDataObject:(OCKCDCarePlanEventResult *)cdObject { NSParameterAssert(cdObject); @@ -285,7 +322,9 @@ - (instancetype)initWithCoreDataObject:(OCKCDCarePlanEventResult *)cdObject { _userInfo = cdObject.userInfo; } else { self = [self initWithValueString:cdObject.valueString - unitString:cdObject.unitString userInfo:cdObject.userInfo]; + unitString:cdObject.unitString + userInfo:cdObject.userInfo + values:cdObject.values]; } if (self) { @@ -340,10 +379,63 @@ - (BOOL)isEqual:(id)object { OCKEqualObjects(self.unitStringKeys, castObject.unitStringKeys) && OCKEqualObjects(self.quantityStringFormatter, castObject.quantityStringFormatter) && OCKEqualObjects(self.categoryValueStringKeys, castObject.categoryValueStringKeys) && - OCKEqualObjects(self.userInfo, castObject.userInfo) + OCKEqualObjects(self.userInfo, castObject.userInfo) && + OCKEqualObjects(self.values, castObject.values) ); } +- (instancetype)copyWithZone:(NSZone *)zone { + OCKCarePlanEventResult *item = [[[self class] allocWithZone:zone] init]; + item->_creationDate = _creationDate; + item->_valueString = [_valueString copy]; + item->_unitString = [_unitString copy]; + item->_sampleType = _sampleType; + item->_sampleUUID = _sampleUUID; + item->_displayUnit = _displayUnit; + item->_unitStringKeys = _unitStringKeys; + item->_quantityStringFormatter = _quantityStringFormatter; + item->_categoryValueStringKeys = _categoryValueStringKeys; + item->_userInfo = _userInfo; + item->_values = [_values copy]; + return item; +} + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)coder { + self = [super init]; + if (self) { + OCK_DECODE_OBJ_CLASS(coder, creationDate, NSDate); + OCK_DECODE_OBJ_CLASS(coder, valueString, NSString); + OCK_DECODE_OBJ_CLASS(coder, unitString, NSString); + OCK_DECODE_OBJ_CLASS(coder, sampleType, HKSampleType); + OCK_DECODE_OBJ_CLASS(coder, sampleUUID, NSUUID); + OCK_DECODE_OBJ_CLASS(coder, displayUnit, HKUnit); + OCK_DECODE_OBJ_CLASS(coder, unitStringKeys, NSDictionary); + OCK_DECODE_OBJ_CLASS(coder, quantityStringFormatter, NSNumberFormatter); + OCK_DECODE_OBJ_CLASS(coder, categoryValueStringKeys, NSDictionary); + OCK_DECODE_OBJ_CLASS(coder, userInfo, NSDictionary); + OCK_DECODE_OBJ_CLASS(coder, values, NSArray); + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + OCK_ENCODE_OBJ(coder, creationDate); + OCK_ENCODE_OBJ(coder, valueString); + OCK_ENCODE_OBJ(coder, unitString); + OCK_ENCODE_OBJ(coder, sampleType); + OCK_ENCODE_OBJ(coder, sampleUUID); + OCK_ENCODE_OBJ(coder, displayUnit); + OCK_ENCODE_OBJ(coder, unitStringKeys); + OCK_ENCODE_OBJ(coder, quantityStringFormatter); + OCK_ENCODE_OBJ(coder, categoryValueStringKeys); + OCK_ENCODE_OBJ(coder, userInfo); + OCK_ENCODE_OBJ(coder, values); +} + @end @@ -361,6 +453,7 @@ - (instancetype)initWithEntity:(NSEntityDescription *)entity self.valueString = result.valueString; self.unitString = result.unitString; self.userInfo = result.userInfo; + self.values = result.values; self.sampleType = result.sampleType; self.displayUnit = result.displayUnit; @@ -379,6 +472,7 @@ - (void)updateWithResult:(OCKCDCarePlanEventResult *)result { self.valueString = result.valueString; self.unitString = result.unitString; self.userInfo = result.userInfo; + self.values = result.values; self.sampleType = result.sampleType; self.displayUnit = result.displayUnit; @@ -397,6 +491,7 @@ @implementation OCKCDCarePlanEventResult (CoreDataProperties) @dynamic valueString; @dynamic unitString; @dynamic userInfo; +@dynamic values; @dynamic event; @dynamic quantityStringFormatter; diff --git a/CareKit/CarePlan/OCKCarePlanEventResult_Internal.h b/CareKit/CarePlan/OCKCarePlanEventResult_Internal.h index ab4f9b576..ac672d007 100644 --- a/CareKit/CarePlan/OCKCarePlanEventResult_Internal.h +++ b/CareKit/CarePlan/OCKCarePlanEventResult_Internal.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -61,6 +61,7 @@ insertIntoManagedObjectContext:(nullable NSManagedObjectContext *)context @property (nullable, nonatomic, retain) NSString *valueString; @property (nullable, nonatomic, retain) NSString *unitString; @property (nullable, nonatomic, retain) id userInfo; +@property (nullable, nonatomic, retain) NSArray *values; @property (nullable, nonatomic, retain) id quantityStringFormatter; @property (nullable, nonatomic, retain) id sampleUUID; diff --git a/CareKit/CarePlan/OCKCarePlanEvent_Internal.h b/CareKit/CarePlan/OCKCarePlanEvent_Internal.h index 5b2d8d021..76beef958 100644 --- a/CareKit/CarePlan/OCKCarePlanEvent_Internal.h +++ b/CareKit/CarePlan/OCKCarePlanEvent_Internal.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/CarePlan/OCKCarePlanStore.h b/CareKit/CarePlan/OCKCarePlanStore.h index 37114da1a..c59ca1661 100644 --- a/CareKit/CarePlan/OCKCarePlanStore.h +++ b/CareKit/CarePlan/OCKCarePlanStore.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -38,6 +38,7 @@ NS_ASSUME_NONNULL_BEGIN @class OCKCarePlanStore; @class OCKCarePlanActivity; @class OCKCarePlanEvent; +@class OCKPatient; /** Implement this delegate to subscribe to the notification of changes in this store. @@ -87,25 +88,63 @@ OCK_CLASS_AVAILABLE - (instancetype)init NS_UNAVAILABLE; +/** + The initializer requires an identifier and a local directory URL. + The directory in the URL must exist, otherwise this initializer raises an exception. + + @param URL The directory for the store to save its database file. + @param identifier A unqiue identifier for the store. + @param patient The patient associated with the store. + + @return An instance of the store. + */ +- (instancetype)initWithPersistenceDirectoryURL:(NSURL *)URL + identifier:(NSString *)identifier + patient:(nullable OCKPatient *)patient; + /** The initializer requires a local directory URL. The directory in the URL must exist, otherwise this initializer raises an exception. - @param URL The directory for the store to save its database file. + @param URL The directory for the store to save its database file. @return An instance of the store. */ -- (instancetype)initWithPersistenceDirectoryURL:(NSURL *)URL NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithPersistenceDirectoryURL:(NSURL *)URL; + + +/** + A unique identifier for the store at the directory url. + */ +@property (nonatomic, copy, readonly) NSString *identifier; + +/** + The persistence directory url for the store to save its database file. + */ +@property (nonatomic, copy, readonly) NSURL *directoryURL; + +/** + The patient object associated with this care plan store. + */ +@property (nonatomic, copy, readonly, nullable) OCKPatient *patient; /** You can use the delegate to subscribe to notifications of changes to the store. */ -@property (nonatomic, weak) id delegate; +@property (nonatomic, weak, nullable) id delegate; /** You can use the watch delegate to subscribe a watch app to notifications of changes to the store. */ -@property (nonatomic, weak) id watchDelegate; +@property (nonatomic, weak, nullable) id watchDelegate; + +/** + The cloud bridge delegate is used to support the Medable, IBM Watson, or CloudMine cloud bridge. + + Developers should not use this delegate. Use `delegate` property instead. + */ +@property (nonatomic, weak, nullable) id cloudBridgeDelegate; + /** Add an activity to this store. @@ -247,6 +286,17 @@ Get all the `OCKCarePlanEvent` objects for a given date. handler:(void (^)(OCKCarePlanEvent * _Nullable event, BOOL *stop))handler completion:(void (^)(BOOL completed, NSError * _Nullable error))completion; +/** + Check the adherance threshold for an activity on a given day. + + @param activity The activity to check. + @param date The date to check. + @param completion A completion block that returns the result of the operation and a possible triggered threshold object. + */ +- (void)evaluateAdheranceThresholdForActivity:(OCKCarePlanActivity *)activity + date:(NSDateComponents *)date + completion:(void (^)(BOOL success, OCKCarePlanThreshold * _Nullable threshold, NSError * _Nullable error))completion; + @end NS_ASSUME_NONNULL_END diff --git a/CareKit/CarePlan/OCKCarePlanStore.m b/CareKit/CarePlan/OCKCarePlanStore.m index 1aeb50b73..228c01e5d 100644 --- a/CareKit/CarePlan/OCKCarePlanStore.m +++ b/CareKit/CarePlan/OCKCarePlanStore.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -48,7 +48,9 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL - options:@{NSFileProtectionKey: NSFileProtectionComplete} + options:@{NSFileProtectionKey: NSFileProtectionComplete, + NSMigratePersistentStoresAutomaticallyOption : @(YES), + NSInferMappingModelAutomaticallyOption : @(YES)} error:error]) { return nil; } @@ -108,6 +110,17 @@ - (instancetype)initWithPersistenceDirectoryURL:(NSURL *)url { return self; } +- (instancetype)initWithPersistenceDirectoryURL:(NSURL *)URL + identifier:(NSString *)identifier + patient:(OCKPatient *)patient { + self = [self initWithPersistenceDirectoryURL:URL]; + if (self) { + _identifier = [identifier copy]; + _patient = patient; + } + return self; +} + - (NSString *)coredataFilePath { return [_persistenceDirectoryURL.path stringByAppendingPathComponent:CoreDataFileName]; } @@ -427,6 +440,9 @@ - (void)handleActivityListChange:(BOOL)result type:(OCKCarePlanActivityType)type if (_watchDelegate && [_watchDelegate respondsToSelector:@selector(carePlanStoreActivityListDidChange:)]) { [_watchDelegate carePlanStoreActivityListDidChange:self]; } + if (_cloudBridgeDelegate && [_cloudBridgeDelegate respondsToSelector:@selector(carePlanStoreActivityListDidChange:)]) { + [_cloudBridgeDelegate carePlanStoreActivityListDidChange:self]; + } }); } } @@ -763,6 +779,9 @@ - (void)updateEvent:(OCKCarePlanEvent *)event if(_watchDelegate && [_watchDelegate respondsToSelector:@selector(carePlanStore:didReceiveUpdateOfEvent:)]) { [_watchDelegate carePlanStore:self didReceiveUpdateOfEvent:copiedEvent]; } + if(_cloudBridgeDelegate && [_cloudBridgeDelegate respondsToSelector:@selector(carePlanStore:didReceiveUpdateOfEvent:)]) { + [_cloudBridgeDelegate carePlanStore:self didReceiveUpdateOfEvent:copiedEvent]; + } }); } }); @@ -872,7 +891,7 @@ - (void)dailyCompletionStatusWithType:(OCKCarePlanActivityType)type for (OCKCarePlanActivity *activity in activities) { NSUInteger count = [activity.schedule numberOfEventsOnDate:day]; - if (count > 0) { + if (count > 0 && !activity.optional) { NSUInteger daysSinceStart = [activity.schedule numberOfDaySinceStart:day]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"activity.identifier = %@ AND numberOfDaysSinceStart = %d AND state = %d", activity.identifier, daysSinceStart, OCKCarePlanEventStateCompleted]; [predicates addObject:predicate]; @@ -902,6 +921,38 @@ - (void)dailyCompletionStatusWithType:(OCKCarePlanActivityType)type }]; } +- (void)evaluateAdheranceThresholdForActivity:(OCKCarePlanActivity *)activity date:(NSDateComponents *)date completion:(void (^)(BOOL success, OCKCarePlanThreshold * _Nullable, NSError * _Nullable))completion { + + OCKThrowInvalidArgumentExceptionIfNil(activity); + OCKThrowInvalidArgumentExceptionIfNil(date); + OCKThrowInvalidArgumentExceptionIfNil(completion); + + OCKCarePlanThreshold *adheranceThreshold = [activity.schedule thresholdOnDate:date]; + + if (!adheranceThreshold) { + completion(YES, nil, nil); + return; + } + + [self eventsForActivity:activity date:date completion:^(NSArray * _Nonnull events, NSError * _Nullable error) { + if (error) { + completion(NO, nil, error); + } + + int eventsCompleted = 0; + for (OCKCarePlanEvent *event in events) { + if (event.state == OCKCarePlanEventStateCompleted) { + eventsCompleted++; + } + } + + if ([adheranceThreshold evaluateThresholdForValue:@(eventsCompleted)]) { + completion(YES, adheranceThreshold, error); + } else { + completion(YES, nil, error); + } + }]; +} #pragma mark - coredata diff --git a/CareKit/CarePlan/OCKCarePlanStore.xcdatamodeld/.xccurrentversion b/CareKit/CarePlan/OCKCarePlanStore.xcdatamodeld/.xccurrentversion new file mode 100644 index 000000000..54e04c428 --- /dev/null +++ b/CareKit/CarePlan/OCKCarePlanStore.xcdatamodeld/.xccurrentversion @@ -0,0 +1,8 @@ + + + + + _XCCurrentVersionName + OCKCarePlanStore v2.xcdatamodel + + diff --git a/CareKit/CarePlan/OCKCarePlanStore.xcdatamodeld/OCKCarePlanStore v2.xcdatamodel/contents b/CareKit/CarePlan/OCKCarePlanStore.xcdatamodeld/OCKCarePlanStore v2.xcdatamodel/contents new file mode 100644 index 000000000..12ec45d3d --- /dev/null +++ b/CareKit/CarePlan/OCKCarePlanStore.xcdatamodeld/OCKCarePlanStore v2.xcdatamodel/contents @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CareKit/CarePlan/OCKCarePlanStore.xcdatamodeld/OCKCarePlanStore.xcdatamodel/contents b/CareKit/CarePlan/OCKCarePlanStore.xcdatamodeld/OCKCarePlanStore.xcdatamodel/contents index ff6a06a38..4cab429b1 100644 --- a/CareKit/CarePlan/OCKCarePlanStore.xcdatamodeld/OCKCarePlanStore.xcdatamodel/contents +++ b/CareKit/CarePlan/OCKCarePlanStore.xcdatamodeld/OCKCarePlanStore.xcdatamodel/contents @@ -1,29 +1,29 @@ - + - + - + - - - + + + - + diff --git a/CareKit/CarePlan/OCKCarePlanStore_Internal.h b/CareKit/CarePlan/OCKCarePlanStore_Internal.h index 058e26cec..86b916008 100644 --- a/CareKit/CarePlan/OCKCarePlanStore_Internal.h +++ b/CareKit/CarePlan/OCKCarePlanStore_Internal.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/CarePlan/OCKCarePlanThreshold.h b/CareKit/CarePlan/OCKCarePlanThreshold.h new file mode 100644 index 000000000..d61a087eb --- /dev/null +++ b/CareKit/CarePlan/OCKCarePlanThreshold.h @@ -0,0 +1,128 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import + + +NS_ASSUME_NONNULL_BEGIN + +/** + Defines the types of thresholds. + + The numeric threshold types are paired with an assessment activity and evaluated against numeric results for that assessment. + + The adherance threshold type is paired with a day in a schedule and evaluated against the number of completed events for a given activity on that day. + */ +typedef NS_ENUM(NSInteger, OCKCarePlanThresholdType) { + // Alert if the activity does not reach the given number of completed events on a day. + OCKCarePlanThresholdTypeAdherance, + // Alert if the numeric result is greater than the given value. + OCKCarePlanThresholdTypeNumericGreaterThan, + // Alert if the numeric result is greater than or equal to the given value. + OCKCarePlanThresholdTypeNumericGreaterThanOrEqual, + // Alert if the numeric result is less than the given value. + OCKCarePlanThresholdTypeNumericLessThan, + // Alert if the numeric result is less than or equal to the given value. + OCKCarePlanThresholdTypeNumericLessThanOrEqual, + // Alert if the numeric result is equal to the given value. + OCKCarePlanThresholdTypeNumericEqual, + // Alert if the numeric result is inside the range [value, upperValue] inclusive. + OCKCarePlanThresholdTypeNumericRangeInclusive, + // Alert if the numeric result is inside the range (value, upperValue) exclusive. + OCKCarePlanThresholdTypeNumericRangeExclusive +}; + + +@interface OCKCarePlanThreshold : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +/** + Convienience initializer for the adherance type threshold. + This initializer covers necessary attributes for building an adherance threshold. + + @param value The value of the threshold (triggers if less than this value of events are completed). + @param title The title associated with the threshold. + + @return Initialized OCKCarePlanThreshold instance. + */ ++ (instancetype)adheranceThresholdWithValue:(NSNumber*)value + title:(nullable NSString*)title; +/** + Convienience initializer for numeric type thresholds. + This initializer covers necessary attributes for building a numeric threshold. + + @param value The value of the threshold. + @param type The type of numeric threshold. + @param upperValue The upper value of the threshold, for RangeInclusive and RangeExclusive. + @param title The title associated with the threshold. + + @return Initialized OCKCarePlanThreshold instance. + */ ++ (instancetype)numericThresholdWithValue:(NSNumber *)value + type:(OCKCarePlanThresholdType)type + upperValue:(nullable NSNumber*)upperValue + title:(nullable NSString*)title; + +/** + Evaluates the threshold against a given value. + + @param valueToCheck The value used in evaluating the threshold. + + @return BOOL whether or not the threshold was triggered. + */ +- (BOOL)evaluateThresholdForValue:(NSNumber *)valueToCheck; + +/** + The type of threshold. + */ +@property (nonatomic, readonly) OCKCarePlanThresholdType type; + +/** + The primary value of the threshold. + */ +@property (nonatomic, readonly) NSNumber *value; + +/** + The title associated with the threshold. + Will be displayed to the user if the threshold is triggered. + */ +@property (nonatomic, readonly, nullable) NSString *title; + +/** + The optional upper value of the threshold. + Only used for NumericRangeInclusive and NumericRangeExclusive threshold types. + */ +@property (nonatomic, readonly, nullable) NSNumber *upperValue; + +@end + +NS_ASSUME_NONNULL_END diff --git a/CareKit/CarePlan/OCKCarePlanThreshold.m b/CareKit/CarePlan/OCKCarePlanThreshold.m new file mode 100644 index 000000000..509e24ace --- /dev/null +++ b/CareKit/CarePlan/OCKCarePlanThreshold.m @@ -0,0 +1,150 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import "OCKCarePlanThreshold.h" +#import "OCKHelpers.h" + + +@implementation OCKCarePlanThreshold + +- (instancetype)init { + OCKThrowMethodUnavailableException(); + return nil; +} + +- (instancetype)initWithType:(OCKCarePlanThresholdType)type + value:(NSNumber *)value + title:(NSString *)title + upperValue:(NSNumber *)upperValue { + + self = [super init]; + if (self) { + _type = type; + _value = [value copy]; + _title = [title copy]; + _upperValue = [upperValue copy]; + } + + return self; +} + ++ (instancetype)adheranceThresholdWithValue:(NSNumber *)value + title:(NSString *)title { + return [[self alloc] initWithType:OCKCarePlanThresholdTypeAdherance + value:value + title:title + upperValue:nil]; +} + ++ (instancetype)numericThresholdWithValue:(NSNumber *)value + type:(OCKCarePlanThresholdType)type + upperValue:(NSNumber *)upperValue + title:(NSString *)title { + return [[self alloc] initWithType:type + value:value + title:title + upperValue:upperValue]; +} + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)coder { + self = [super init]; + if (self) { + OCK_DECODE_ENUM(coder, type); + OCK_DECODE_OBJ_CLASS(coder, value, NSNumber); + OCK_DECODE_OBJ_CLASS(coder, title, NSString); + OCK_DECODE_OBJ_CLASS(coder, upperValue, NSNumber); + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + OCK_ENCODE_ENUM(coder, type); + OCK_ENCODE_OBJ(coder, value); + OCK_ENCODE_OBJ(coder, title); + OCK_ENCODE_OBJ(coder, upperValue); +} + +- (BOOL)isEqual:(id)object { + BOOL isClassMatch = ([self class] == [object class]); + + __typeof(self) castObject = object; + return (isClassMatch && + self.type == castObject.type && + OCKEqualObjects(self.value, castObject.value) && + OCKEqualObjects(self.title, castObject.title) && + OCKEqualObjects(self.upperValue, castObject.upperValue) + ); +} + +- (instancetype)copyWithZone:(NSZone *)zone { + OCKCarePlanThreshold *threshold = [[[self class] alloc] initWithType:self.type value:self.value title:self.title upperValue:self.upperValue]; + return threshold; +} + + + +- (BOOL)evaluateThresholdForValue:(NSNumber *)valueToCheck { + NSComparisonResult result = [self.value compare:valueToCheck]; + NSComparisonResult secondResult; + switch (self.type) { + case OCKCarePlanThresholdTypeAdherance: + return (result == NSOrderedDescending); + + case OCKCarePlanThresholdTypeNumericGreaterThan: + return (result == NSOrderedAscending); + + case OCKCarePlanThresholdTypeNumericGreaterThanOrEqual: + return ((result == NSOrderedAscending) || (result == NSOrderedSame)); + + case OCKCarePlanThresholdTypeNumericLessThan: + return (result == NSOrderedDescending); + + case OCKCarePlanThresholdTypeNumericLessThanOrEqual: + return ((result == NSOrderedDescending) || (result == NSOrderedSame)); + + case OCKCarePlanThresholdTypeNumericEqual: + return (result == NSOrderedSame); + + case OCKCarePlanThresholdTypeNumericRangeInclusive: + secondResult = [self.upperValue compare:valueToCheck]; + return (((result == NSOrderedAscending) || (result == NSOrderedSame)) && ((secondResult == NSOrderedDescending) || (secondResult == NSOrderedSame))); + + case OCKCarePlanThresholdTypeNumericRangeExclusive: + secondResult = [self.upperValue compare:valueToCheck]; + return ((result == NSOrderedAscending) && (secondResult == NSOrderedDescending)); + } +} + +@end diff --git a/CareKit/CarePlan/OCKCareSchedule.h b/CareKit/CarePlan/OCKCareSchedule.h index ddd85c3cb..9214b73c4 100644 --- a/CareKit/CarePlan/OCKCareSchedule.h +++ b/CareKit/CarePlan/OCKCareSchedule.h @@ -30,6 +30,7 @@ #import "OCKDefines.h" +#import "OCKCarePlanThreshold.h" NS_ASSUME_NONNULL_BEGIN @@ -77,27 +78,28 @@ OCK_CLASS_AVAILABLE occurrencesPerDay:(NSUInteger)occurrencesPerDay; /** - Defines a schedule that repeats every week. + Defines a schedule that has the same number of occurrences each day. - Each weekday can have a different number of occurrences. You can set the end date later by using the CarePlanStore API. - @param startDate Start date for a schedule, using the Gregorian calendar. - @param occurrencesFromSundayToSaturday Number of occurrences for Sunday through Saturday. + @param startDate Start date for a schedule, using the Gregorian calendar. + @param occurrencesPerDay Number of occurrences in each day. + @param threshold The threshold to apply to each day's occurences count. @return An OCKCareSchedule instance. */ -+ (instancetype)weeklyScheduleWithStartDate:(NSDateComponents *)startDate - occurrencesOnEachDay:(NSArray *)occurrencesFromSundayToSaturday; ++ (instancetype)dailyScheduleWithStartDate:(NSDateComponents *)startDate + occurrencesPerDay:(NSUInteger)occurrencesPerDay + dailyThreshold:(OCKCarePlanThreshold *)threshold; /** Defines a schedule that has the same number of occurrences every day. @param startDate Start date for a schedule, using the Gregorian calendar. @param occurrencesPerDay Number of occurrences in each day. - @param daysToSkip Number of days between two active days during this period for which the schedule has no occurrence. - (That is, number of skipped days.) - First day of a schedule is recognized as an active day. + @param daysToSkip Number of days between two active days during this period for which the schedule has no occurrence. + (That is, number of skipped days.) + First day of a schedule is recognized as an active day. @param endDate End date for a schedule, , using the Gregorian calendar. @return An OCKCareSchedule instance. @@ -107,6 +109,55 @@ OCK_CLASS_AVAILABLE daysToSkip:(NSUInteger)daysToSkip endDate:(nullable NSDateComponents *)endDate; +/** + Defines a schedule that has the same number of occurrences every day. + + @param startDate Start date for a schedule, using the Gregorian calendar. + @param occurrencesPerDay Number of occurrences in each day. + @param daysToSkip Number of days between two active days during this period for which the schedule has no occurrence. + (That is, number of skipped days.) + First day of a schedule is recognized as an active day. + @param endDate End date for a schedule, , using the Gregorian calendar. + @param threshold The threshold to apply to each day's occurences count. + + @return An OCKCareSchedule instance. + */ ++ (instancetype)dailyScheduleWithStartDate:(NSDateComponents *)startDate + occurrencesPerDay:(NSUInteger)occurrencesPerDay + daysToSkip:(NSUInteger)daysToSkip + endDate:(nullable NSDateComponents *)endDate + dailyThreshold:(nullable OCKCarePlanThreshold *)threshold; + +/** + Defines a schedule that repeats every week. + + Each weekday can have a different number of occurrences. + You can set the end date later by using the CarePlanStore API. + + @param startDate Start date for a schedule, using the Gregorian calendar. + @param occurrencesFromSundayToSaturday Number of occurrences for Sunday through Saturday. + + @return An OCKCareSchedule instance. + */ ++ (instancetype)weeklyScheduleWithStartDate:(NSDateComponents *)startDate + occurrencesOnEachDay:(NSArray *)occurrencesFromSundayToSaturday; + +/** + Defines a schedule that repeats every week. + + Each weekday can have a different number of occurrences. + You can set the end date later by using the CarePlanStore API. + + @param startDate Start date for a schedule, using the Gregorian calendar. + @param occurrencesFromSundayToSaturday Number of occurrences for Sunday through Saturday. + @param thresholdsFromSundayToSaturday The thresholds to apply to each days' occurrences count, from Sunday through Saturday. + + @return An OCKCareSchedule instance. + */ ++ (instancetype)weeklyScheduleWithStartDate:(NSDateComponents *)startDate + occurrencesOnEachDay:(NSArray *)occurrencesFromSundayToSaturday + thresholdsOnEachDay:(NSArray *)thresholdsFromSundayToSaturday; + /** Defines a schedule that repeats every week. @@ -124,6 +175,25 @@ OCK_CLASS_AVAILABLE occurrencesOnEachDay:(NSArray *)occurrencesFromSundayToSaturday weeksToSkip:(NSUInteger)weeksToSkip endDate:(nullable NSDateComponents *)endDate; +/** + Defines a schedule that repeats every week. + + Each weekday can have a different number of occurrences. + + @param startDate Start date for a schedule, using the Gregorian calendar. + @param occurrencesFromSundayToSaturday Number of occurrences in each day. + @param weeksToSkip Number of weeks between two active weeks during this period for which the schedule has no occurrence. + (That is, number of skipped weeks.) + @param endDate End date for a schedule, , using the Gregorian calendar. + @param thresholdsFromSundayToSaturday The thresholds to apply to each days' occurrences count, from Sunday through Saturday. + + @return An OCKCareSchedule instance. + */ ++ (instancetype)weeklyScheduleWithStartDate:(NSDateComponents *)startDate + occurrencesOnEachDay:(NSArray *)occurrencesFromSundayToSaturday + weeksToSkip:(NSUInteger)weeksToSkip + endDate:(nullable NSDateComponents *)endDate + thresholdsOnEachDay:(nullable NSArray *)thresholdsFromSundayToSaturday; /** Type of schedule. @@ -156,6 +226,13 @@ OCK_CLASS_AVAILABLE */ @property (nonatomic, copy, readonly) NSArray *occurrences; +/** + Adherance thresholds for each day within the time range. + + Must be thresholds of type OCKCarePlanThresholdTypeAdherance + */ +@property (nonatomic, copy, readonly, nullable) NSArray *thresholds; + /** Number of inactive time units between two active time units. During this period, schedule has no occurrence. @@ -176,6 +253,16 @@ OCK_CLASS_AVAILABLE */ - (NSUInteger)numberOfEventsOnDate:(NSDateComponents *)date; +/** + The adherance threshold for a given date. + + @param date Gregorian calendar representation of a date. + Only Era/Year/Month/Day attributes are observed. + + @return The threshold object for the specified date, or nil if no threshold set. + */ +- (OCKCarePlanThreshold *)thresholdOnDate:(NSDateComponents *)date; + @end NS_ASSUME_NONNULL_END diff --git a/CareKit/CarePlan/OCKCareSchedule.m b/CareKit/CarePlan/OCKCareSchedule.m index 09424ea57..17bcb0fea 100644 --- a/CareKit/CarePlan/OCKCareSchedule.m +++ b/CareKit/CarePlan/OCKCareSchedule.m @@ -52,12 +52,14 @@ + (instancetype)dailyScheduleWithStartDate:(NSDateComponents *)startDate endDate:nil]; } -+ (instancetype)weeklyScheduleWithStartDate:(NSDateComponents *)startDate - occurrencesOnEachDay:(NSArray *)occurrencesFromSundayToSaturday { - return [[OCKCareWeeklySchedule alloc] initWithStartDate:startDate - weeksToSkip:0 - occurrencesOnEachDay:occurrencesFromSundayToSaturday - endDate:nil]; ++ (instancetype)dailyScheduleWithStartDate:(NSDateComponents *)startDate + occurrencesPerDay:(NSUInteger)occurrencesPerDay + dailyThreshold:(OCKCarePlanThreshold *)threshold { + return [[OCKCareDailySchedule alloc] initWithStartDate:startDate + daysToSkip:0 + occurrencesPerDay:occurrencesPerDay + endDate:nil + dailyThreshold:threshold]; } + (instancetype)dailyScheduleWithStartDate:(NSDateComponents *)startDate @@ -65,11 +67,41 @@ + (instancetype)dailyScheduleWithStartDate:(NSDateComponents *)startDate daysToSkip:(NSUInteger)daysToSkip endDate:(nullable NSDateComponents *)endDate { return [[OCKCareDailySchedule alloc] initWithStartDate:startDate - daysToSkip:daysToSkip - occurrencesPerDay:occurrencesPerDay + daysToSkip:daysToSkip + occurrencesPerDay:occurrencesPerDay endDate:endDate]; } ++ (instancetype)dailyScheduleWithStartDate:(NSDateComponents *)startDate + occurrencesPerDay:(NSUInteger)occurrencesPerDay + daysToSkip:(NSUInteger)daysToSkip + endDate:(NSDateComponents *)endDate + dailyThreshold:(OCKCarePlanThreshold *)threshold { + return [[OCKCareDailySchedule alloc] initWithStartDate:startDate + daysToSkip:daysToSkip + occurrencesPerDay:occurrencesPerDay + endDate:endDate + dailyThreshold:threshold]; +} + ++ (instancetype)weeklyScheduleWithStartDate:(NSDateComponents *)startDate + occurrencesOnEachDay:(NSArray *)occurrencesFromSundayToSaturday { + return [[OCKCareWeeklySchedule alloc] initWithStartDate:startDate + weeksToSkip:0 + occurrencesOnEachDay:occurrencesFromSundayToSaturday + endDate:nil]; +} + ++ (instancetype)weeklyScheduleWithStartDate:(NSDateComponents *)startDate + occurrencesOnEachDay:(NSArray *)occurrencesFromSundayToSaturday + thresholdsOnEachDay:(NSArray *)thresholdsFromSundayToSaturday { + return [[OCKCareWeeklySchedule alloc] initWithStartDate:startDate + weeksToSkip:0 + occurrencesOnEachDay:occurrencesFromSundayToSaturday + endDate:nil + thresholdsOnEachDay:thresholdsFromSundayToSaturday]; +} + + (instancetype)weeklyScheduleWithStartDate:(NSDateComponents *)startDate occurrencesOnEachDay:(NSArray *)occurrencesFromSundayToSaturday weeksToSkip:(NSUInteger)weeksToSkip @@ -80,9 +112,22 @@ + (instancetype)weeklyScheduleWithStartDate:(NSDateComponents *)startDate endDate:endDate]; } ++ (instancetype)weeklyScheduleWithStartDate:(NSDateComponents *)startDate + occurrencesOnEachDay:(NSArray *)occurrencesFromSundayToSaturday + weeksToSkip:(NSUInteger)weeksToSkip + endDate:(NSDateComponents *)endDate + thresholdsOnEachDay:(NSArray *)thresholdsFromSundayToSaturday { + return [[OCKCareWeeklySchedule alloc] initWithStartDate:startDate + weeksToSkip:weeksToSkip + occurrencesOnEachDay:occurrencesFromSundayToSaturday + endDate:endDate + thresholdsOnEachDay:thresholdsFromSundayToSaturday]; +} + - (instancetype)initWithStartDate:(NSDateComponents *)startDate endDate:(NSDateComponents *)endDate occurrences:(NSArray *)occurrences + thresholds:(NSArray *)thresholds timeUnitsToSkip:(NSUInteger)timeUnitsToSkip { OCKThrowInvalidArgumentExceptionIfNil(startDate); @@ -90,11 +135,18 @@ - (instancetype)initWithStartDate:(NSDateComponents *)startDate NSAssert(![startDate isLaterThan:endDate], @"startDate should be earlier than endDate."); } + for (OCKCarePlanThreshold *threshold in thresholds) { + NSAssert(threshold.type == OCKCarePlanThresholdTypeAdherance, @"Thresholds for schedules must be of type adherance."); + } + self = [super init]; if (self) { _startDate = [startDate validatedDateComponents]; _endDate = [endDate validatedDateComponents]; _occurrences = [occurrences copy]; + if (thresholds != nil) { + _thresholds = [thresholds copy]; + } _timeUnitsToSkip = timeUnitsToSkip; } return self; @@ -107,11 +159,11 @@ + (BOOL)supportsSecureCoding { - (instancetype)initWithCoder:(NSCoder *)coder { self = [super init]; if (self) { - OCK_DECODE_OBJ_CLASS(coder, startDate, NSDateComponents); OCK_DECODE_OBJ_CLASS(coder, endDate, NSDateComponents); OCK_DECODE_OBJ_ARRAY(coder, occurrences, NSNumber); OCK_DECODE_INTEGER(coder, timeUnitsToSkip); + OCK_DECODE_OBJ_ARRAY(coder, thresholds, OCKCarePlanThreshold); } return self; } @@ -121,6 +173,7 @@ - (void)encodeWithCoder:(NSCoder *)coder { OCK_ENCODE_OBJ(coder, endDate); OCK_ENCODE_OBJ(coder, occurrences); OCK_ENCODE_INTEGER(coder, timeUnitsToSkip); + OCK_ENCODE_OBJ(coder, thresholds); } - (BOOL)isEqual:(id)object { @@ -131,11 +184,12 @@ - (BOOL)isEqual:(id)object { OCKEqualObjects(self.startDate, castObject.startDate) && OCKEqualObjects(self.endDate, castObject.endDate) && OCKEqualObjects(self.occurrences, castObject.occurrences) && + OCKEqualObjects(self.thresholds, castObject.thresholds) && (self.timeUnitsToSkip == castObject.timeUnitsToSkip)); } - (instancetype)copyWithZone:(NSZone *)zone { - OCKCareSchedule *schedule = [[[self class] alloc] initWithStartDate:self.startDate endDate:self.endDate occurrences:self.occurrences timeUnitsToSkip:self.timeUnitsToSkip]; + OCKCareSchedule *schedule = [[[self class] alloc] initWithStartDate:self.startDate endDate:self.endDate occurrences:self.occurrences thresholds:self.thresholds timeUnitsToSkip:self.timeUnitsToSkip]; return schedule; } @@ -156,6 +210,10 @@ - (NSUInteger)numberOfEventsOnDate:(NSDateComponents *)day { OCKThrowMethodUnavailableException(); } +- (OCKCarePlanThreshold *)thresholdOnDate:(NSDateComponents *)day { + OCKThrowMethodUnavailableException(); +} + - (NSUInteger)numberOfDaySinceStart:(NSDateComponents *)day { NSCalendar *calendar = [self UTC_calendar]; @@ -171,7 +229,7 @@ - (NSUInteger)numberOfDaySinceStart:(NSDateComponents *)day { return daysSinceStart; } --(void)setEndDate:(NSDateComponents *)endDate { +- (void)setEndDate:(NSDateComponents *)endDate { NSAssert(![_startDate isLaterThan:endDate], @"startDate should be earlier than endDate. %@ %@", _startDate, endDate); _endDate = [endDate validatedDateComponents]; } @@ -197,7 +255,20 @@ - (instancetype)initWithStartDate:(NSDateComponents *)startDate daysToSkip:(NSUInteger)daysToSkip occurrencesPerDay:(NSUInteger)occurrencesPerDay endDate:(nullable NSDateComponents *)endDate { - self = [self initWithStartDate:startDate endDate:endDate occurrences: @[@(occurrencesPerDay)] timeUnitsToSkip:daysToSkip]; + return [self initWithStartDate:startDate daysToSkip:daysToSkip occurrencesPerDay:occurrencesPerDay endDate:endDate dailyThreshold:nil]; +} + +- (instancetype)initWithStartDate:(NSDateComponents *)startDate daysToSkip:(NSUInteger)daysToSkip occurrencesPerDay:(NSUInteger)occurrencesPerDay endDate:(NSDateComponents *)endDate dailyThreshold:(OCKCarePlanThreshold * _Nullable)threshold { + + NSArray *thresholdsArray; + + if (threshold) { + thresholdsArray = @[threshold]; + } else { + thresholdsArray = nil; + } + + self = [self initWithStartDate:startDate endDate:endDate occurrences:@[@(occurrencesPerDay)] thresholds:thresholdsArray timeUnitsToSkip:daysToSkip]; return self; } @@ -212,6 +283,17 @@ - (NSUInteger)numberOfEventsOnDate:(NSDateComponents *)day { return occurrences; } +- (OCKCarePlanThreshold *)thresholdOnDate:(NSDateComponents *)day { + if (([self isDateInRange:day]) || (self.thresholds)) { + NSUInteger daysSinceStart = [self numberOfDaySinceStart:day]; + + if ((daysSinceStart % (self.timeUnitsToSkip + 1)) == 0) { + return self.thresholds.firstObject; + } + } + return nil; +} + @end @@ -225,11 +307,15 @@ - (instancetype)initWithStartDate:(NSDateComponents *)startDate weeksToSkip:(NSUInteger)weeksToSkip occurrencesOnEachDay:(NSArray *)occurrencesFromSundayToSaturday endDate:(nullable NSDateComponents *)endDate { + return [self initWithStartDate:startDate weeksToSkip:weeksToSkip occurrencesOnEachDay:occurrencesFromSundayToSaturday endDate:endDate thresholdsOnEachDay:nil]; +} + +- (instancetype)initWithStartDate:(NSDateComponents *)startDate weeksToSkip:(NSUInteger)weeksToSkip occurrencesOnEachDay:(NSArray *)occurrencesFromSundayToSaturday endDate:(NSDateComponents *)endDate thresholdsOnEachDay:(NSArray *)thresholdsFromSundayToSaturday { OCKThrowInvalidArgumentExceptionIfNil(occurrencesFromSundayToSaturday); NSParameterAssert(occurrencesFromSundayToSaturday.count == 7); - self = [self initWithStartDate:startDate endDate:endDate occurrences:occurrencesFromSundayToSaturday timeUnitsToSkip:weeksToSkip]; + self = [self initWithStartDate:startDate endDate:endDate occurrences:occurrencesFromSundayToSaturday thresholds:thresholdsFromSundayToSaturday timeUnitsToSkip:weeksToSkip]; return self; } @@ -253,4 +339,25 @@ - (NSUInteger)numberOfEventsOnDate:(NSDateComponents *)day { return occurrences; } +- (OCKCarePlanThreshold *)thresholdOnDate:(NSDateComponents *)day { + if (([self isDateInRange:day]) && (self.thresholds)) { + NSCalendar *calendar = [self UTC_calendar]; + + NSInteger startWeek = [calendar ordinalityOfUnit:NSCalendarUnitWeekOfYear + inUnit:NSCalendarUnitEra + forDate:[self.startDate UTC_dateWithGregorianCalendar]]; + + NSInteger endWeek = [calendar ordinalityOfUnit:NSCalendarUnitWeekOfYear + inUnit:NSCalendarUnitEra + forDate:[day UTC_dateWithGregorianCalendar]]; + + NSUInteger weeksSinceStart = endWeek - startWeek; + NSUInteger weekday = [calendar component:NSCalendarUnitWeekday fromDate:[day UTC_dateWithGregorianCalendar]]; + if ((weeksSinceStart % (self.timeUnitsToSkip + 1)) == 0) { + return self.thresholds[weekday - 1]; + } + } + return nil; +} + @end diff --git a/CareKit/CarePlan/OCKCareSchedule_Internal.h b/CareKit/CarePlan/OCKCareSchedule_Internal.h index b0b69200a..3efb12f4e 100644 --- a/CareKit/CarePlan/OCKCareSchedule_Internal.h +++ b/CareKit/CarePlan/OCKCareSchedule_Internal.h @@ -50,6 +50,13 @@ NS_ASSUME_NONNULL_BEGIN occurrencesPerDay:(NSUInteger)occurrencesPerDay endDate:(nullable NSDateComponents *)endDate; +- (instancetype)initWithStartDate:(NSDateComponents *)startDate + daysToSkip:(NSUInteger)daysToSkip + occurrencesPerDay:(NSUInteger)occurrencesPerDay + endDate:(nullable NSDateComponents *)endDate + dailyThreshold:(nullable OCKCarePlanThreshold *)threshold; + + @end @@ -60,6 +67,12 @@ NS_ASSUME_NONNULL_BEGIN occurrencesOnEachDay:(NSArray *)occurrencesFromSundayToSaturday endDate:(nullable NSDateComponents *)endDate; +- (instancetype)initWithStartDate:(NSDateComponents *)startDate + weeksToSkip:(NSUInteger)weeksToSkip + occurrencesOnEachDay:(NSArray *)occurrencesFromSundayToSaturday + endDate:(nullable NSDateComponents *)endDate + thresholdsOnEachDay:(nullable NSArray *)thresholdsFromSundayToSaturday; + @end NS_ASSUME_NONNULL_END diff --git a/CareKit/CarePlan/OCKCareSchedule_Private.h b/CareKit/CarePlan/OCKCareSchedule_Private.h index adf18af6b..8a5eec049 100644 --- a/CareKit/CarePlan/OCKCareSchedule_Private.h +++ b/CareKit/CarePlan/OCKCareSchedule_Private.h @@ -40,6 +40,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithStartDate:(NSDateComponents *)startDate endDate:(NSDateComponents *)endDate occurrences:(NSArray *)occurrences + thresholds:(NSArray *)thresholds timeUnitsToSkip:(NSUInteger)timeUnitsToSkip; @end diff --git a/CareKit/Common/OCKColor.h b/CareKit/Common/OCKColor.h new file mode 100644 index 000000000..f714d5b59 --- /dev/null +++ b/CareKit/Common/OCKColor.h @@ -0,0 +1,73 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import + + +@interface OCKColor : NSObject + +#if UIKIT_DEFINE_AS_PROPERTIES +@property(class, nonatomic, readonly) UIColor *purple; +@property(class, nonatomic, readonly) UIColor *lightPink; +@property(class, nonatomic, readonly) UIColor *green; +@property(class, nonatomic, readonly) UIColor *darkPurple; +@property(class, nonatomic, readonly) UIColor *peach; +@property(class, nonatomic, readonly) UIColor *fuchsia; +@property(class, nonatomic, readonly) UIColor *pink; +@property(class, nonatomic, readonly) UIColor *goldenYellow; +@property(class, nonatomic, readonly) UIColor *lightBlue; +@property(class, nonatomic, readonly) UIColor *rose; +@property(class, nonatomic, readonly) UIColor *red; +@property(class, nonatomic, readonly) UIColor *royalBlue; +@property(class, nonatomic, readonly) UIColor *orange; +@property(class, nonatomic, readonly) UIColor *mediumBlue; +@property(class, nonatomic, readonly) UIColor *lightOrange; +@property(class, nonatomic, readonly) UIColor *brightPurple; +#else ++ (UIColor *)purple; ++ (UIColor *)lightPink; ++ (UIColor *)green; ++ (UIColor *)darkPurple; ++ (UIColor *)peach; ++ (UIColor *)fuchsia; ++ (UIColor *)pink; ++ (UIColor *)goldenYellow; ++ (UIColor *)lightBlue; ++ (UIColor *)rose; ++ (UIColor *)red; ++ (UIColor *)royalBlue; ++ (UIColor *)orange; ++ (UIColor *)mediumBlue; ++ (UIColor *)lightOrange; ++ (UIColor *)brightPurple; +#endif + +@end diff --git a/CareKit/Common/OCKColor.m b/CareKit/Common/OCKColor.m new file mode 100644 index 000000000..329481f06 --- /dev/null +++ b/CareKit/Common/OCKColor.m @@ -0,0 +1,100 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "OCKColor.h" + +@implementation OCKColor + ++ (UIColor *)purple { + return [UIColor colorWithRed:155.0/255.0 green:95.0/255.0 blue:182.0/255.0 alpha:1.0]; +} + ++ (UIColor *)lightPink { + return [UIColor colorWithRed:255.0/255.0 green:147.0/255.0 blue:227.0/255.0 alpha:1.0]; +} + ++ (UIColor *)green { + return [UIColor colorWithRed:88.0/255.0 green:196.0/255.0 blue:47.0/255.0 alpha:1.0]; +} + ++ (UIColor *)darkPurple { + return [UIColor colorWithRed:146.0/255.0 green:83.0/255.0 blue:175.0/255.0 alpha:1.0]; +} + ++ (UIColor *)peach { + return [UIColor colorWithRed:250.0/255.0 green:127.0/255.0 blue:127.0/255.0 alpha:1.0]; +} + ++ (UIColor *)fuchsia { + return [UIColor colorWithRed:231.0/255.0 green:111.0/255.0 blue:238.0/255.0 alpha:1.0]; +} + ++ (UIColor *)pink { + return [UIColor colorWithRed:248.0/255.0 green:80.0/255.0 blue:173.0/255.0 alpha:1.0]; +} + ++ (UIColor *)goldenYellow { + return [UIColor colorWithRed:255.0/255.0 green:198.0/255.0 blue:62.0/255.0 alpha:1.0]; +} + ++ (UIColor *)lightBlue { + return [UIColor colorWithRed:74.0/255.0 green:208.0/255.0 blue:242.0/255.0 alpha:1.0]; +} + ++ (UIColor *)rose { + return [UIColor colorWithRed:234.0/255.0 green:153.0/255.0 blue:189.0/255.0 alpha:1.0]; +} + ++ (UIColor *)red { + return [UIColor colorWithRed:237.0/255.0 green:63.0/255.0 blue:63.0/255.0 alpha:1.0]; +} + ++ (UIColor *)royalBlue { + return [UIColor colorWithRed:32.0/255.0 green:115.0/255.0 blue:255.0/255.0 alpha:1.0]; +} + ++ (UIColor *)orange { + return [UIColor colorWithRed:255.0/255.0 green:131.0/255.0 blue:46.0/255.0 alpha:1.0]; +} + ++ (UIColor *)mediumBlue { + return [UIColor colorWithRed:122.0/255.0 green:137.0/255.0 blue:194.0/255.0 alpha:1.0]; +} + ++ (UIColor *)lightOrange { + return [UIColor colorWithRed:255.0/255.0 green:157.0/255.0 blue:2.0/255.0 alpha:1.0]; +} + ++ (UIColor *)brightPurple { + return [UIColor colorWithRed:89.0/255.0 green:50.0/255.0 blue:243.0/255.0 alpha:1.0]; +} + +@end + diff --git a/CareKit/Common/OCKGlyph.h b/CareKit/Common/OCKGlyph.h new file mode 100644 index 000000000..9a01ccfa9 --- /dev/null +++ b/CareKit/Common/OCKGlyph.h @@ -0,0 +1,113 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import +#import "OCKDefines.h" + + +NS_ASSUME_NONNULL_BEGIN + +OCK_ENUM_AVAILABLE +typedef NS_ENUM(NSInteger, OCKGlyphType) { + +// A default GlyphType with images of a heart in active and inactive states +// with big and small imagess for header and week views respectively. + OCKGlyphTypeHeart = 0, + + OCKGlyphTypeAccessibility, + + OCKGlyphTypeActiveLife, + + OCKGlyphTypeAdultLearning, + + OCKGlyphTypeAwareness, + + OCKGlyphTypeBlood, + + OCKGlyphTypeBloodPressure, + + OCKGlyphTypeCardio, + + OCKGlyphTypeChildLearning, + + OCKGlyphTypeDentalHealth, + + OCKGlyphTypeFemaleHealth, + + OCKGlyphTypeHearing, + + OCKGlyphTypeHomeCare, + + OCKGlyphTypeInfantCare, + + OCKGlyphTypeLaboratory, + + OCKGlyphTypeMaleHealth, + + OCKGlyphTypeMaternalHealth, + + OCKGlyphTypeMedication, + + OCKGlyphTypeMentalHealth, + + OCKGlyphTypeNeuro, + + OCKGlyphTypeNutrition, + + OCKGlyphTypeOptometry, + + OCKGlyphTypePediatrics, + + OCKGlyphTypePhysicalTherapy, + + OCKGlyphTypePodiatry, + + OCKGlyphTypeRespiratoryHealth, + + OCKGlyphTypeScale, + + OCKGlyphTypeStethoscope, + + OCKGlyphTypeSyringe, + + OCKGlyphTypeCustom +}; + + +@interface OCKGlyph : NSObject + +- (instancetype)init NS_UNAVAILABLE; + ++ (UIColor *)defaultColorForGlyph:(OCKGlyphType)type; + +@end + +NS_ASSUME_NONNULL_END diff --git a/CareKit/Common/OCKGlyph.m b/CareKit/Common/OCKGlyph.m new file mode 100644 index 000000000..54997f50b --- /dev/null +++ b/CareKit/Common/OCKGlyph.m @@ -0,0 +1,206 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import "OCKGlyph.h" +#import "OCKGlyph_Internal.h" +#import "OCKColor.h" + + +@implementation OCKGlyph + + ++ (UIImage *)glyphImageForType:(OCKGlyphType)type { + NSMutableString *glyphName = [NSMutableString stringWithString:[self nameForGlyphType:type]]; + return [UIImage imageNamed:glyphName inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil]; +} + ++ (NSString *)nameForGlyphType:(OCKGlyphType)type { + NSString *name; + switch (type) { + case OCKGlyphTypeHeart: + name = @"Heart"; + break; + case OCKGlyphTypeAccessibility: + name = @"Accessibility"; + break; + case OCKGlyphTypeActiveLife: + name = @"ActiveLife"; + break; + case OCKGlyphTypeAdultLearning: + name = @"AdultLearning"; + break; + case OCKGlyphTypeAwareness: + name = @"Awareness"; + break; + case OCKGlyphTypeBlood: + name = @"Blood"; + break; + case OCKGlyphTypeBloodPressure: + name = @"BloodPressure"; + break; + case OCKGlyphTypeCardio: + name = @"Cardio"; + break; + case OCKGlyphTypeChildLearning: + name = @"ChildLearning"; + break; + case OCKGlyphTypeDentalHealth: + name = @"DentalHealth"; + break; + case OCKGlyphTypeFemaleHealth: + name = @"FemaleHealth"; + break; + case OCKGlyphTypeHearing: + name = @"Hearing"; + break; + case OCKGlyphTypeHomeCare: + name = @"HomeCare"; + break; + case OCKGlyphTypeInfantCare: + name = @"InfantCare"; + break; + case OCKGlyphTypeLaboratory: + name = @"Laboratory"; + break; + case OCKGlyphTypeMaleHealth: + name = @"MaleHealth"; + break; + case OCKGlyphTypeMaternalHealth: + name = @"MaternalHealth"; + break; + case OCKGlyphTypeMedication: + name = @"Medication"; + break; + case OCKGlyphTypeMentalHealth: + name = @"MentalHealth"; + break; + case OCKGlyphTypeNeuro: + name = @"Neuro"; + break; + case OCKGlyphTypeNutrition: + name = @"Nutrition"; + break; + case OCKGlyphTypeOptometry: + name = @"Optometry"; + break; + case OCKGlyphTypePediatrics: + name = @"Pediatrics"; + break; + case OCKGlyphTypePhysicalTherapy: + name = @"PhysicalTherapy"; + break; + case OCKGlyphTypePodiatry: + name = @"Podiatry"; + break; + case OCKGlyphTypeRespiratoryHealth: + name = @"RespiratoryHealth"; + break; + case OCKGlyphTypeScale: + name = @"Scale"; + break; + case OCKGlyphTypeStethoscope: + name = @"Stethoscope"; + break; + case OCKGlyphTypeSyringe: + name = @"Syringe"; + break; + default: + break; + } + return name; +} + ++ (UIColor *)defaultColorForGlyph:(OCKGlyphType)type { + switch (type) { + case OCKGlyphTypeHeart: + return OCKColor.red; + case OCKGlyphTypeAccessibility: + return OCKColor.royalBlue; + case OCKGlyphTypeActiveLife: + return OCKColor.green; + case OCKGlyphTypeAdultLearning: + return OCKColor.red; + case OCKGlyphTypeAwareness: + return OCKColor.fuchsia; + case OCKGlyphTypeBlood: + return OCKColor.rose; + case OCKGlyphTypeBloodPressure: + return OCKColor.peach; + case OCKGlyphTypeCardio: + return OCKColor.red; + case OCKGlyphTypeChildLearning: + return OCKColor.lightOrange; + case OCKGlyphTypeDentalHealth: + return OCKColor.mediumBlue; + case OCKGlyphTypeFemaleHealth: + return OCKColor.orange; + case OCKGlyphTypeHearing: + return OCKColor.lightBlue; + case OCKGlyphTypeHomeCare: + return OCKColor.goldenYellow; + case OCKGlyphTypeInfantCare: + return OCKColor.fuchsia; + case OCKGlyphTypeLaboratory: + return OCKColor.peach; + case OCKGlyphTypeMaleHealth: + return OCKColor.lightOrange; + case OCKGlyphTypeMaternalHealth: + return OCKColor.lightPink; + case OCKGlyphTypeMedication: + return OCKColor.purple; + case OCKGlyphTypeMentalHealth: + return OCKColor.lightBlue; + case OCKGlyphTypeNeuro: + return OCKColor.darkPurple; + case OCKGlyphTypeNutrition: + return OCKColor.green; + case OCKGlyphTypeOptometry: + return OCKColor.brightPurple; + case OCKGlyphTypePediatrics: + return OCKColor.brightPurple; + case OCKGlyphTypePhysicalTherapy: + return OCKColor.orange; + case OCKGlyphTypePodiatry: + return OCKColor.lightBlue; + case OCKGlyphTypeRespiratoryHealth: + return OCKColor.peach; + case OCKGlyphTypeScale: + return OCKColor.green; + case OCKGlyphTypeStethoscope: + return OCKColor.red; + case OCKGlyphTypeSyringe: + return OCKColor.goldenYellow; + default: + return OCKColor.red; + } +} + +@end diff --git a/CareKit/Common/OCKGlyph_Internal.h b/CareKit/Common/OCKGlyph_Internal.h new file mode 100644 index 000000000..9c9bd4e41 --- /dev/null +++ b/CareKit/Common/OCKGlyph_Internal.h @@ -0,0 +1,39 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import "OCKGlyph.h" + + +@interface OCKGlyph() + ++ (UIImage *)glyphImageForType:(OCKGlyphType)type; + +@end diff --git a/CareKit/Common/OCKHeaderView.h b/CareKit/Common/OCKHeaderView.h new file mode 100644 index 000000000..9986fc1c8 --- /dev/null +++ b/CareKit/Common/OCKHeaderView.h @@ -0,0 +1,68 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import + + +NS_ASSUME_NONNULL_BEGIN + +@class OCKRingView; + +@interface OCKHeaderView : UIView + +// Initial value is 0 +@property (nonatomic) double value; + +// Title for the header view +@property (nonatomic, copy) NSString *title; + +// Text for the header view +@property (nonatomic, copy, nullable) NSString *text; + +// Ring View for the header +@property (nonatomic, readonly) OCKRingView *ringView; + +// Date for the day selected +@property (nonatomic, copy) NSString *date; + +// (see OCKGlyphType) selected +@property (nonatomic) OCKGlyphType glyphType; + +//Boolean true for OCKCareCardViewController, false for OCKSymptomTrackerViewCOntroller +@property (nonatomic) BOOL isCareCard; + +// Image required to show activity. +@property (nonatomic) UIImage *glyphImage; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/CareKit/SymptomTracker/OCKSymptomTrackerTableViewHeader.m b/CareKit/Common/OCKHeaderView.m similarity index 70% rename from CareKit/SymptomTracker/OCKSymptomTrackerTableViewHeader.m rename to CareKit/Common/OCKHeaderView.m index 4400fad0d..10c9bbf39 100644 --- a/CareKit/SymptomTracker/OCKSymptomTrackerTableViewHeader.m +++ b/CareKit/Common/OCKHeaderView.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -29,24 +29,21 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE */ -#import "OCKSymptomTrackerTableViewHeader.h" +#import "OCKHeaderView.h" #import "OCKRingView.h" #import "OCKHelpers.h" #import "OCKDefines_Private.h" #import "OCKLabel.h" -static const CGFloat TopMargin = 50.0; -static const CGFloat BottomMargin = 50.0; -static const CGFloat HorizontalMargin = 10.0; -static const CGFloat TrailingMargin = 20.0; static const CGFloat RingViewSize = 110.0; -@implementation OCKSymptomTrackerTableViewHeader { - OCKLabel *_titleLabel; +@implementation OCKHeaderView { OCKLabel *_dateLabel; - UIView *_topEdge; - UIView *_bottomEdge; + OCKLabel *_titleLabel; + UIStackView *_horizontalStackView; + UIStackView *_verticalStackView; + NSNumberFormatter *_numberFormatter; NSMutableArray *_constraints; } @@ -54,9 +51,9 @@ - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { if (!UIAccessibilityIsReduceTransparencyEnabled()) { - self.backgroundColor = [UIColor clearColor]; + self.backgroundColor = [UIColor groupTableViewBackgroundColor]; - UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]; + UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleProminent]; UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect]; blurEffectView.frame = self.bounds; blurEffectView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; @@ -73,35 +70,30 @@ - (instancetype)initWithFrame:(CGRect)frame { - (void)prepareView { if (!_ringView) { - _ringView = [[OCKRingView alloc] initWithFrame:CGRectMake(0, 0, RingViewSize, RingViewSize)]; + _ringView = [[OCKRingView alloc] initWithFrame:CGRectMake(0, 0, RingViewSize, RingViewSize) + useSmallRing:NO]; [self addSubview:_ringView]; } - if (!_titleLabel) { - _titleLabel = [OCKLabel new]; - _titleLabel.textStyle = UIFontTextStyleHeadline; - _titleLabel.numberOfLines = 2; - [self addSubview:_titleLabel]; - } - if (!_dateLabel) { _dateLabel = [OCKLabel new]; - _dateLabel.textStyle = UIFontTextStyleSubheadline; - _dateLabel.textColor = [UIColor darkGrayColor]; - _dateLabel.numberOfLines = 2; - [self addSubview:_dateLabel]; + _dateLabel.textStyle = UIFontTextStyleCaption1; + _dateLabel.textColor = [UIColor lightGrayColor]; + _dateLabel.numberOfLines = 0; } - if (!_topEdge) { - _topEdge = [UIView new]; - _topEdge.backgroundColor = [UIColor groupTableViewBackgroundColor]; - [self addSubview:_topEdge]; + if (!_titleLabel) { + _titleLabel = [OCKLabel new]; + _titleLabel.numberOfLines = 0; + _titleLabel.textStyle = UIFontTextStyleHeadline; } - if (!_bottomEdge) { - _bottomEdge = [UILabel new]; - _bottomEdge.backgroundColor = [UIColor groupTableViewBackgroundColor]; - [self addSubview:_bottomEdge]; + if (!_verticalStackView) { + _verticalStackView = [[UIStackView alloc] initWithArrangedSubviews:@[_dateLabel, _titleLabel]]; + _verticalStackView.spacing = 5.0; + _verticalStackView.axis = UILayoutConstraintAxisVertical; + _verticalStackView.distribution = UIStackViewDistributionEqualCentering; + [self addSubview:_verticalStackView]; } [self updateView]; @@ -109,10 +101,18 @@ - (void)prepareView { } - (void)updateView { + if (self.isCareCard) { + _titleLabel.text = [NSString stringWithFormat:OCKLocalizedString(@"HEADER_TITLE_CARE_OVERVIEW", nil), [self valuePercentageString]]; + } else { + _titleLabel.text = [NSString stringWithFormat:OCKLocalizedString(@"HEADER_TITLE_ACTIVITY_STATUS", nil), [self valuePercentageString]]; + } + _dateLabel.text = self.date; + self.ringView.tintColor = self.tintColor; + self.ringView.glyphImage = self.glyphImage; + self.ringView.isCareCard = self.isCareCard; + self.ringView.glyphType = self.glyphType; self.ringView.value = self.value; - _titleLabel.text = self.title; - _dateLabel.text = self.date; } - (void)setUpConstraints { @@ -120,137 +120,116 @@ - (void)setUpConstraints { _constraints = [NSMutableArray new]; - self.ringView.translatesAutoresizingMaskIntoConstraints = NO; + _ringView.translatesAutoresizingMaskIntoConstraints = NO; + _verticalStackView.translatesAutoresizingMaskIntoConstraints = NO; _titleLabel.translatesAutoresizingMaskIntoConstraints = NO; - _dateLabel.translatesAutoresizingMaskIntoConstraints = NO; - _topEdge.translatesAutoresizingMaskIntoConstraints = NO; - _bottomEdge.translatesAutoresizingMaskIntoConstraints = NO; [_constraints addObjectsFromArray:@[ - [NSLayoutConstraint constraintWithItem:self.ringView - attribute:NSLayoutAttributeCenterX + [NSLayoutConstraint constraintWithItem:_verticalStackView + attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeCenterX + toItem:_ringView + attribute:NSLayoutAttributeTrailing multiplier:1.0 - constant:-RingViewSize/1.25], - [NSLayoutConstraint constraintWithItem:self.ringView - attribute:NSLayoutAttributeCenterY - relatedBy:NSLayoutRelationEqual + constant:15.0], + [NSLayoutConstraint constraintWithItem:_verticalStackView + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationLessThanOrEqual toItem:self - attribute:NSLayoutAttributeCenterY - multiplier:1.0 - constant:0.0], - [NSLayoutConstraint constraintWithItem:self.ringView - attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute - multiplier:1.0 - constant:RingViewSize], - [NSLayoutConstraint constraintWithItem:self.ringView - attribute:NSLayoutAttributeWidth - relatedBy:NSLayoutRelationEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute + attribute:NSLayoutAttributeTrailing multiplier:1.0 - constant:RingViewSize], - [NSLayoutConstraint constraintWithItem:_titleLabel + constant:-10.0], + [NSLayoutConstraint constraintWithItem:_verticalStackView attribute:NSLayoutAttributeTop - relatedBy:NSLayoutRelationEqual + relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 - constant:TopMargin], - [NSLayoutConstraint constraintWithItem:_titleLabel - attribute:NSLayoutAttributeLeading - relatedBy:NSLayoutRelationEqual - toItem:self.ringView - attribute:NSLayoutAttributeTrailing - multiplier:1.0 - constant:HorizontalMargin], - [NSLayoutConstraint constraintWithItem:_titleLabel - attribute:NSLayoutAttributeTrailing - relatedBy:NSLayoutRelationEqual + constant:10.0], + [NSLayoutConstraint constraintWithItem:_verticalStackView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationLessThanOrEqual toItem:self - attribute:NSLayoutAttributeTrailing + attribute:NSLayoutAttributeBottom multiplier:1.0 - constant:-TrailingMargin], - [NSLayoutConstraint constraintWithItem:_dateLabel - attribute:NSLayoutAttributeLeading + constant:-10.0], + [NSLayoutConstraint constraintWithItem:_verticalStackView + attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual - toItem:_titleLabel - attribute:NSLayoutAttributeLeading + toItem:self + attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0], - [NSLayoutConstraint constraintWithItem:_dateLabel - attribute:NSLayoutAttributeTrailing + [NSLayoutConstraint constraintWithItem:_verticalStackView + attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self - attribute:NSLayoutAttributeTrailing + attribute:NSLayoutAttributeCenterX multiplier:1.0 - constant:-TrailingMargin], - [NSLayoutConstraint constraintWithItem:_dateLabel - attribute:NSLayoutAttributeBottom - relatedBy:NSLayoutRelationEqual + constant:RingViewSize/2 + 5], + [NSLayoutConstraint constraintWithItem:_ringView + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:self - attribute:NSLayoutAttributeBottom - multiplier:1.0 - constant:-BottomMargin], - [NSLayoutConstraint constraintWithItem:_titleLabel - attribute:NSLayoutAttributeBottom - relatedBy:NSLayoutRelationEqual - toItem:_dateLabel attribute:NSLayoutAttributeTop multiplier:1.0 - constant:0.0], - [NSLayoutConstraint constraintWithItem:_bottomEdge + constant:10.0], + [NSLayoutConstraint constraintWithItem:_ringView attribute:NSLayoutAttributeBottom - relatedBy:NSLayoutRelationEqual + relatedBy:NSLayoutRelationLessThanOrEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1.0 - constant:0.0], - [NSLayoutConstraint constraintWithItem:_bottomEdge - attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute - multiplier:1.0 - constant:1.0], - [NSLayoutConstraint constraintWithItem:_bottomEdge - attribute:NSLayoutAttributeWidth - relatedBy:NSLayoutRelationEqual + constant:-10.0], + [NSLayoutConstraint constraintWithItem:_ringView + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:self - attribute:NSLayoutAttributeWidth + attribute:NSLayoutAttributeLeading multiplier:1.0 - constant:0.0], - [NSLayoutConstraint constraintWithItem:_topEdge - attribute:NSLayoutAttributeTop + constant:10.0], + [NSLayoutConstraint constraintWithItem:_ringView + attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self - attribute:NSLayoutAttributeTop + attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0], - [NSLayoutConstraint constraintWithItem:_topEdge + [NSLayoutConstraint constraintWithItem:_ringView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 - constant:1.0], - [NSLayoutConstraint constraintWithItem:_topEdge + constant:RingViewSize], + [NSLayoutConstraint constraintWithItem:_ringView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual - toItem:self + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:RingViewSize], + [NSLayoutConstraint constraintWithItem:_titleLabel attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 - constant:0.0] + constant:160.0], ]]; [NSLayoutConstraint activateConstraints:_constraints]; } +- (NSString *)valuePercentageString { + if (!_numberFormatter) { + _numberFormatter = [NSNumberFormatter new]; + _numberFormatter.numberStyle = NSNumberFormatterPercentStyle; + _numberFormatter.maximumFractionDigits = 0; + } + return [_numberFormatter stringFromNumber:@(self.value)]; +} + - (void)setValue:(double)value { _value = value; [self updateView]; @@ -261,11 +240,36 @@ - (void)setDate:(NSString *)date { [self updateView]; } +- (void)setText:(NSString *)text { + _text = text; + [self updateView]; +} + +- (void)setTitle:(NSString *)title { + _title = title; + [self updateView]; +} + - (void)tintColorDidChange { [super tintColorDidChange]; [self updateView]; } +- (void)setGlyphType:(OCKGlyphType)glyphType { + _glyphType = glyphType; + [self updateView]; +} + +- (void)willMoveToWindow:(UIWindow *)newWindow { + [super willMoveToWindow:newWindow]; + + self.layer.shadowOffset = CGSizeMake(0, 1 / [UIScreen mainScreen].scale); + self.layer.shadowRadius = 0; + + self.layer.shadowColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:1].CGColor; + self.layer.shadowOpacity = 0.25; +} + #pragma mark - Accessibility diff --git a/CareKit/Common/OCKWeekLabelsView.h b/CareKit/Common/OCKWeekLabelsView.h index f91a393e8..3afdd3878 100644 --- a/CareKit/Common/OCKWeekLabelsView.h +++ b/CareKit/Common/OCKWeekLabelsView.h @@ -36,6 +36,8 @@ @property (nonatomic, readonly) NSMutableArray *weekLabels; +@property (nonatomic) BOOL isToday; + - (void)highlightDay:(NSInteger)selectedIndex; @end diff --git a/CareKit/Common/OCKWeekLabelsView.m b/CareKit/Common/OCKWeekLabelsView.m index e7a98ed72..333680342 100644 --- a/CareKit/Common/OCKWeekLabelsView.m +++ b/CareKit/Common/OCKWeekLabelsView.m @@ -65,8 +65,8 @@ - (void)prepareView { _weekLabels = [NSMutableArray new]; for (int i = 0; i < 7; i++) { UILabel *dayLabel = [UILabel new]; - dayLabel.font = [UIFont systemFontOfSize:12.0 weight:UIFontWeightThin]; - dayLabel.layer.cornerRadius = 3; + dayLabel.font = [UIFont systemFontOfSize:12.0 weight:UIFontWeightLight]; + dayLabel.layer.cornerRadius = 5.0; dayLabel.clipsToBounds = YES; dayLabel.text = _weekStrings[i]; dayLabel.textAlignment = NSTextAlignmentCenter; @@ -139,11 +139,14 @@ - (void)highlightDay:(NSInteger)selectedIndex { _selectedIndex = selectedIndex; for (UILabel *label in self.weekLabels) { - label.backgroundColor = [UIColor clearColor]; + label.layer.backgroundColor = [UIColor clearColor].CGColor; label.textColor = [UIColor blackColor]; } - self.weekLabels[selectedIndex].backgroundColor = self.tintColor; - self.weekLabels[selectedIndex].textColor = [UIColor whiteColor]; + + [UIView animateWithDuration:0.30 animations:^{ + self.weekLabels[selectedIndex].layer.backgroundColor = self.isToday ? [self.tintColor colorWithAlphaComponent:0.70].CGColor : [UIColor lightGrayColor].CGColor; + self.weekLabels[selectedIndex].textColor = [UIColor whiteColor]; + }]; } - (void)tintColorDidChange { diff --git a/CareKit/Common/OCKWeekViewController.h b/CareKit/Common/OCKWeekViewController.h index 20344fe60..b6133d4dc 100644 --- a/CareKit/Common/OCKWeekViewController.h +++ b/CareKit/Common/OCKWeekViewController.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -32,7 +32,9 @@ #import -@class OCKCareCardWeekView, OCKSymptomTrackerWeekView; +NS_ASSUME_NONNULL_BEGIN + +@class OCKWeekView; @protocol OCKWeekViewDelegate @@ -40,21 +42,15 @@ - (void)weekViewSelectionDidChange:(UIView *)weekView; -- (BOOL)weekViewCanSelectDayAtIndex:(NSUInteger)index; - @end @interface OCKWeekViewController : UIViewController -- (instancetype)initWithShowCareCardWeekView:(BOOL)showCareCardWeekView; - -@property (nonatomic) BOOL showCareCardWeekView; - -@property (nonatomic, readonly) OCKCareCardWeekView *careCardWeekView; - -@property (nonatomic, readonly) OCKSymptomTrackerWeekView *symptomTrackerWeekView; +@property (nonatomic, readonly) OCKWeekView *weekView; @property (nonatomic) NSInteger weekIndex; @end + +NS_ASSUME_NONNULL_END diff --git a/CareKit/Common/OCKWeekViewController.m b/CareKit/Common/OCKWeekViewController.m index bc731d243..098dfab92 100644 --- a/CareKit/Common/OCKWeekViewController.m +++ b/CareKit/Common/OCKWeekViewController.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -31,8 +31,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE #import "OCKWeekViewController.h" #import "OCKWeekLabelsView.h" -#import "OCKCareCardWeekView.h" -#import "OCKSymptomTrackerWeekView.h" +#import "OCKWeekView.h" @implementation OCKWeekViewController @@ -40,43 +39,18 @@ @implementation OCKWeekViewController - (instancetype)init { self = [super init]; if (self) { - _showCareCardWeekView = YES; _weekIndex = 0; [self prepareView]; } return self; } -- (instancetype)initWithShowCareCardWeekView:(BOOL)showCareCardWeekView { - self = [super init]; - if (self) { - _showCareCardWeekView = showCareCardWeekView; - [self prepareView]; - } - return self; -} - - (void)prepareView { - if (self.showCareCardWeekView) { - if (!_careCardWeekView) { - _careCardWeekView = [[OCKCareCardWeekView alloc] initWithFrame:self.view.bounds]; - _careCardWeekView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - [self.view addSubview:_careCardWeekView]; - [_symptomTrackerWeekView removeFromSuperview]; - } - } else { - if (!_symptomTrackerWeekView) { - _symptomTrackerWeekView = [[OCKSymptomTrackerWeekView alloc] initWithFrame:self.view.bounds]; - _symptomTrackerWeekView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - [self.view addSubview:_symptomTrackerWeekView]; - [_careCardWeekView removeFromSuperview]; - } - } -} + [_weekView removeFromSuperview]; + _weekView = [[OCKWeekView alloc] initWithFrame:self.view.bounds]; + _weekView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [self.view addSubview:_weekView]; -- (void)setShowCareCardWeekView:(BOOL)showCareCardWeekView { - _showCareCardWeekView = showCareCardWeekView; - [self prepareView]; } @end diff --git a/CareKit/Connect/OCKConnectDetailViewController.m b/CareKit/Connect/OCKConnectDetailViewController.m index 84b681f1f..7c05d9e77 100644 --- a/CareKit/Connect/OCKConnectDetailViewController.m +++ b/CareKit/Connect/OCKConnectDetailViewController.m @@ -115,11 +115,11 @@ - (void)createTableViewDataArray { NSMutableArray *contactInfoSection = [NSMutableArray new]; NSMutableArray *sharingSection = [NSMutableArray new]; - [contactInfoSection addObjectsFromArray:self.contact.contactInfoItems]; + [contactInfoSection addObjectsFromArray:self.contact.contactInfoItems]; if (self.delegate) { - NSString *sharingTitle = [self sharingTitle]; - [sharingSection addObject:sharingTitle]; + NSString *sharingTitle = [self sharingTitle]; + [sharingSection addObject:sharingTitle]; } if (contactInfoSection.count > 0) { @@ -135,14 +135,14 @@ - (void)createTableViewDataArray { } - (NSString *)sharingTitle { - NSString *sharingTitle = OCKLocalizedString(@"SHARING_CELL_TITLE", nil); - if ([self.delegate respondsToSelector:@selector(connectViewController:titleForSharingCellForContact:)]) { - NSString *delegateTitle = [self.delegate connectViewController:self.masterViewController titleForSharingCellForContact:self.contact]; - if (delegateTitle.length > 0) { - sharingTitle = delegateTitle; - } - } - return sharingTitle; + NSString *sharingTitle = OCKLocalizedString(@"SHARING_CELL_TITLE", nil); + if ([self.delegate respondsToSelector:@selector(connectViewController:titleForSharingCellForContact:)]) { + NSString *delegateTitle = [self.delegate connectViewController:self.masterViewController titleForSharingCellForContact:self.contact]; + if (delegateTitle.length > 0) { + sharingTitle = delegateTitle; + } + } + return sharingTitle; } - (void)makeCallToNumber:(NSString *)number { @@ -169,59 +169,59 @@ - (void)sendEmailToAddress:(NSString *)address delegate:(id *)defaultActionHandler { - BOOL handled = NO; - if (self.delegate && [self.delegate respondsToSelector:@selector(connectViewController:handleContactInfoSelected:)]) { - handled = [self.delegate connectViewController:self.masterViewController handleContactInfoSelected:contactInfo]; - } - - if (!handled) { - if (contactInfo.actionURL) { - [[UIApplication sharedApplication] openURL:contactInfo.actionURL]; - } else { - switch (contactInfo.type) { - case OCKContactInfoTypePhone: - [self makeCallToNumber:contactInfo.displayString]; - break; - - case OCKContactInfoTypeMessage: - [self sendMessageToNumber:contactInfo.displayString delegate:defaultActionHandler presentingViewController:defaultActionHandler]; - break; - - case OCKContactInfoTypeEmail: - [self sendEmailToAddress:contactInfo.displayString delegate:defaultActionHandler presentingViewController:defaultActionHandler]; - break; - - case OCKContactInfoTypeVideo: - [self makeVideoCallToNumber:contactInfo.displayString]; - break; - } - } - } + BOOL handled = NO; + if (self.delegate && [self.delegate respondsToSelector:@selector(connectViewController:handleContactInfoSelected:)]) { + handled = [self.delegate connectViewController:self.masterViewController handleContactInfoSelected:contactInfo]; + } + + if (!handled) { + if (contactInfo.actionURL) { + [[UIApplication sharedApplication] openURL:contactInfo.actionURL]; + } else { + switch (contactInfo.type) { + case OCKContactInfoTypePhone: + [self makeCallToNumber:contactInfo.displayString]; + break; + + case OCKContactInfoTypeMessage: + [self sendMessageToNumber:contactInfo.displayString delegate:defaultActionHandler presentingViewController:defaultActionHandler]; + break; + + case OCKContactInfoTypeEmail: + [self sendEmailToAddress:contactInfo.displayString delegate:defaultActionHandler presentingViewController:defaultActionHandler]; + break; + + case OCKContactInfoTypeVideo: + [self makeVideoCallToNumber:contactInfo.displayString]; + break; + } + } + } } - (void)sharingOptionSelectedWithSourceView:(UIView *)presentationSourceView { - if (self.delegate && - [self.delegate respondsToSelector:@selector(connectViewController:didSelectShareButtonForContact:presentationSourceView:)]) { - [self.delegate connectViewController:self.masterViewController didSelectShareButtonForContact:self.contact presentationSourceView:presentationSourceView]; - } + if (self.delegate && + [self.delegate respondsToSelector:@selector(connectViewController:didSelectShareButtonForContact:presentationSourceView:)]) { + [self.delegate connectViewController:self.masterViewController didSelectShareButtonForContact:self.contact presentationSourceView:presentationSourceView]; + } } #pragma mark - OCKContactInfoTableViewCellDelegate - (void)contactInfoTableViewCellDidSelectConnection:(OCKContactInfoTableViewCell *)cell { - [self contactInfoOptionSelected:cell.contactInfo defaultActionHandler:self]; + [self contactInfoOptionSelected:cell.contactInfo defaultActionHandler:self]; } #pragma mark - OCKContactSharingTableViewCellDelegate - (void)sharingTableViewCellDidSelectShareButton:(OCKContactSharingTableViewCell *)cell { - [self sharingOptionSelectedWithSourceView:cell.shareButton]; + [self sharingOptionSelectedWithSourceView:cell.shareButton]; } #pragma mark - UITableViewDelegate @@ -253,7 +253,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N if (!cell) { cell = [[OCKContactInfoTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ContactCellIdentifier]; - cell.tintColor = self.contact.tintColor; + cell.tintColor = self.contact.tintColor; } cell.contactInfo = _tableViewData[indexPath.section][indexPath.row]; cell.delegate = self; @@ -278,7 +278,7 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath NSString *sectionTitle = _sectionTitles[indexPath.section]; UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; - + if ([sectionTitle isEqualToString:_contactInfoSectionTitle]) { [self contactInfoTableViewCellDidSelectConnection:(OCKContactInfoTableViewCell*)cell]; } else if ([sectionTitle isEqualToString:_sharingSectionTitle]) { @@ -316,17 +316,17 @@ - (void)mailComposeController:(MFMailComposeViewController *)controller didFinis - (NSArray> *)previewActionItems { NSMutableArray> *actions = [[NSMutableArray alloc] init]; - for (OCKContactInfo *contactInfo in self.contact.contactInfoItems) { - NSString *capitalizedTitle = [contactInfo.label capitalizedStringWithLocale:[NSLocale currentLocale]]; - UIPreviewAction *previewAction = [UIPreviewAction actionWithTitle:capitalizedTitle style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) { - [self contactInfoOptionSelected:contactInfo defaultActionHandler:self.masterViewController]; - }]; - [actions addObject:previewAction]; - } + for (OCKContactInfo *contactInfo in self.contact.contactInfoItems) { + NSString *capitalizedTitle = [contactInfo.label capitalizedStringWithLocale:[NSLocale currentLocale]]; + UIPreviewAction *previewAction = [UIPreviewAction actionWithTitle:capitalizedTitle style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) { + [self contactInfoOptionSelected:contactInfo defaultActionHandler:self.masterViewController]; + }]; + [actions addObject:previewAction]; + } if (self.delegate) { - NSString *sharingTitle = [self sharingTitle]; - UIPreviewAction *shareAction = [UIPreviewAction actionWithTitle:sharingTitle style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) { - [self sharingOptionSelectedWithSourceView:nil]; + NSString *sharingTitle = [self sharingTitle]; + UIPreviewAction *shareAction = [UIPreviewAction actionWithTitle:sharingTitle style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) { + [self sharingOptionSelectedWithSourceView:nil]; }]; [actions addObject:shareAction]; } diff --git a/CareKit/Connect/OCKConnectHeaderView.h b/CareKit/Connect/OCKConnectHeaderView.h new file mode 100644 index 000000000..4cad2a039 --- /dev/null +++ b/CareKit/Connect/OCKConnectHeaderView.h @@ -0,0 +1,40 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import + + +@interface OCKConnectHeaderView : UIView + +@property (nonatomic) OCKPatient *patient; +@property (nonatomic) BOOL hideChevron; + +@end diff --git a/CareKit/Connect/OCKConnectHeaderView.m b/CareKit/Connect/OCKConnectHeaderView.m new file mode 100644 index 000000000..124b0930f --- /dev/null +++ b/CareKit/Connect/OCKConnectHeaderView.m @@ -0,0 +1,300 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import "OCKConnectHeaderView.h" +#import "OCKHelpers.h" +#import "OCKLabel.h" + + +static const CGFloat TopMargin = 15.0; +static const CGFloat BottomMargin = 15.0; +static const CGFloat LeadingMargin = 20.0; +static const CGFloat TrailingMargin = 20.0; +static const CGFloat VerticalMargin = 10.0; +static const CGFloat HorizontalMargin = 10.0; +static const CGFloat ImageViewSize = 75.0; + +@implementation OCKConnectHeaderView { + UIImageView *_imageView; + OCKLabel *_monogramLabel; + OCKLabel *_titleLabel; + OCKLabel *_detailLabel; + UIButton *_disclosureIndicator; + + NSMutableArray *_constraints; +} + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + _hideChevron = YES; + + [self prepareView]; + } + return self; +} + +- (void)prepareView { + if (!UIAccessibilityIsReduceTransparencyEnabled()) { + self.backgroundColor = [UIColor groupTableViewBackgroundColor]; + + UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleProminent]; + UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect]; + blurEffectView.frame = self.bounds; + blurEffectView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [self addSubview:blurEffectView]; + } + else { + self.backgroundColor = [UIColor whiteColor]; + } + + if (!_disclosureIndicator) { + _disclosureIndicator = [UIButton new]; + + UITableViewCell *disclosure = [UITableViewCell new]; + disclosure.frame = _disclosureIndicator.bounds; + disclosure.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + disclosure.userInteractionEnabled = NO; + [_disclosureIndicator addSubview:disclosure]; + _disclosureIndicator.hidden = _hideChevron; + [self addSubview:_disclosureIndicator]; + } + + if (!_imageView) { + _imageView = [UIImageView new]; + _imageView.layer.cornerRadius = 38.0; + _imageView.clipsToBounds = YES; + _imageView.contentMode = UIViewContentModeScaleAspectFill; + [self addSubview:_imageView]; + } + + if (!_titleLabel) { + _titleLabel = [OCKLabel new]; + _titleLabel.textStyle = UIFontTextStyleHeadline; + _titleLabel.numberOfLines = 0; + _titleLabel.lineBreakMode = NSLineBreakByWordWrapping; + _titleLabel.textAlignment = NSTextAlignmentCenter; + [self addSubview:_titleLabel]; + } + + if (!_detailLabel) { + _detailLabel = [OCKLabel new]; + _detailLabel.textStyle = UIFontTextStyleSubheadline; + _detailLabel.textColor = [UIColor lightGrayColor]; + _detailLabel.numberOfLines = 0; + _detailLabel.lineBreakMode = NSLineBreakByWordWrapping; + _detailLabel.textAlignment = NSTextAlignmentCenter; + [self addSubview:_detailLabel]; + } + + if (!_monogramLabel) { + _monogramLabel = [OCKLabel new]; + _monogramLabel.textColor = [UIColor whiteColor]; + _monogramLabel.textAlignment = NSTextAlignmentCenter; + _monogramLabel.font = [UIFont boldSystemFontOfSize:40.0]; + _monogramLabel.adjustsFontSizeToFitWidth = YES; + [self addSubview:_monogramLabel]; + } + + [self updateView]; + [self setUpConstraints]; +} + +- (void)updateView { + if (self.patient.image) { + _imageView.image = self.patient.image; + _imageView.backgroundColor = [UIColor clearColor]; + _monogramLabel.hidden = YES; + } else { + _monogramLabel.text = self.patient.monogram; + _imageView.backgroundColor = [UIColor grayColor]; + _monogramLabel.hidden = NO; + } + + _detailLabel.text = self.patient.detailInfo; + _titleLabel.text = self.patient.name; + _disclosureIndicator.hidden = self.hideChevron; +} + +- (void)setUpConstraints { + [NSLayoutConstraint deactivateConstraints:_constraints]; + + _constraints = [NSMutableArray new]; + + _imageView.translatesAutoresizingMaskIntoConstraints = NO; + _titleLabel.translatesAutoresizingMaskIntoConstraints = NO; + _detailLabel.translatesAutoresizingMaskIntoConstraints = NO; + _monogramLabel.translatesAutoresizingMaskIntoConstraints = NO; + _disclosureIndicator.translatesAutoresizingMaskIntoConstraints = NO; + + [_constraints addObjectsFromArray:@[ + [NSLayoutConstraint constraintWithItem:_imageView + attribute:NSLayoutAttributeCenterX + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeCenterX + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_imageView + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:TopMargin], + [NSLayoutConstraint constraintWithItem:_imageView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:_titleLabel + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:-VerticalMargin], + [NSLayoutConstraint constraintWithItem:_imageView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:ImageViewSize], + [NSLayoutConstraint constraintWithItem:_imageView + attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:ImageViewSize], + [NSLayoutConstraint constraintWithItem:_titleLabel + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:LeadingMargin], + [NSLayoutConstraint constraintWithItem:_titleLabel + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:-TrailingMargin], + [NSLayoutConstraint constraintWithItem:_titleLabel + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:_detailLabel + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_detailLabel + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:LeadingMargin], + [NSLayoutConstraint constraintWithItem:_detailLabel + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:-TrailingMargin], + [NSLayoutConstraint constraintWithItem:_detailLabel + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:-BottomMargin], + [NSLayoutConstraint constraintWithItem:_monogramLabel + attribute:NSLayoutAttributeCenterX + relatedBy:NSLayoutRelationEqual + toItem:_imageView + attribute:NSLayoutAttributeCenterX + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_monogramLabel + attribute:NSLayoutAttributeCenterY + relatedBy:NSLayoutRelationEqual + toItem:_imageView + attribute:NSLayoutAttributeCenterY + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_monogramLabel + attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:_imageView + attribute:NSLayoutAttributeWidth + multiplier:1.0 + constant:-HorizontalMargin],[NSLayoutConstraint constraintWithItem:_disclosureIndicator + attribute:NSLayoutAttributeCenterY + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeCenterY + multiplier:1.0 + constant:10.0], + [NSLayoutConstraint constraintWithItem:_disclosureIndicator + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:25.0]]]; + + [NSLayoutConstraint activateConstraints:_constraints]; +} + +- (void)setPatient:(OCKPatient *)patient { + _patient = patient; + [self updateView]; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + _titleLabel.preferredMaxLayoutWidth = _titleLabel.bounds.size.width; + _detailLabel.preferredMaxLayoutWidth = _detailLabel.bounds.size.width; +} + +- (void)tintColorDidChange { + [super tintColorDidChange]; + [self updateView]; +} + +- (void)willMoveToWindow:(UIWindow *)newWindow { + [super willMoveToWindow:newWindow]; + + self.layer.shadowOffset = CGSizeMake(0, 1 / [UIScreen mainScreen].scale); + self.layer.shadowRadius = 0; + + self.layer.shadowColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:1].CGColor; + self.layer.shadowOpacity = 0.25; +} + +@end diff --git a/CareKit/Connect/OCKConnectMessageItem.h b/CareKit/Connect/OCKConnectMessageItem.h new file mode 100644 index 000000000..c87ebd367 --- /dev/null +++ b/CareKit/Connect/OCKConnectMessageItem.h @@ -0,0 +1,100 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import +#import "OCKDefines.h" + + +NS_ASSUME_NONNULL_BEGIN + +/** + An enumeration of the types of message items available. + */ +OCK_ENUM_AVAILABLE +typedef NS_ENUM(NSInteger, OCKConnectMessageType) { + /** + A recieved message. + */ + OCKConnectMessageTypeReceived = 0, + + /** + A sent message. + */ + OCKConnectMessageTypeSent +}; + + +/** + The `OCKConnectMessageItem` is an object that can be used to display a message in the connect view controller. + */ +OCK_CLASS_AVAILABLE +@interface OCKConnectMessageItem : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +/** + Returns an initialzed message item using the specified values. + + @param type The message type (see `OCKConnectMessageType`). + @param name The contact name associated with the message. + @param message The content of the message. + @param dateString The string representation of the message date. + + @return An initialzed connect message item. + */ +- (instancetype)initWithMessageType:(OCKConnectMessageType)type + name:(NSString *)name + message:(NSString *)message + dateString:(NSString *)dateString; + +/** + The message type (see OCKConnectMessageType). + */ +@property (nonatomic, readonly) OCKConnectMessageType type; + +/** + A string indicating the contact name associated with the message. + */ +@property (nonatomic, copy, readonly) NSString *name; + +/** + A string indicating the message content. + */ +@property (nonatomic, copy, readonly) NSString *message; + +/** + A string indicating the date for the message. + */ +@property (nonatomic, readonly) NSString *dateString; + +@end + +NS_ASSUME_NONNULL_END diff --git a/CareKit/Connect/OCKConnectMessageItem.m b/CareKit/Connect/OCKConnectMessageItem.m new file mode 100644 index 000000000..c654bd9a1 --- /dev/null +++ b/CareKit/Connect/OCKConnectMessageItem.m @@ -0,0 +1,110 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import "OCKConnectMessageItem.h" +#import "OCKHelpers.h" + + +@implementation OCKConnectMessageItem + +- (instancetype)ock_init { + return [super init]; +} + +- (instancetype)init { + OCKThrowMethodUnavailableException(); + return nil; +} + +- (instancetype)initWithMessageType:(OCKConnectMessageType)type + name:(NSString *)name + message:(NSString *)message + dateString:(NSString *)dateString { + self = [super init]; + if (self) { + _type = type; + _name = [name copy]; + _message = [message copy]; + _dateString = [dateString copy]; + } + return self; +} + +- (BOOL)isEqual:(id)object { + if ([self class] != [object class]) { + return NO; + } + + __typeof(self) castObject = object; + return (self.type == castObject.type && + OCKEqualObjects(self.name, castObject.name) && + OCKEqualObjects(self.message, castObject.message) && + OCKEqualObjects(self.dateString, castObject.dateString)); +} + + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self) { + OCK_DECODE_ENUM(aDecoder, type); + OCK_DECODE_OBJ_CLASS(aDecoder, name, NSString); + OCK_DECODE_OBJ_CLASS(aDecoder, message, NSString); + OCK_DECODE_OBJ_CLASS(aDecoder, dateString, NSString); + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + OCK_ENCODE_ENUM(aCoder, type); + OCK_ENCODE_OBJ(aCoder, name); + OCK_ENCODE_OBJ(aCoder, message); + OCK_ENCODE_OBJ(aCoder, dateString); +} + + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone { + OCKConnectMessageItem *item = [[[self class] allocWithZone:zone] ock_init]; + item->_type = _type; + item->_name = [_name copy]; + item->_message = [_message copy]; + item->_dateString = [_dateString copy]; + return item; +} + +@end diff --git a/CareKit/Connect/OCKConnectMessageTableViewCell.h b/CareKit/Connect/OCKConnectMessageTableViewCell.h new file mode 100644 index 000000000..40e1e3911 --- /dev/null +++ b/CareKit/Connect/OCKConnectMessageTableViewCell.h @@ -0,0 +1,41 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import + + +@interface OCKConnectMessageTableViewCell : UITableViewCell + +@property (nonatomic) OCKConnectMessageItem *messageItem; + +@property (nonatomic) BOOL usePadding; + +@end diff --git a/CareKit/Connect/OCKConnectMessageTableViewCell.m b/CareKit/Connect/OCKConnectMessageTableViewCell.m new file mode 100644 index 000000000..1be63dbef --- /dev/null +++ b/CareKit/Connect/OCKConnectMessageTableViewCell.m @@ -0,0 +1,251 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import "OCKConnectMessageTableViewCell.h" +#import "OCKLabel.h" + + +static const CGFloat VerticalMargin = 13.0; +static const CGFloat HorizontalMargin = 13.0; +static const CGFloat PaddingMargin = 125.0; + +@implementation OCKConnectMessageTableViewCell { + OCKLabel *_nameLabel; + OCKLabel *_dateLabel; + OCKLabel *_messageLabel; + UIView *_containerView; + NSMutableArray *_constraints; +} + +- (void)setMessageItem:(OCKConnectMessageItem *)messageItem { + _messageItem = messageItem; + _usePadding = YES; + [self prepareView]; +} + +- (void)setUsePadding:(BOOL)usePadding { + _usePadding = usePadding; + [self setUpConstraints]; +} + +- (void)prepareView { + self.backgroundColor = [UIColor groupTableViewBackgroundColor]; + + OCKConnectMessageType type = self.messageItem.type; + UIColor *backgroundColor = (type == OCKConnectMessageTypeReceived) ? [UIColor whiteColor] : self.tintColor; + UIColor *primaryTextColor = (type == OCKConnectMessageTypeReceived) ? [UIColor blackColor] : [UIColor whiteColor]; + UIColor *secondaryTextColor = (type == OCKConnectMessageTypeReceived) ? [UIColor lightGrayColor] : [UIColor lightTextColor]; + UIColor *bodyTextColor = (type == OCKConnectMessageTypeReceived) ? [UIColor darkGrayColor] : [UIColor lightTextColor]; + + if (!_containerView) { + _containerView = [UIView new]; + _containerView.backgroundColor = backgroundColor; + _containerView.layer.cornerRadius = 5.0; + [self addSubview:_containerView]; + } + + if (!_nameLabel) { + _nameLabel = [OCKLabel new]; + _nameLabel.textStyle = UIFontTextStyleSubheadline; + _nameLabel.backgroundColor = backgroundColor; + _nameLabel.textColor = primaryTextColor; + _nameLabel.textAlignment = NSTextAlignmentLeft; + _nameLabel.lineBreakMode = NSLineBreakByWordWrapping; + [_containerView addSubview:_nameLabel]; + } + + if (!_dateLabel) { + _dateLabel = [OCKLabel new]; + _dateLabel.textStyle = UIFontTextStyleSubheadline; + _dateLabel.backgroundColor = backgroundColor; + _dateLabel.textColor = secondaryTextColor; + _dateLabel.textAlignment = NSTextAlignmentRight; + _dateLabel.lineBreakMode = NSLineBreakByWordWrapping; + [_containerView addSubview:_dateLabel]; + } + + if (!_messageLabel) { + _messageLabel = [OCKLabel new]; + _messageLabel.textStyle = UIFontTextStyleSubheadline; + _messageLabel.backgroundColor = backgroundColor; + _messageLabel.textColor = bodyTextColor; + _messageLabel.numberOfLines = 0; + _messageLabel.lineBreakMode = NSLineBreakByWordWrapping; + [_containerView addSubview:_messageLabel]; + } + + [self updateView]; + [self setUpConstraints]; +} + +- (void)updateView { + _nameLabel.text = self.messageItem.name; + _messageLabel.text = self.messageItem.message; + _dateLabel.text = self.messageItem.dateString; +} + +- (void)setUpConstraints { + [NSLayoutConstraint deactivateConstraints:_constraints]; + + _constraints = [NSMutableArray new]; + + _containerView.translatesAutoresizingMaskIntoConstraints = NO; + _nameLabel.translatesAutoresizingMaskIntoConstraints = NO; + _dateLabel.translatesAutoresizingMaskIntoConstraints = NO; + _messageLabel.translatesAutoresizingMaskIntoConstraints = NO; + + CGFloat paddingMargin = self.usePadding ? PaddingMargin : 75.0; + + if (self.messageItem.type == OCKConnectMessageTypeSent) { + [_constraints addObjectsFromArray:@[ + [NSLayoutConstraint constraintWithItem:_containerView + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:paddingMargin], + [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:_containerView + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:HorizontalMargin] + ]]; + } else { + [_constraints addObjectsFromArray:@[ + [NSLayoutConstraint constraintWithItem:_containerView + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:HorizontalMargin], + [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:_containerView + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:paddingMargin] + ]]; + } + + [_constraints addObjectsFromArray:@[ + [NSLayoutConstraint constraintWithItem:_containerView + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationGreaterThanOrEqual + toItem:self + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:VerticalMargin], + [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationGreaterThanOrEqual + toItem:_containerView + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:0.0] + ]]; + + [_constraints addObjectsFromArray:@[ + [NSLayoutConstraint constraintWithItem:_nameLabel + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:_containerView + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:VerticalMargin], + [NSLayoutConstraint constraintWithItem:_messageLabel + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:_nameLabel + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:VerticalMargin/2], + [NSLayoutConstraint constraintWithItem:_containerView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:_messageLabel + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:VerticalMargin], + [NSLayoutConstraint constraintWithItem:_dateLabel + attribute:NSLayoutAttributeBaseline + relatedBy:NSLayoutRelationEqual + toItem:_nameLabel + attribute:NSLayoutAttributeBaseline + multiplier:1.0 + constant:0.0], + + [NSLayoutConstraint constraintWithItem:_nameLabel + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:_containerView + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:HorizontalMargin], + [NSLayoutConstraint constraintWithItem:_dateLabel + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:_containerView + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:-HorizontalMargin], + [NSLayoutConstraint constraintWithItem:_nameLabel + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationLessThanOrEqual + toItem:_dateLabel + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:-HorizontalMargin], + [NSLayoutConstraint constraintWithItem:_messageLabel + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:_containerView + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:HorizontalMargin], + [NSLayoutConstraint constraintWithItem:_messageLabel + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:_containerView + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:-HorizontalMargin] + ]]; + + + [NSLayoutConstraint activateConstraints:_constraints]; +} + +@end diff --git a/CareKit/CareCard/OCKCareCardTableViewHeader.h b/CareKit/Connect/OCKConnectMessagesViewController.h similarity index 80% rename from CareKit/CareCard/OCKCareCardTableViewHeader.h rename to CareKit/Connect/OCKConnectMessagesViewController.h index ffd76ca3b..0fa316c99 100644 --- a/CareKit/CareCard/OCKCareCardTableViewHeader.h +++ b/CareKit/Connect/OCKConnectMessagesViewController.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -34,17 +34,15 @@ NS_ASSUME_NONNULL_BEGIN -@class OCKHeartView; +@interface OCKConnectMessagesViewController : UIViewController -@interface OCKCareCardTableViewHeader : UITableViewHeaderFooterView +@property (nonatomic, weak, nullable) id dataSource; -@property (nonatomic) double value; +@property (nonatomic, weak, nullable) id delegate; -@property (nonatomic, readonly) OCKHeartView *heartView; +@property (nonatomic) OCKContact *contact; -@property (nonatomic, copy) NSString *title; - -@property (nonatomic, copy) NSString *date; +@property (nonatomic, weak) OCKConnectViewController *masterViewController; @end diff --git a/CareKit/Connect/OCKConnectMessagesViewController.m b/CareKit/Connect/OCKConnectMessagesViewController.m new file mode 100644 index 000000000..eaa524204 --- /dev/null +++ b/CareKit/Connect/OCKConnectMessagesViewController.m @@ -0,0 +1,443 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import "OCKConnectMessagesViewController.h" +#import "OCKTextView.h" +#import "OCKConnectMessageTableViewCell.h" +#import "OCKDefines_Private.h" + + +static const CGFloat VerticalMargin = 7.0; +static const CGFloat HorizontalMargin = 13.0; +static const CGFloat TextViewHeight = 75.0; +static const CGFloat SeparatorViewHeight = 1.0; + +static NSString *EmptyString = @""; + +@interface OCKConnectMessagesViewController () + +@end + + +@implementation OCKConnectMessagesViewController { + UITableView *_tableView; + OCKTextView *_textView; + UIView *_separatorView; + UIButton *_sendButton; + NSMutableArray *_constraints; + BOOL _isKeyboardVisible; + NSString *_placeholderString; +} + +- (instancetype)init { + self = [super init]; + if (self) { + self.view.backgroundColor = [UIColor whiteColor]; + + [self prepareView]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(orientationChanged:) + name:@"UIDeviceOrientationDidChangeNotification" + object:nil]; + + UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap)]; + [self.view addGestureRecognizer:tap]; + [self registerForKeyboardNotifications]; + } + return self; +} + +- (void)viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + + _isKeyboardVisible = NO; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.navigationController.navigationBar.translucent = NO; + [self.navigationController.navigationBar setBarTintColor:[UIColor colorWithRed:245.0/255.0 green:244.0/255.0 blue:246.0/255.0 alpha:1.0]]; +} + +- (void)prepareView { + _placeholderString = OCKLocalizedString(@"CONNECT_MESSAGE_PLACEHOLDER", nil); + self.title = OCKLocalizedString(@"CONNECT_INBOX_TITLE", nil); + + if (!_tableView) { + _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; + _tableView.delegate = self; + _tableView.dataSource = self; + _tableView.estimatedRowHeight = 90.0; + _tableView.rowHeight = UITableViewAutomaticDimension; + _tableView.showsVerticalScrollIndicator = NO; + _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; + _tableView.backgroundColor = [UIColor groupTableViewBackgroundColor]; + _tableView.estimatedSectionHeaderHeight = 0; + _tableView.estimatedSectionFooterHeight = 0; + [self.view addSubview:_tableView]; + } + + if (!_separatorView) { + _separatorView = [UIView new]; + _separatorView.backgroundColor = [UIColor groupTableViewBackgroundColor]; + [self.view addSubview:_separatorView]; + } + + if (!_textView) { + _textView = [OCKTextView new]; + _textView.delegate = self; + _textView.text = _placeholderString; + [self.view addSubview:_textView]; + } + + if (!_sendButton) { + _sendButton = [UIButton buttonWithType:UIButtonTypeSystem]; + [_sendButton setTitle:OCKLocalizedString(@"CONNECT_SEND_BUTTON_TITLE", nil) forState:UIControlStateNormal]; + [_sendButton addTarget:self action:@selector(saveMessage:) forControlEvents:UIControlEventTouchUpInside]; + _sendButton.hidden = YES; + [self.view addSubview:_sendButton]; + } + + [self setUpConstraints]; +} + +- (void)setUpConstraints { + [NSLayoutConstraint deactivateConstraints:_constraints]; + + _constraints = [NSMutableArray new]; + + _tableView.translatesAutoresizingMaskIntoConstraints = NO; + _separatorView.translatesAutoresizingMaskIntoConstraints = NO; + _textView.translatesAutoresizingMaskIntoConstraints = NO; + _sendButton.translatesAutoresizingMaskIntoConstraints = NO; + + if (_sendButton.hidden) { + [_constraints addObjectsFromArray:@[ + [NSLayoutConstraint constraintWithItem:_textView + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:-HorizontalMargin] + ]]; + } else { + [_constraints addObjectsFromArray:@[ + [NSLayoutConstraint constraintWithItem:_sendButton + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:_textView + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_sendButton + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:_textView + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:7.0], + [NSLayoutConstraint constraintWithItem:self.view + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:_sendButton + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:HorizontalMargin] + ]]; + } + + [_constraints addObjectsFromArray:@[ + [NSLayoutConstraint constraintWithItem:_tableView + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.topLayoutGuide + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_tableView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:_separatorView + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_textView + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:_separatorView + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:2*VerticalMargin], + [NSLayoutConstraint constraintWithItem:_sendButton + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:_textView + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_textView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:self.bottomLayoutGuide + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:-2*VerticalMargin], + [NSLayoutConstraint constraintWithItem:_tableView + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_tableView + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_separatorView + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_separatorView + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_textView + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:HorizontalMargin], + [NSLayoutConstraint constraintWithItem:_separatorView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:SeparatorViewHeight], + [NSLayoutConstraint constraintWithItem:_textView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:TextViewHeight] + ]]; + + [NSLayoutConstraint activateConstraints:_constraints]; +} + +- (void)viewDidLayoutSubviews { + [super viewDidLayoutSubviews]; + [self setUpConstraints]; +} + +- (void)orientationChanged:(NSNotification *)notification { + [self setUpConstraints]; + [_tableView reloadData]; +} + +- (void)setContact:(OCKContact *)contact { + _contact = contact; + self.title = _contact.name; +} + + +#pragma mark - Helpers + +- (void)registerForKeyboardNotifications { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(keyboardWillShow:) + name:UIKeyboardWillShowNotification object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(keyboardWillHide:) + name:UIKeyboardWillHideNotification object:nil]; +} + +- (void)handleTap { + [self.view endEditing:YES]; +} + +- (void)loadConnectMessages { + if (self.dataSource && + [self.dataSource respondsToSelector:@selector(connectViewControllerNumberOfConnectMessageItems:careTeamContact:)]) { + NSInteger numberOfMessages = [self.dataSource connectViewControllerNumberOfConnectMessageItems:self.masterViewController careTeamContact:self.contact]; + [_tableView reloadData]; + + NSIndexPath *lastRowIndexPath = [NSIndexPath indexPathForRow:numberOfMessages - 1 inSection:0]; + [_tableView scrollToRowAtIndexPath:lastRowIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO]; + } +} + + +#pragma mark - Actions + +- (void)saveMessage:(id)sender { + if (![_textView.text isEqualToString:EmptyString]) { + // Send a call back to the CTP delegate with the new message. + if (self.delegate && + [self.delegate respondsToSelector:@selector(connectViewController:didSendConnectMessage:careTeamContact:)]) { + [self.delegate connectViewController:self.masterViewController didSendConnectMessage:[_textView.text copy] careTeamContact:self.contact]; + } + } + + [_textView endEditing:YES]; + _textView.text = _placeholderString; + _textView.textColor = [UIColor lightGrayColor]; + + [self loadConnectMessages]; +} + + +#pragma mark - Keyboard notifications + +- (void)keyboardWillShow:(NSNotification*)aNotification { + if (!_isKeyboardVisible) { + NSDictionary *info = [aNotification userInfo]; + NSNumber *duration = info[UIKeyboardAnimationDurationUserInfoKey]; + + CGRect reportedKeyboardFrameRaw = [[[aNotification userInfo] valueForKey: UIKeyboardFrameEndUserInfoKey] CGRectValue]; + CGRect reportedKeyboardFrame = [self.view.window convertRect: reportedKeyboardFrameRaw fromWindow:nil]; + CGRect visibleKeyboardFrame = CGRectIntersection(reportedKeyboardFrame, self.view.window.frame); + + if (reportedKeyboardFrame.size.height == visibleKeyboardFrame.size.height) { + [UIView animateWithDuration:duration.doubleValue animations:^{ + CGRect newFrame = self.view.frame; + newFrame.origin.y -= (visibleKeyboardFrame.size.height - 7*VerticalMargin); + self.view.frame = newFrame; + } completion:^(BOOL finished) {}]; + + _isKeyboardVisible = YES; + } + } +} + +- (void)keyboardWillHide:(NSNotification*)aNotification { + if (_isKeyboardVisible) { + NSDictionary *info = [aNotification userInfo]; + NSNumber *duration = info[UIKeyboardAnimationDurationUserInfoKey]; + + CGRect reportedKeyboardFrameRaw = [[[aNotification userInfo] valueForKey: UIKeyboardFrameEndUserInfoKey] CGRectValue]; + CGRect reportedKeyboardFrame = [self.view.window convertRect: reportedKeyboardFrameRaw fromWindow:nil]; + + [UIView animateWithDuration:duration.doubleValue animations:^{ + CGRect newFrame = self.view.frame; + newFrame.origin.y += (reportedKeyboardFrame.size.height - 7*VerticalMargin); + self.view.frame = newFrame; + } completion:^(BOOL finished) {}]; + + _isKeyboardVisible = NO; + } +} + + +#pragma mark - UITextViewDelegate + +- (void)textViewDidBeginEditing:(UITextView *)textView { + if ([_textView.text isEqualToString:_placeholderString]) { + _textView.text = EmptyString; + _textView.textColor = [UIColor darkGrayColor]; + + } + _sendButton.hidden = NO; + [self setUpConstraints]; +} + +- (void)textViewDidEndEditing:(UITextView *)textView { + if ([_textView.text isEqualToString:EmptyString]) { + _textView.text = _placeholderString; + _textView.textColor = [UIColor lightGrayColor]; + } + _sendButton.hidden = YES; + [self setUpConstraints]; +} + +- (void)textViewDidChange:(UITextView *)textView { + _sendButton.enabled = ![_textView.text isEqualToString:EmptyString]; +} + + +#pragma mark - UITableViewDelegate + +- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath { + return NO; +} + + +#pragma mark - UITableViewDataSource + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + NSInteger numberOfRows = 0; + if (self.dataSource && + [self.dataSource respondsToSelector:@selector(connectViewControllerNumberOfConnectMessageItems:careTeamContact:)]) { + numberOfRows = [self.dataSource connectViewControllerNumberOfConnectMessageItems:self.masterViewController careTeamContact:self.contact]; + } + return numberOfRows; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + if (self.dataSource && + [self.dataSource respondsToSelector:@selector(connectViewController:connectMessageItemAtIndex:careTeamContact:)]) { + + static NSString *ConnectMessageReceivedCellIdentifier = @"ConnectMessageReceivedCell"; + static NSString *ConnectMessageSentCellIdentifier = @"ConnectMessageSentCell"; + + OCKConnectMessageItem *item = [self.dataSource connectViewController:self.masterViewController connectMessageItemAtIndex:indexPath.row careTeamContact:self.contact]; + + NSString *cellIdentifier = (item.type == OCKConnectMessageTypeReceived) ? ConnectMessageReceivedCellIdentifier : ConnectMessageSentCellIdentifier; + OCKConnectMessageTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; + if (!cell) { + cell = [[OCKConnectMessageTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault + reuseIdentifier:cellIdentifier]; + } + cell.tintColor = self.view.tintColor; + cell.messageItem = item; + cell.usePadding = NO; + return cell; + } + + return nil; +} + +@end diff --git a/CareKit/Connect/OCKConnectTableViewCell.m b/CareKit/Connect/OCKConnectTableViewCell.m index 5b65a8a42..dfde0a94a 100644 --- a/CareKit/Connect/OCKConnectTableViewCell.m +++ b/CareKit/Connect/OCKConnectTableViewCell.m @@ -34,8 +34,8 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE #import "OCKLabel.h" -static const CGFloat TopMargin = 30.0; -static const CGFloat BottomMargin = 30.0; +static const CGFloat TopMargin = 25.0; +static const CGFloat BottomMargin = 25.0; static const CGFloat HorizontalMargin = 10.0; static const CGFloat ImageViewSize = 40.0; @@ -62,7 +62,6 @@ - (void)prepareView { _imageView = [UIImageView new]; _imageView.layer.cornerRadius = 20.0; _imageView.clipsToBounds = YES; - _imageView.layer.borderWidth = 1.0; _imageView.contentMode = UIViewContentModeScaleAspectFill; [self addSubview:_imageView]; } @@ -94,8 +93,6 @@ - (void)prepareView { } - (void)updateView { - _imageView.layer.borderColor = self.tintColor.CGColor; - if (self.contact.image) { _imageView.image = self.contact.image; _monogramLabel.text = nil; @@ -110,10 +107,6 @@ - (void)updateView { _relationLabel.text = self.contact.relation; } -- (void)updateImageViewBackgroundColor { - _imageView.backgroundColor = self.contact.image ? [UIColor clearColor] : OCKSystemGrayColor(); -} - - (void)setUpConstraints { [NSLayoutConstraint deactivateConstraints:_constraints]; @@ -238,6 +231,10 @@ - (void)setUpConstraints { [NSLayoutConstraint activateConstraints:_constraints]; } +- (void)updateImageViewBackgroundColor { + _imageView.backgroundColor = self.contact.image ? [UIColor clearColor] : OCKSystemGrayColor(); +} + - (void)setSelected:(BOOL)selected animated:(BOOL)animated { [super setSelected:selected animated:animated]; [self updateImageViewBackgroundColor]; diff --git a/CareKit/Connect/OCKConnectViewController.h b/CareKit/Connect/OCKConnectViewController.h index a8b703bf7..a83351342 100644 --- a/CareKit/Connect/OCKConnectViewController.h +++ b/CareKit/Connect/OCKConnectViewController.h @@ -1,7 +1,9 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. - Copyright (c) 2016, WWT Asynchrony Labs. All rights reserved. - Copyright (c) 2016, Erik Hornberger. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. + Copyright (c) 2017, WWT Asynchrony Labs. All rights reserved. + Copyright (c) 2017, Erik Hornberger. All rights reserved. + Copyright (c) 2017, Troy Tsubota. All rights reserved. + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -37,7 +39,44 @@ NS_ASSUME_NONNULL_BEGIN -@class OCKContact, OCKConnectViewController; +@class OCKContact, OCKConnectViewController, OCKConnectMessageItem; + +/** + An object that adopts the `OCKConnectViewControllerDataSource` protocol can use it provide connect message to be displayed. + */ +@protocol OCKConnectViewControllerDataSource + +@required +/** + Asks the data source for an array of contacts to be displayed under inbox. + + @param viewController The view controller providing the callback. + */ +- (NSArray *)connectViewControllerCareTeamConnections:(OCKConnectViewController *)viewController; + +/** + Asks the data source for a connect message item for a given index. + + The message items (connect messages) are displayed in the inbox under connect. + The `connectViewControllerNumberOfConnectMessageItems:` and `connectViewControllerCareTeamConnections:` implementations are required with this method. + + @param viewController The view controller providing the callback. + @param index The index of the table view row. + @param contact The care team contact. + */ +- (OCKConnectMessageItem *)connectViewController:(OCKConnectViewController *)viewController connectMessageItemAtIndex:(NSInteger)index careTeamContact:(OCKContact *)contact; + +/** + Asks the data source for the number of connect message items. + + The message items (connect messages) are displayed in the inbox under connect. + The `connectViewController:connectMessageItemAtIndex:` implementation is required with this method. + + @param viewController The view controller providing the callback. + @param contact The care team contact. + */ +- (NSInteger)connectViewControllerNumberOfConnectMessageItems:(OCKConnectViewController *)viewController careTeamContact:(OCKContact *)contact; +@end /** An object that adopts the `OCKConnectViewControllerDelegate` protocol is responsible for providing the @@ -69,7 +108,7 @@ NS_ASSUME_NONNULL_BEGIN - (nullable NSString *)connectViewController:(OCKConnectViewController *)connectViewController titleForSharingCellForContact:(OCKContact *)contact; /** - Asks the delegate to handle the selection of the contact info. This can be used to provide custom handling for + Asks the delegate to handle the selection of the contact info. This can be used to provide custom handling for contacting the contact. If the method is not implemented or if it returns NO then the default handling will be used instead. @@ -80,6 +119,23 @@ NS_ASSUME_NONNULL_BEGIN */ - (BOOL)connectViewController:(OCKConnectViewController *)connectViewController handleContactInfoSelected:(OCKContactInfo *)contactInfo; +/** + Tells the delegate when the user has sent a connect message. + + @param viewController The view controller providing the callback. + @param message The message that is being sent. + @param contact The care team contact the message is being sent to. + */ +- (void)connectViewController:(OCKConnectViewController *)viewController didSendConnectMessage:(NSString *)message careTeamContact:(OCKContact *)contact; + +/** + Tells the delegate when the user has tapped the profile header. + + @param viewController The view controller providing the callback. + @param patient The patient profile. + */ +- (void)connectViewController:(OCKConnectViewController *)viewController didSelectProfileForPatient:(OCKPatient *)patient; + @end @@ -90,6 +146,18 @@ NS_ASSUME_NONNULL_BEGIN OCK_CLASS_AVAILABLE @interface OCKConnectViewController : UIViewController +/** + Returns an initialized connect view controller using the specified contacts. + + @param contacts An array of `OCKContact` objects. + @param patient A patient object. + + @return An initialized connect view controller. + */ +- (instancetype)initWithContacts:(nullable NSArray *)contacts + patient:(nullable OCKPatient *)patient; + + /** Returns an initialized connect view controller using the specified contacts. @@ -104,6 +172,18 @@ OCK_CLASS_AVAILABLE */ @property (nonatomic, copy, nullable) NSArray *contacts; +/** + A patient object. + */ +@property (nonatomic, copy, nullable) OCKPatient *patient; + +/** + The data source can be used to provide connect message items. + + See the `OCKConnectViewControllerDataSource` protocol. + */ +@property (nonatomic, weak, nullable) id dataSource; + /** The delegate is used for the sharing section in the contact detail view. diff --git a/CareKit/Connect/OCKConnectViewController.m b/CareKit/Connect/OCKConnectViewController.m index 11f8e58f4..b7d9e2d62 100644 --- a/CareKit/Connect/OCKConnectViewController.m +++ b/CareKit/Connect/OCKConnectViewController.m @@ -1,7 +1,7 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. - Copyright (c) 2016, Troy Tsubota. All rights reserved. - Copyright (c) 2016, Erik Hornberger. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. + Copyright (c) 2017, Troy Tsubota. All rights reserved. + Copyright (c) 2017, Erik Hornberger. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -37,18 +37,21 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE #import "OCKHelpers.h" #import "OCKDefines_Private.h" #import "OCKLabel.h" +#import "OCKConnectMessagesViewController.h" +#import "OCKConnectHeaderView.h" @interface OCKConnectViewController() @end - @implementation OCKConnectViewController { + UITableView *_tableView; NSMutableArray *_constraints; NSMutableArray*> *_sectionedContacts; NSMutableArray *_sectionTitles; OCKLabel *_noContactsLabel; + OCKConnectHeaderView *_headerView; } + (instancetype)new { @@ -61,18 +64,26 @@ - (instancetype)init { return nil; } -- (instancetype)initWithContacts:(NSArray *)contacts { +- (instancetype)initWithContacts:(NSArray *)contacts + patient:(OCKPatient *)patient { self = [super init]; if (self) { _contacts = OCKArrayCopyObjects(contacts); - _showEdgeIndicators = NO; + _patient = patient; } return self; } +- (instancetype)initWithContacts:(NSArray *)contacts { + return [self initWithContacts:contacts + patient:nil]; +} + - (void)viewDidLoad { [super viewDidLoad]; + self.view.backgroundColor = [UIColor groupTableViewBackgroundColor]; + _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped]; _tableView.dataSource = self; _tableView.delegate = self; @@ -82,12 +93,27 @@ - (void)viewDidLoad { _tableView.estimatedRowHeight = 44.0; _tableView.rowHeight = UITableViewAutomaticDimension; + _tableView.showsVerticalScrollIndicator = NO; + _tableView.estimatedSectionHeaderHeight = 0; + _tableView.estimatedSectionFooterHeight = 0; + + self.navigationController.navigationBar.translucent = NO; + [self.navigationController.navigationBar setBarTintColor:[UIColor colorWithRed:245.0/255.0 green:244.0/255.0 blue:246.0/255.0 alpha:1.0]]; [self createSectionedContacts]; if ([self respondsToSelector:@selector(registerForPreviewingWithDelegate:sourceView:)]) { [self registerForPreviewingWithDelegate:self sourceView:_tableView]; } + + _headerView = [OCKConnectHeaderView new]; + _headerView.patient = _patient; + [self.view addSubview:_headerView]; + + UITapGestureRecognizer *singleFingerTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(profileHeaderTapped:)]; + [_headerView addGestureRecognizer:singleFingerTap]; + + [self updateHeaderView]; } - (void)viewWillAppear:(BOOL)animated { @@ -102,18 +128,34 @@ - (void)setContacts:(NSArray *)contacts { [_tableView reloadData]; } +- (void)setDataSource:(id)dataSource { + _dataSource = dataSource; + [_tableView reloadData]; + [self createSectionedContacts]; + + [self updateHeaderView]; +} + - (void)setUpConstraints { [NSLayoutConstraint deactivateConstraints:_constraints]; _constraints = [NSMutableArray new]; + _headerView.translatesAutoresizingMaskIntoConstraints = NO; _tableView.translatesAutoresizingMaskIntoConstraints = NO; [_constraints addObjectsFromArray:@[ - [NSLayoutConstraint constraintWithItem:_tableView + [NSLayoutConstraint constraintWithItem:_headerView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual - toItem:self.view + toItem:self.topLayoutGuide + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_headerView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:_tableView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0], @@ -136,6 +178,20 @@ - (void)setUpConstraints { relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_headerView + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_headerView + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0.0] ]]; @@ -168,6 +224,12 @@ - (void)createSectionedContacts { _sectionedContacts = [NSMutableArray new]; _sectionTitles = [NSMutableArray new]; + if ([self shouldInboxBeVisible]) { + NSArray *connections = [self.dataSource connectViewControllerCareTeamConnections:self]; + [_sectionedContacts addObject:connections]; + [_sectionTitles addObject:OCKLocalizedString(@"CONNECT_INBOX_TITLE", nil)]; + } + NSMutableArray *careTeamContacts = [NSMutableArray new]; NSMutableArray *personalContacts = [NSMutableArray new]; @@ -194,9 +256,6 @@ - (void)createSectionedContacts { } - (void)prepareHeaderView { - if (![self isViewLoaded]) { - return; - } if (self.contacts.count == 0) { if (!_noContactsLabel) { _noContactsLabel = [OCKLabel new]; @@ -210,19 +269,39 @@ - (void)prepareHeaderView { } } +- (void)updateHeaderView { + if (self.delegate && + [self.delegate respondsToSelector:@selector(connectViewController:didSelectProfileForPatient:)]) { + // Show disclosure indicator on profile tab. + _headerView.hideChevron = NO; + } else { + // Hide disclosure indicator on profile tab. + _headerView.hideChevron = YES; + } +} + - (void)viewWillLayoutSubviews { [super viewWillLayoutSubviews]; [self setUpConstraints]; } -- (void)setShowEdgeIndicators:(BOOL)showEdgeIndicators { - _showEdgeIndicators = showEdgeIndicators; - - [_tableView reloadData]; +- (void)profileHeaderTapped:(id)sender { + if (self.delegate && + [self.delegate respondsToSelector:@selector(connectViewController:didSelectProfileForPatient:)]) { + [self.delegate connectViewController:self didSelectProfileForPatient:self.patient]; + } } + #pragma mark - Helpers +- (BOOL)shouldInboxBeVisible { + return self.dataSource && + [self.dataSource respondsToSelector:@selector(connectViewControllerNumberOfConnectMessageItems:careTeamContact:)] && + [self.dataSource respondsToSelector:@selector(connectViewController:connectMessageItemAtIndex:careTeamContact:)] && + [self.dataSource respondsToSelector:@selector(connectViewControllerCareTeamConnections:)]; +} + - (OCKContact *)contactForIndexPath:(NSIndexPath *)indexPath { return _sectionedContacts[indexPath.section][indexPath.row]; } @@ -231,7 +310,6 @@ - (OCKConnectDetailViewController *)detailViewControllerForContact:(OCKContact * OCKConnectDetailViewController *detailViewController = [[OCKConnectDetailViewController alloc] initWithContact:contact]; detailViewController.delegate = self.delegate; detailViewController.masterViewController = self; - detailViewController.showEdgeIndicator = _showEdgeIndicators; return detailViewController; } @@ -239,9 +317,18 @@ - (OCKConnectDetailViewController *)detailViewControllerForContact:(OCKContact * #pragma mark - UITableViewDelegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - OCKContact *contact = [self contactForIndexPath:indexPath]; - [self.navigationController pushViewController:[self detailViewControllerForContact:contact] animated:YES]; + if ([self shouldInboxBeVisible] && indexPath.section == 0) { + OCKConnectMessagesViewController *viewController = [OCKConnectMessagesViewController new]; + viewController.dataSource = self.dataSource; + viewController.delegate = self.delegate; + viewController.masterViewController = self; + viewController.contact = [self.dataSource connectViewControllerCareTeamConnections:self][indexPath.row]; + [self.navigationController pushViewController:viewController animated:YES]; + } else { + OCKContact *contact = [self contactForIndexPath:indexPath]; + [self.navigationController pushViewController:[self detailViewControllerForContact:contact] animated:YES]; + } [tableView deselectRowAtIndexPath:indexPath animated:YES]; } @@ -258,20 +345,39 @@ - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInte } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + if ([self shouldInboxBeVisible] && section == 0) { + return [self.dataSource connectViewControllerCareTeamConnections:self].count; + } return _sectionedContacts[section].count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - static NSString *CellIdentifier = @"ConnectCell"; - OCKConnectTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; - if (!cell) { - cell = [[OCKConnectTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault - reuseIdentifier:CellIdentifier]; - } - cell.contact = _sectionedContacts[indexPath.section][indexPath.row]; - cell.showEdgeIndicator = self.showEdgeIndicators; - return cell; + if ([self shouldInboxBeVisible] && indexPath.section == 0) { + static NSString *ConnectMessageCellIdentifier = @"ConnectMessageCell"; + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ConnectMessageCellIdentifier]; + if (!cell) { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault + reuseIdentifier:ConnectMessageCellIdentifier]; + } + cell.imageView.image = [[UIImage imageNamed:@"message" inBundle:OCKBundle() compatibleWithTraitCollection:nil] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + cell.imageView.tintColor = [UIColor lightGrayColor]; + cell.tintColor = self.view.tintColor; + cell.textLabel.text = _sectionedContacts[indexPath.section][indexPath.row].name; + cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + return cell; + + } else { + static NSString *CellIdentifier = @"ConnectCell"; + OCKConnectTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; + if (!cell) { + cell = [[OCKConnectTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault + reuseIdentifier:CellIdentifier]; + } + cell.contact = _sectionedContacts[indexPath.section][indexPath.row]; + return cell; + } + return nil; } #pragma mark - MFMessageComposeViewControllerDelegate @@ -286,6 +392,7 @@ - (void)messageComposeViewController:(MFMessageComposeViewController *)controlle } } + #pragma mark - MFMailComposeViewControllerDelegate - (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error { @@ -298,15 +405,21 @@ - (void)mailComposeController:(MFMailComposeViewController *)controller didFinis } } + #pragma mark - UIViewControllerPreviewingDelegate - (UIViewController *)previewingContext:(id )previewingContext viewControllerForLocation:(CGPoint)location { NSIndexPath *indexPath = [_tableView indexPathForRowAtPoint:location]; - OCKContact *contact = [self contactForIndexPath:indexPath]; - if (indexPath) { - CGRect cellFrame = [_tableView cellForRowAtIndexPath:indexPath].frame; - previewingContext.sourceRect = cellFrame; + if ([self shouldInboxBeVisible] && indexPath.section == 0) { + OCKConnectMessagesViewController *viewController = [OCKConnectMessagesViewController new]; + viewController.dataSource = self.dataSource; + viewController.delegate = self.delegate; + viewController.masterViewController = self; + viewController.contact = [self.dataSource connectViewControllerCareTeamConnections:self][indexPath.row]; + return viewController; + } else { + OCKContact *contact = [self contactForIndexPath:indexPath]; return [self detailViewControllerForContact:contact]; } diff --git a/CareKit/Connect/OCKContactInfoTableViewCell.m b/CareKit/Connect/OCKContactInfoTableViewCell.m index 95d81bcb3..20c6acd67 100644 --- a/CareKit/Connect/OCKContactInfoTableViewCell.m +++ b/CareKit/Connect/OCKContactInfoTableViewCell.m @@ -97,7 +97,7 @@ - (void)setUpConstraints { _connectTypeLabel.translatesAutoresizingMaskIntoConstraints = NO; CGFloat LeadingMargin = self.separatorInset.left; - CGFloat TrailingMargin = (self.separatorInset.right > 0) ? self.separatorInset.right : 20; + CGFloat TrailingMargin = (self.separatorInset.right > 0) ? self.separatorInset.right : 15; [_constraints addObjectsFromArray:@[ [NSLayoutConstraint constraintWithItem:_connectTypeLabel diff --git a/CareKit/Connect/OCKContactSharingTableViewCell.h b/CareKit/Connect/OCKContactSharingTableViewCell.h index c3ee338e2..4075b2384 100644 --- a/CareKit/Connect/OCKContactSharingTableViewCell.h +++ b/CareKit/Connect/OCKContactSharingTableViewCell.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Connect/OCKContactSharingTableViewCell.m b/CareKit/Connect/OCKContactSharingTableViewCell.m index d33495db8..f0dd1f0fb 100644 --- a/CareKit/Connect/OCKContactSharingTableViewCell.m +++ b/CareKit/Connect/OCKContactSharingTableViewCell.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -89,7 +89,7 @@ - (void)setUpConstraints { _shareButton.translatesAutoresizingMaskIntoConstraints = NO; CGFloat LeadingMargin = self.separatorInset.left; - CGFloat TrailingMargin = (self.separatorInset.right > 0) ? self.separatorInset.right : 20; + CGFloat TrailingMargin = (self.separatorInset.right > 0) ? self.separatorInset.right : 15; [_constraints addObjectsFromArray:@[ [NSLayoutConstraint constraintWithItem:_titleLabel diff --git a/CareKit/CareCard/OCKHeartView.h b/CareKit/Connect/OCKTextView.h similarity index 88% rename from CareKit/CareCard/OCKHeartView.h rename to CareKit/Connect/OCKTextView.h index 99e6e9fa1..622c1d597 100644 --- a/CareKit/CareCard/OCKHeartView.h +++ b/CareKit/Connect/OCKTextView.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -32,12 +32,6 @@ #import -@interface OCKHeartView : UIView - -@property (nonatomic) BOOL animationEnabled; - -@property (nonatomic) UIImage *maskImage; - -@property (nonatomic) double value; +@interface OCKTextView : UITextView @end diff --git a/CareKit/Connect/OCKTextView.m b/CareKit/Connect/OCKTextView.m new file mode 100644 index 000000000..ae213e268 --- /dev/null +++ b/CareKit/Connect/OCKTextView.m @@ -0,0 +1,52 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import "OCKTextView.h" + + +@implementation OCKTextView + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + self.textColor = [UIColor lightGrayColor]; + self.font = [UIFont systemFontOfSize:[UIFont systemFontSize]]; + self.textContainerInset = UIEdgeInsetsMake(10.0, 3.0, 10.0, 3.0); + self.layer.borderColor = [UIColor groupTableViewBackgroundColor].CGColor; + self.layer.borderWidth = 1.0; + self.layer.cornerRadius = 10.0; + self.showsVerticalScrollIndicator = NO; + self.autocorrectionType = UITextAutocorrectionTypeNo; + } + return self; +} + +@end diff --git a/CareKit/Info.plist b/CareKit/Info.plist index d3de8eefb..602d86443 100644 --- a/CareKit/Info.plist +++ b/CareKit/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.0 + 1.2 CFBundleSignature ???? CFBundleVersion diff --git a/CareKit/Insights/OCKBarChart.h b/CareKit/Insights/OCKBarChart.h index 19dea8ac1..96f3ce3ed 100644 --- a/CareKit/Insights/OCKBarChart.h +++ b/CareKit/Insights/OCKBarChart.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Insights/OCKBarChart.m b/CareKit/Insights/OCKBarChart.m index 6a8202794..50df51a83 100644 --- a/CareKit/Insights/OCKBarChart.m +++ b/CareKit/Insights/OCKBarChart.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Insights/OCKBarSeries.h b/CareKit/Insights/OCKBarSeries.h index 44d30b172..2dd36b2bd 100644 --- a/CareKit/Insights/OCKBarSeries.h +++ b/CareKit/Insights/OCKBarSeries.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Insights/OCKBarSeries.m b/CareKit/Insights/OCKBarSeries.m index 4946dc68f..bff61476a 100644 --- a/CareKit/Insights/OCKBarSeries.m +++ b/CareKit/Insights/OCKBarSeries.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Insights/OCKChart.h b/CareKit/Insights/OCKChart.h index 5eb55f414..eb99c6c10 100644 --- a/CareKit/Insights/OCKChart.h +++ b/CareKit/Insights/OCKChart.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Insights/OCKChart.m b/CareKit/Insights/OCKChart.m index d936e08fd..96f7e7356 100644 --- a/CareKit/Insights/OCKChart.m +++ b/CareKit/Insights/OCKChart.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Insights/OCKGroupedBarChartView.h b/CareKit/Insights/OCKGroupedBarChartView.h index e01e67527..d518c5c59 100644 --- a/CareKit/Insights/OCKGroupedBarChartView.h +++ b/CareKit/Insights/OCKGroupedBarChartView.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Insights/OCKGroupedBarChartView.m b/CareKit/Insights/OCKGroupedBarChartView.m index ed2d64047..e538c3101 100644 --- a/CareKit/Insights/OCKGroupedBarChartView.m +++ b/CareKit/Insights/OCKGroupedBarChartView.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -38,9 +38,9 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE static const CGFloat BarPointSize = 12.0; static const CGFloat BarEndFontSize = 11.0; -static const CGFloat MarginBetweenBars = 2.0; +static const CGFloat MarginBetweenBars = 4.0; static const CGFloat MarginBetweenGroups = 16.0; -static const CGFloat MarginBetweenBarAndLabel = 6.0; +static const CGFloat MarginBetweenBarAndLabel = 10.0; @interface OCKGroupedBarChartBar : NSObject @@ -153,6 +153,7 @@ - (void)layoutSubviews { _barLayer.path = path.CGPath; _barLayer.strokeColor = _bar.color.CGColor; _barLayer.lineWidth = barHeight; + _barLayer.lineCap = @"round"; } } @@ -182,7 +183,7 @@ - (void)prepareView { NSMutableArray *constraints = [NSMutableArray new]; - NSString *visualFormat = [NSString stringWithFormat:@"H:|[barView]-%f-[valueLabel]", MarginBetweenBarAndLabel]; + NSString *visualFormat = [NSString stringWithFormat:@"H:|[barView]-%f-[valueLabel]|", MarginBetweenBarAndLabel]; [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:visualFormat options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil @@ -196,7 +197,7 @@ - (void)prepareView { toItem:self attribute:NSLayoutAttributeWidth multiplier:percentage - constant:-(_bar.valueLabelMaxWidth+6.0)*percentage]]; + constant:-(_bar.valueLabelMaxWidth+10.0)*percentage]]; [constraints addObject:[NSLayoutConstraint constraintWithItem:_barView attribute:NSLayoutAttributeHeight diff --git a/CareKit/Insights/OCKInsightItem.h b/CareKit/Insights/OCKInsightItem.h index 2b418cd05..e567a49ee 100644 --- a/CareKit/Insights/OCKInsightItem.h +++ b/CareKit/Insights/OCKInsightItem.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Insights/OCKInsightItem.m b/CareKit/Insights/OCKInsightItem.m index 5f6f1063b..c453a9704 100644 --- a/CareKit/Insights/OCKInsightItem.m +++ b/CareKit/Insights/OCKInsightItem.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Insights/OCKInsightsMessageTableViewCell.m b/CareKit/Insights/OCKInsightsMessageTableViewCell.m index 1454c475f..ae3b0eb5e 100644 --- a/CareKit/Insights/OCKInsightsMessageTableViewCell.m +++ b/CareKit/Insights/OCKInsightsMessageTableViewCell.m @@ -204,6 +204,10 @@ - (NSString *)stringForMessageType:(OCKMessageItemType)type { case OCKMessageItemTypeTip: string = TipSymbol; break; + + case OCKMessageItemTypePlain: + string = nil; + break; } return string; } diff --git a/CareKit/CareCard/OCKHeartButton.h b/CareKit/Insights/OCKInsightsRingTableViewCell.h similarity index 92% rename from CareKit/CareCard/OCKHeartButton.h rename to CareKit/Insights/OCKInsightsRingTableViewCell.h index e171b20de..bc0e8f6fd 100644 --- a/CareKit/CareCard/OCKHeartButton.h +++ b/CareKit/Insights/OCKInsightsRingTableViewCell.h @@ -29,13 +29,11 @@ */ -#import +#import "OCKTableViewCell.h" -@class OCKHeartView; +@interface OCKInsightsRingTableViewCell : OCKTableViewCell -@interface OCKHeartButton : UIButton - -@property (nonatomic) OCKHeartView *heartView; +@property (nonatomic) OCKRingItem *ringItem; @end diff --git a/CareKit/CareCard/OCKHeartButton.m b/CareKit/Insights/OCKInsightsRingTableViewCell.m similarity index 70% rename from CareKit/CareCard/OCKHeartButton.m rename to CareKit/Insights/OCKInsightsRingTableViewCell.m index 1febdf770..01d7b4038 100644 --- a/CareKit/CareCard/OCKHeartButton.m +++ b/CareKit/Insights/OCKInsightsRingTableViewCell.m @@ -29,62 +29,96 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE */ -#import "OCKHeartButton.h" -#import "OCKHeartView.h" +#import "OCKInsightsRingTableViewCell.h" +#import "OCKRingItem.h" +#import "OCKHeaderView.h" -static const CGFloat HeartViewSize = 30.0; +static const CGFloat LeadingMargin = 3.0; +static const CGFloat TrailingMargin = 18.0; -@implementation OCKHeartButton { +@implementation OCKInsightsRingTableViewCell { + OCKHeaderView *_headerView; NSMutableArray *_constraints; } -- (void)setHeartView:(OCKHeartView *)heartView { - [_heartView removeFromSuperview]; - _heartView = heartView; - [self addSubview:_heartView]; +- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; + if (self) { + [self prepareView]; + } + return self; +} + +- (void)setRingItem:(OCKRingItem *)ringItem { + _ringItem = ringItem; + [self updateView]; +} + +- (void)prepareView { + [super prepareView]; + if (!_headerView) { + _headerView = [OCKHeaderView new]; + [self addSubview:_headerView]; + } + + [self updateView]; [self setUpConstraints]; } +- (void)updateView { + _headerView.title = self.ringItem.title; + _headerView.text = self.ringItem.text; + _headerView.value = self.ringItem.value; + _headerView.tintColor = self.ringItem.tintColor; + _headerView.glyphType = self.ringItem.glyphType; +} + - (void)setUpConstraints { [NSLayoutConstraint deactivateConstraints:_constraints]; _constraints = [NSMutableArray new]; - self.heartView.translatesAutoresizingMaskIntoConstraints = NO; + + _headerView.translatesAutoresizingMaskIntoConstraints = NO; [_constraints addObjectsFromArray:@[ - [NSLayoutConstraint constraintWithItem:self.heartView - attribute:NSLayoutAttributeCenterX + [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeCenterX + toItem:_headerView + attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0], - [NSLayoutConstraint constraintWithItem:self.heartView - attribute:NSLayoutAttributeCenterY + [NSLayoutConstraint constraintWithItem:_headerView + attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self - attribute:NSLayoutAttributeCenterY + attribute:NSLayoutAttributeBottom multiplier:1.0 - constant:8.0], - [NSLayoutConstraint constraintWithItem:self.heartView - attribute:NSLayoutAttributeWidth + constant:0.0], + [NSLayoutConstraint constraintWithItem:_headerView + attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute + toItem:self + attribute:NSLayoutAttributeLeading multiplier:1.0 - constant:HeartViewSize], - [NSLayoutConstraint constraintWithItem:self.heartView - attribute:NSLayoutAttributeHeight + constant:LeadingMargin], + [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute + toItem:_headerView + attribute:NSLayoutAttributeTrailing multiplier:1.0 - constant:HeartViewSize], + constant:TrailingMargin] ]]; [NSLayoutConstraint activateConstraints:_constraints]; } +- (void)tintColorDidChange { + [super tintColorDidChange]; + [self updateView]; +} + @end diff --git a/CareKit/Insights/OCKInsightsTableViewHeaderView.h b/CareKit/Insights/OCKInsightsTableViewHeaderView.h index b36e2f1ad..25cc1dbfe 100644 --- a/CareKit/Insights/OCKInsightsTableViewHeaderView.h +++ b/CareKit/Insights/OCKInsightsTableViewHeaderView.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -32,10 +32,19 @@ #import +NS_ASSUME_NONNULL_BEGIN + @interface OCKInsightsTableViewHeaderView : UIView -@property (nonatomic, copy) NSString *title; +- (instancetype)initWithWidgets:(NSArray *)widgets + store:(nullable OCKCarePlanStore *)store; + +@property (nonatomic, copy, readonly) NSArray *widgets; -@property (nonatomic, copy) NSString *subtitle; +@property (nonatomic, copy, readonly) OCKCarePlanStore *store; + +- (void)updateWidgets; @end + +NS_ASSUME_NONNULL_END diff --git a/CareKit/Insights/OCKInsightsTableViewHeaderView.m b/CareKit/Insights/OCKInsightsTableViewHeaderView.m index 20a1aa5e4..6064ac435 100644 --- a/CareKit/Insights/OCKInsightsTableViewHeaderView.m +++ b/CareKit/Insights/OCKInsightsTableViewHeaderView.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -31,54 +31,61 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE #import "OCKInsightsTableViewHeaderView.h" #import "OCKHelpers.h" -#import "OCKLabel.h" +#import "OCKPatientWidgetView.h" +#import "OCKPatientWidget_Internal.h" -static const CGFloat TopMargin = 20.0; -static const CGFloat LeadingMargin = 15.0; -static const CGFloat TrailingMargin = 15.0; -static const CGFloat BottomMargin = 20.0; - @implementation OCKInsightsTableViewHeaderView { - OCKLabel *_titleLabel; - OCKLabel *_subtitleLabel; + UIStackView *_stackView; NSMutableArray *_constraints; + NSMutableArray *_widgetViews; + NSNumberFormatter *_numberFormatter; } -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; +- (instancetype)initWithWidgets:(NSArray *)widgets + store:(OCKCarePlanStore *)store { + self = [super init]; if (self) { + _widgets = OCKArrayCopyObjects(widgets); + _store = store; + [self prepareView]; } return self; } - (void)prepareView { - if (!_titleLabel) { - _titleLabel = [OCKLabel new]; - _titleLabel.backgroundColor = OCKSystemGrayColor(); - _titleLabel.textColor = [UIColor darkGrayColor]; - _titleLabel.textStyle = UIFontTextStyleHeadline; - [self addSubview:_titleLabel]; + if (!UIAccessibilityIsReduceTransparencyEnabled()) { + self.backgroundColor = [UIColor groupTableViewBackgroundColor]; + + UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleProminent]; + UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect]; + blurEffectView.frame = self.bounds; + blurEffectView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [self addSubview:blurEffectView]; + } + else { + self.backgroundColor = [UIColor whiteColor]; } - if (!_subtitleLabel) { - _subtitleLabel = [OCKLabel new]; - _subtitleLabel.backgroundColor = OCKSystemGrayColor(); - _subtitleLabel.textStyle = UIFontTextStyleSubheadline; - _subtitleLabel.numberOfLines = 2; - _subtitleLabel.lineBreakMode = NSLineBreakByWordWrapping; - _subtitleLabel.textColor = [UIColor darkGrayColor]; - [self addSubview:_subtitleLabel]; + _widgetViews = [NSMutableArray new]; + + for (OCKPatientWidget *widget in self.widgets) { + OCKPatientWidgetView *widgetView = [OCKPatientWidgetView viewForWidget:widget]; + [_widgetViews addObject:widgetView]; } - [self updateView]; + + _stackView = [[UIStackView alloc] initWithArrangedSubviews:_widgetViews]; + _stackView.alignment = UIStackViewAlignmentCenter; + _stackView.distribution = UIStackViewDistributionFillEqually; + _stackView.spacing = 10.0; + + [self addSubview:_stackView]; + [self setUpConstraints]; -} - -- (void)updateView { - _titleLabel.text = self.title; - _subtitleLabel.text = self.subtitle; + [self updateWidgets]; + } - (void)setUpConstraints { @@ -86,103 +93,163 @@ - (void)setUpConstraints { _constraints = [NSMutableArray new]; - _titleLabel.translatesAutoresizingMaskIntoConstraints = NO; - _subtitleLabel.translatesAutoresizingMaskIntoConstraints = NO; + _stackView.translatesAutoresizingMaskIntoConstraints = NO; [_constraints addObjectsFromArray:@[ - [NSLayoutConstraint constraintWithItem:_titleLabel + [NSLayoutConstraint constraintWithItem:_stackView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 - constant:TopMargin], - [NSLayoutConstraint constraintWithItem:_titleLabel + constant:0.0], + [NSLayoutConstraint constraintWithItem:_stackView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeading multiplier:1.0 - constant:LeadingMargin], - [NSLayoutConstraint constraintWithItem:_titleLabel - attribute:NSLayoutAttributeBottom - relatedBy:NSLayoutRelationEqual - toItem:_subtitleLabel - attribute:NSLayoutAttributeTop - multiplier:1.0 - constant:0.0], - [NSLayoutConstraint constraintWithItem:_subtitleLabel - attribute:NSLayoutAttributeLeading + constant:20.0], + [NSLayoutConstraint constraintWithItem:_stackView + attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self - attribute:NSLayoutAttributeLeading + attribute:NSLayoutAttributeTrailing multiplier:1.0 - constant:LeadingMargin], - [NSLayoutConstraint constraintWithItem:self + constant:-20.0], + [NSLayoutConstraint constraintWithItem:_stackView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual - toItem:_subtitleLabel + toItem:self attribute:NSLayoutAttributeBottom multiplier:1.0 - constant:BottomMargin] + constant:0.0], + [NSLayoutConstraint constraintWithItem:_stackView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationGreaterThanOrEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:90.0] ]]; - - NSLayoutConstraint *titleTrailingConstraint = [NSLayoutConstraint constraintWithItem:_titleLabel - attribute:NSLayoutAttributeTrailing - relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeTrailing - multiplier:1.0 - constant:-TrailingMargin]; - titleTrailingConstraint.priority = 999; - - NSLayoutConstraint *subtitleTrailingConstraint = [NSLayoutConstraint constraintWithItem:_subtitleLabel - attribute:NSLayoutAttributeTrailing - relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeTrailing - multiplier:1.0 - constant:-TrailingMargin]; - subtitleTrailingConstraint.priority = 999; - - [_constraints addObject:titleTrailingConstraint]; - [_constraints addObject:subtitleTrailingConstraint]; - [NSLayoutConstraint activateConstraints:_constraints]; } -- (void)setTitle:(NSString *)title { - _title = [title copy]; - [self updateView]; -} - -- (void)setSubtitle:(NSString *)subtitle { - _subtitle = [subtitle copy]; - [self updateView]; -} - -- (void)setBackgroundColor:(UIColor *)backgroundColor { - [super setBackgroundColor:backgroundColor]; - _titleLabel.backgroundColor = backgroundColor; - _subtitleLabel.backgroundColor = backgroundColor; -} - - (void)layoutSubviews { [super layoutSubviews]; - _titleLabel.preferredMaxLayoutWidth = _titleLabel.bounds.size.width; - _subtitleLabel.preferredMaxLayoutWidth = _subtitleLabel.bounds.size.width; + [self setUpConstraints]; } +- (void)willMoveToWindow:(UIWindow *)newWindow { + [super willMoveToWindow:newWindow]; + + self.layer.shadowOffset = CGSizeMake(0, 1 / [UIScreen mainScreen].scale); + self.layer.shadowRadius = 0; + + self.layer.shadowColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:1].CGColor; + self.layer.shadowOpacity = 0.25; +} -#pragma mark - Accessibility - -- (BOOL)isAccessibilityElement { - return YES; +- (void)updateWidget:(OCKPatientWidget *)widget forWidgetView:(OCKPatientWidgetView *)widgetView { + OCKPatientWidgetType type = widget.type; + + // Get content for widgets if it includes an activity identifier. + NSString *activityIdentifier = widget.primaryIdentifier; + if (activityIdentifier) { + // Go get the activity for the activity identifier, if one exists. + [self.store activityForIdentifier:activityIdentifier completion:^(BOOL success, OCKCarePlanActivity * _Nullable activity, NSError * _Nullable error) { + if (success && activity) { + NSDateComponents *components = [[NSDateComponents alloc] initWithDate:[NSDate date] calendar:[NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian]]; + + if (activity.type == OCKCarePlanActivityTypeIntervention) { + // Create widget with activity title and percentage string. + __block int totalEvents = 0; + __block int completedEvents = 0; + + [self.store enumerateEventsOfActivity:activity startDate:components endDate:components handler:^(OCKCarePlanEvent * _Nullable event, BOOL * _Nonnull stop) { + // Caluclate adherence + totalEvents++; + if (event.state == OCKCarePlanEventStateCompleted) { + completedEvents++; + } + + } completion:^(BOOL completed, NSError * _Nullable error) { + if (completed) { + double adherence = completedEvents/totalEvents; + if (!_numberFormatter) { + _numberFormatter = [NSNumberFormatter new]; + _numberFormatter.numberStyle = NSNumberFormatterPercentStyle; + _numberFormatter.maximumFractionDigits = 0; + } + NSString *adherenceString = [NSString stringWithFormat:@"%@", [_numberFormatter stringFromNumber:@(adherence * 100)]]; + + // Determine if we should apply tint color based on threshold exceeded or not. + [self.store evaluateAdheranceThresholdForActivity:activity date:components completion:^(BOOL success, OCKCarePlanThreshold * _Nullable threshold, NSError * _Nullable error) { + dispatch_async(dispatch_get_main_queue(), ^{ + widgetView.shouldApplyTintColor = threshold ? YES : NO; + + if (type == OCKPatientWidgetTypeDefault) { + widgetView.widget = [OCKPatientWidget defaultWidgetWithTitle:activity.title text:adherenceString tintColor:widget.tintColor]; + } else if (type == OCKPatientWidgetTypeBadge) { + widgetView.widget = [OCKPatientWidget badgeWidgetWithTitle:activity.title value:@(adherence) tintColor:widget.tintColor]; + } + + }); + }]; + } else { + OCK_Log_Error(@"%@", error.localizedDescription); + } + }]; + } + + else if (activity.type == OCKCarePlanActivityTypeAssessment) { + [self.store eventsForActivity:activity date:components completion:^(NSArray * _Nonnull events, NSError * _Nullable error) { + OCKCarePlanEvent *latestEvent = events.lastObject; + + dispatch_async(dispatch_get_main_queue(), ^{ + NSArray*> *thresholds = [latestEvent evaluateNumericThresholds]; + widgetView.shouldApplyTintColor = thresholds.firstObject.count > 0 ? YES : NO; + if (type == OCKPatientWidgetTypeDefault) { + NSMutableString *text = [NSMutableString new]; + if (latestEvent.result.valueString) { + [text appendString:latestEvent.result.valueString]; + if (latestEvent.result.unitString) { + [text appendString:@" "]; + [text appendString:latestEvent.result.unitString]; + } + } else { + [text appendString:@"--"]; + } + + widgetView.widget = [OCKPatientWidget defaultWidgetWithTitle:activity.title text:text tintColor:widget.tintColor]; + } else if (type == OCKPatientWidgetTypeBadge) { + NSNumber *value = @0; + if (latestEvent.result.values.count > 0) { + value = latestEvent.result.values.firstObject; + } + widgetView.widget = [OCKPatientWidget badgeWidgetWithTitle:activity.title value:value tintColor:widget.tintColor]; + } + }); + }]; + } + + } + else { + OCK_Log_Error(@"No activities found with identifier: %@", activityIdentifier); + } + }]; + } + else { + widgetView.widget = widget; + widgetView.shouldApplyTintColor = YES; + } } -- (NSString *)accessibilityLabel { - return OCKAccessibilityStringForVariables(_titleLabel, _subtitleLabel); +- (void)updateWidgets { + for (int i = 0; i < self.widgets.count; i++) { + [self updateWidget:self.widgets[i] forWidgetView:_widgetViews[i]]; + } } @end diff --git a/CareKit/Insights/OCKInsightsViewController.h b/CareKit/Insights/OCKInsightsViewController.h index 136ae955e..e8a4cf44a 100644 --- a/CareKit/Insights/OCKInsightsViewController.h +++ b/CareKit/Insights/OCKInsightsViewController.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -33,6 +33,7 @@ NS_ASSUME_NONNULL_BEGIN +@class OCKInsightsViewController, OCKPatientWidget; /** The `OCKInsightsViewController` class is a view controller that displays an array of `OCKInsightItem` objects. @@ -41,17 +42,19 @@ OCK_CLASS_AVAILABLE @interface OCKInsightsViewController : UIViewController /** - Returns an initialzed insights view controller using the specified items. + Returns an initialzed insights view controller using the specified parameters. - @param items An array of `OCKInsightItem` objects. - @param headerTitle A string representing the title in the header view. - @param headerSubtitle A string representing the subtitle in the header view. + @param items An array of `OCKInsightItem` objects. + @param widgets An array of `OCKPatientWidget` objects. + @param thresholds An array of threshold activity identifiers. + @param store A care plan store. @return An initialized insights view controller. */ -- (instancetype)initWithInsightItems:(NSArray *)items - headerTitle:(nullable NSString *)headerTitle - headerSubtitle:(nullable NSString *)headerSubtitle; +- (instancetype)initWithInsightItems:(nullable NSArray *)items + patientWidgets:(nullable NSArray *)widgets + thresholds:(nullable NSArray *)thresholds + store:(nullable OCKCarePlanStore *)store; /** Returns an initialzed insights view controller using the specified items. @@ -60,33 +63,32 @@ OCK_CLASS_AVAILABLE @return An initialized insights view controller. */ -- (instancetype)initWithInsightItems:(NSArray *)items; +- (instancetype)initWithInsightItems:(nullable NSArray *)items; /** An array of insight items. -*/ -@property (nonatomic, copy) NSArray *items; + */ +@property (nonatomic, copy, nullable) NSArray *items; /** - A string representing the title in the header view. + An array of patient widgets. - Single-lined. + Maximum of 3 widgets. + A care plan store is required for widgets with activity identifiers. */ -@property (nonatomic, copy, nullable) NSString *headerTitle; +@property (nonatomic, copy, nullable, readonly) NSArray *widgets; /** - A string representing the subtitle in the header view. + An array of activity identifiers for threshold evaluations. - Maximum of 2 lines. + A care plan store is required for thresholds. */ -@property (nonatomic, copy, nullable) NSString *headerSubtitle; +@property (nonatomic, copy, nullable, readonly) NSArray *thresholds; /** - A boolean to show the edge indicators. - - The default value is NO. + A care plan store is required for threshold and widgets. */ -@property (nonatomic) BOOL showEdgeIndicators; +@property (nonatomic, copy, nullable, readonly) OCKCarePlanStore *store; @end diff --git a/CareKit/Insights/OCKInsightsViewController.m b/CareKit/Insights/OCKInsightsViewController.m index e56566515..a89c46d24 100644 --- a/CareKit/Insights/OCKInsightsViewController.m +++ b/CareKit/Insights/OCKInsightsViewController.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -33,12 +33,12 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE #import "OCKInsightsTableViewHeaderView.h" #import "OCKInsightsChartTableViewCell.h" #import "OCKInsightsMessageTableViewCell.h" +#import "OCKInsightsRingTableViewCell.h" #import "OCKChart.h" #import "OCKHelpers.h" +#import "OCKDefines_Private.h" -static const CGFloat HeaderViewHeight = 60.0; - @interface OCKInsightsViewController() @end @@ -49,6 +49,8 @@ @implementation OCKInsightsViewController { OCKInsightsTableViewHeaderView *_headerView; NSMutableArray *_constraints; BOOL _hasAnimated; + NSMutableArray *_triggeredThresholds; + NSMutableArray *_triggeredThresholdActivities; } - (instancetype)init { @@ -57,46 +59,59 @@ - (instancetype)init { } - (instancetype)initWithInsightItems:(NSArray *)items - headerTitle:(NSString *)headerTitle - headerSubtitle:(NSString *)headerSubtitle { + patientWidgets:(NSArray *)widgets + thresholds:(NSArray *)thresholds + store:(OCKCarePlanStore *)store { + NSAssert(widgets.count < 4, @"A maximum of 3 patient widgets is allowed."); + if (thresholds.count > 0) { + NSAssert(store, @"A care plan store is required for thresholds."); + } + self = [super init]; if (self) { _items = OCKArrayCopyObjects(items); - _headerTitle = [headerTitle copy]; - _headerSubtitle = [headerSubtitle copy]; + _widgets = OCKArrayCopyObjects(widgets); + _thresholds = OCKArrayCopyObjects(thresholds); + _store = store; _hasAnimated = NO; - _showEdgeIndicators = NO; } return self; } - (instancetype)initWithInsightItems:(NSArray *)items { return [[OCKInsightsViewController alloc] initWithInsightItems:items - headerTitle:nil - headerSubtitle:nil]; + patientWidgets:nil + thresholds:nil + store:nil]; } - (void)viewDidLoad { [super viewDidLoad]; - _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped]; - _tableView.dataSource = self; - _tableView.delegate = self; - [self.view addSubview:_tableView]; - - [self setUpConstraints]; + self.view.backgroundColor = [UIColor groupTableViewBackgroundColor]; if (!_headerView) { - _headerView = [[OCKInsightsTableViewHeaderView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, HeaderViewHeight)]; - _headerView.backgroundColor = _tableView.backgroundColor; + _headerView = [[OCKInsightsTableViewHeaderView alloc] initWithWidgets:self.widgets + store:self.store]; + [self.view addSubview:_headerView]; } - [self updateHeaderView]; - - _tableView.estimatedRowHeight = 90.0; - _tableView.rowHeight = UITableViewAutomaticDimension; - _tableView.sectionHeaderHeight = 0.0; - _tableView.sectionFooterHeight = 5.0; + if (!_tableView) { + _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; + _tableView.dataSource = self; + _tableView.delegate = self; + _tableView.estimatedRowHeight = 90.0; + _tableView.rowHeight = UITableViewAutomaticDimension; + _tableView.showsVerticalScrollIndicator = NO; + [self.view addSubview:_tableView]; + } + [self setUpConstraints]; +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + [_headerView updateWidgets]; + [self evaluateThresholds]; } - (void)viewDidAppear:(BOOL)animated { @@ -118,85 +133,129 @@ - (void)setItems:(NSArray *)items { [_tableView reloadData]; } -- (void)setHeaderTitle:(NSString *)headerTitle { - _headerTitle = [headerTitle copy]; - [self updateHeaderView]; -} - -- (void)setHeaderSubtitle:(NSString *)headerSubtitle { - _headerSubtitle = [headerSubtitle copy]; - [self updateHeaderView]; -} - - (void)setUpConstraints { [NSLayoutConstraint deactivateConstraints:_constraints]; _constraints = [NSMutableArray new]; - _tableView.translatesAutoresizingMaskIntoConstraints = NO; + _headerView.translatesAutoresizingMaskIntoConstraints = NO; + + CGFloat headerViewHeight = (self.widgets.count > 0) ? 100 : 0; [_constraints addObjectsFromArray:@[ - [NSLayoutConstraint constraintWithItem:_tableView - attribute:NSLayoutAttributeTop - relatedBy:NSLayoutRelationEqual - toItem:self.view - attribute:NSLayoutAttributeTop - multiplier:1.0 - constant:0.0], - [NSLayoutConstraint constraintWithItem:_tableView - attribute:NSLayoutAttributeBottom - relatedBy:NSLayoutRelationEqual - toItem:self.view - attribute:NSLayoutAttributeBottom - multiplier:1.0 - constant:0.0], - [NSLayoutConstraint constraintWithItem:_tableView - attribute:NSLayoutAttributeLeading - relatedBy:NSLayoutRelationEqual - toItem:self.view - attribute:NSLayoutAttributeLeading - multiplier:1.0 - constant:0.0], - [NSLayoutConstraint constraintWithItem:_tableView - attribute:NSLayoutAttributeTrailing - relatedBy:NSLayoutRelationEqual - toItem:self.view - attribute:NSLayoutAttributeTrailing - multiplier:1.0 - constant:0.0] + [NSLayoutConstraint constraintWithItem: _headerView + attribute: NSLayoutAttributeTop + relatedBy: NSLayoutRelationEqual + toItem: self.topLayoutGuide + attribute: NSLayoutAttributeBottom + multiplier: 1.0 + constant: 0.0], + [NSLayoutConstraint constraintWithItem: _headerView + attribute: NSLayoutAttributeLeading + relatedBy: NSLayoutRelationEqual + toItem: self.view + attribute: NSLayoutAttributeLeading + multiplier: 1.0 + constant: 0.0], + [NSLayoutConstraint constraintWithItem: _headerView + attribute: NSLayoutAttributeTrailing + relatedBy: NSLayoutRelationEqual + toItem: self.view + attribute: NSLayoutAttributeTrailing + multiplier: 1.0 + constant: 0.0], + [NSLayoutConstraint constraintWithItem: _headerView + attribute: NSLayoutAttributeHeight + relatedBy: NSLayoutRelationEqual + toItem: nil + attribute: NSLayoutAttributeNotAnAttribute + multiplier: 1.0 + constant: headerViewHeight], + [NSLayoutConstraint constraintWithItem: _tableView + attribute: NSLayoutAttributeTop + relatedBy: NSLayoutRelationEqual + toItem: _headerView + attribute: NSLayoutAttributeBottom + multiplier: 1.0 + constant: 1.0], + [NSLayoutConstraint constraintWithItem: _tableView + attribute: NSLayoutAttributeLeading + relatedBy: NSLayoutRelationEqual + toItem: self.view + attribute: NSLayoutAttributeLeading + multiplier: 1.0 + constant: 0.0], + [NSLayoutConstraint constraintWithItem: _tableView + attribute: NSLayoutAttributeTrailing + relatedBy: NSLayoutRelationEqual + toItem: self.view + attribute: NSLayoutAttributeTrailing + multiplier: 1.0 + constant: 0.0], + [NSLayoutConstraint constraintWithItem: _tableView + attribute: NSLayoutAttributeBottom + relatedBy: NSLayoutRelationEqual + toItem: self.bottomLayoutGuide + attribute: NSLayoutAttributeTop + multiplier: 1.0 + constant: 0.0] ]]; [NSLayoutConstraint activateConstraints:_constraints]; + + } -- (void)updateHeaderView { - _headerView.title = self.headerTitle; - _headerView.subtitle = self.headerSubtitle; - if (_headerView.title || _headerView.subtitle) { - _tableView.tableHeaderView = _headerView; - } else { - _tableView.tableHeaderView = nil; - } - [_tableView reloadData]; -} - -- (void)viewDidLayoutSubviews { - [super viewDidLayoutSubviews]; +- (void)evaluateThresholds { + NSDateComponents *dateComponents = [[NSDateComponents alloc] initWithDate:[NSDate date] + calendar:[NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian]]; + _triggeredThresholds = [NSMutableArray new]; + _triggeredThresholdActivities = [NSMutableArray new]; - CGFloat height = [_headerView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height; - CGRect headerViewFrame = _headerView.frame; - - if (height != headerViewFrame.size.height) { - headerViewFrame.size.height = height; - _headerView.frame = headerViewFrame; - _tableView.tableHeaderView = _headerView; + for (NSString *identifier in self.thresholds) { + [self.store activityForIdentifier:identifier completion:^(BOOL success, OCKCarePlanActivity * _Nullable activity, NSError * _Nullable error) { + if (success && activity) { + [self.store eventsForActivity:activity date:dateComponents completion:^(NSArray * _Nonnull events, NSError * _Nullable error) { + if (activity.type == OCKCarePlanActivityTypeIntervention) { + [self.store evaluateAdheranceThresholdForActivity:activity date:dateComponents completion:^(BOOL success, OCKCarePlanThreshold * _Nullable threshold, NSError * _Nullable error) { + dispatch_async(dispatch_get_main_queue(), ^{ + if (success && threshold) { + [_triggeredThresholds addObject:threshold]; + [_triggeredThresholdActivities addObject:activity]; + } + + if ([identifier isEqualToString:self.thresholds.lastObject]) { + [_tableView reloadData]; + } + }); + }]; + } else { + dispatch_async(dispatch_get_main_queue(), ^{ + for (OCKCarePlanEvent *event in events) { + NSArray *> *thresholds = [event evaluateNumericThresholds]; + for (NSArray *thresholdArray in thresholds) { + for (OCKCarePlanThreshold *threshold in thresholdArray) { + if (threshold) { + [_triggeredThresholds addObject:threshold]; + [_triggeredThresholdActivities addObject:activity]; + } + } + } + } + + if ([identifier isEqualToString:self.thresholds.lastObject]) { + [_tableView reloadData]; + } + }); + } + }]; + } + }]; + } + if (_triggeredThresholds.count <= 0) { + [_tableView reloadData]; } -} - -- (void)setShowEdgeIndicators:(BOOL)showEdgeIndicators { - _showEdgeIndicators = showEdgeIndicators; - [_tableView reloadData]; } @@ -206,41 +265,96 @@ - (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSInde return NO; } +- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { + return 30.0; +} + #pragma mark - UITableViewDataSource - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { - return self.items.count; + NSInteger numberOfSections = 0; + + if (_triggeredThresholds.count > 0) { + numberOfSections++; + } + + if (self.items.count > 0) { + numberOfSections++; + } + + return numberOfSections; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return 1; + NSInteger numberOfRows = 0; + + if (_triggeredThresholds.count > 0 && self.items.count > 0) { + numberOfRows = (section == 0) ? _triggeredThresholds.count : self.items.count; + } else if (_triggeredThresholds.count > 0) { + numberOfRows = _triggeredThresholds.count; + } else if (self.items.count > 0) { + numberOfRows = self.items.count; + } + + return numberOfRows; +} + +- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { + return tableView.numberOfSections - 1 == section ? OCKLocalizedString(@"INSIGHTS_SECTION_HEADER_TITLE_INSIGHTS", nil) : OCKLocalizedString(@"INSIGHTS_SECTION_HEADER_TITLE_THRESHOLD_ALERTS", nil); } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - OCKInsightItem *item = self.items[indexPath.section]; - - if ([item isKindOfClass:[OCKChart class]]) { - static NSString *ChartCellIdentifier = @"ChartCell"; - OCKInsightsChartTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ChartCellIdentifier]; - if (!cell) { - cell = [[OCKInsightsChartTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault - reuseIdentifier:ChartCellIdentifier]; + if (indexPath.section == 0 && tableView.numberOfSections == 2) { + if (_triggeredThresholds.count > 0) { + static NSString *ThresholdCellIdentifier = @"ThresholdCellIdentifier"; + OCKInsightsMessageTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ThresholdCellIdentifier]; + if (!cell) { + cell = [[OCKInsightsMessageTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault + reuseIdentifier:ThresholdCellIdentifier]; + } + OCKCarePlanThreshold *threshold = _triggeredThresholds[indexPath.row]; + OCKCarePlanActivity *activity = _triggeredThresholdActivities[indexPath.row]; + OCKMessageItem *messageItem = [[OCKMessageItem alloc] initWithTitle:activity.title text:threshold.title tintColor:nil messageType:OCKMessageItemTypePlain]; + cell.messageItem = messageItem; + return cell; + + } + } else { + OCKInsightItem *item = self.items[indexPath.row]; + + if ([item isKindOfClass:[OCKChart class]]) { + static NSString *ChartCellIdentifier = @"ChartCell"; + OCKInsightsChartTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ChartCellIdentifier]; + if (!cell) { + cell = [[OCKInsightsChartTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault + reuseIdentifier:ChartCellIdentifier]; + } + cell.chart = (OCKChart *)item; + return cell; + } + else if ([item isKindOfClass:[OCKMessageItem class]]) { + static NSString *MessageCellIdentifier = @"MessageCell"; + OCKInsightsMessageTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MessageCellIdentifier]; + if (!cell) { + cell = [[OCKInsightsMessageTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault + reuseIdentifier:MessageCellIdentifier]; + } + cell.messageItem = (OCKMessageItem *)item; + return cell; } - cell.chart = (OCKChart *)item; - cell.showEdgeIndicator = _showEdgeIndicators; - return cell; - } else if ([item isKindOfClass:[OCKMessageItem class]]) { - static NSString *MessageCellIdentifier = @"MessageCell"; - OCKInsightsMessageTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MessageCellIdentifier]; - if (!cell) { - cell = [[OCKInsightsMessageTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault - reuseIdentifier:MessageCellIdentifier]; + else if ([item isKindOfClass:[OCKRingItem class]]) { + static NSString *RingCellIdentifier = @"RingCell"; + OCKInsightsRingTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:RingCellIdentifier]; + if (!cell) { + cell = [[OCKInsightsRingTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault + reuseIdentifier:RingCellIdentifier]; + } + cell.ringItem = (OCKRingItem *)item; + return cell; } - cell.messageItem = (OCKMessageItem *)item; - cell.showEdgeIndicator = self.showEdgeIndicators; - return cell; + } return nil; diff --git a/CareKit/Insights/OCKMessageItem.h b/CareKit/Insights/OCKMessageItem.h index e8c199f61..0dfc1b875 100644 --- a/CareKit/Insights/OCKMessageItem.h +++ b/CareKit/Insights/OCKMessageItem.h @@ -46,7 +46,12 @@ typedef NS_ENUM(NSInteger, OCKMessageItemType) { /** An alert message type. */ - OCKMessageItemTypeAlert + OCKMessageItemTypeAlert, + + /** + A plain message type. + */ + OCKMessageItemTypePlain }; diff --git a/CareKit/Insights/OCKRingItem.h b/CareKit/Insights/OCKRingItem.h new file mode 100644 index 000000000..c58db511e --- /dev/null +++ b/CareKit/Insights/OCKRingItem.h @@ -0,0 +1,88 @@ +/* + Copyright (c) 2016, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import + + +NS_ASSUME_NONNULL_BEGIN + +/** + The `OCKRingItem` is an object that can display a ring representing a value, + along with a title and text. + */ +@interface OCKRingItem : OCKInsightItem + +/** + Returns an initialzed ring item using the specified values. + + @param title The title for the message item (see `OCKInsightItem`). + @param text The description text for the message item (see `OCKInsightItem`). + @param tintColor The tint color for the message item (see `OCKInsightItem`). + @param value The value to display in the ring view. + @param glyphType The glyph type (see `OCKGlyphType`). + @param glyphFilename The glyph filename to be used if using glyph type custom (see `OCKGlyphTypeCustom`). + + @return An initialzed ring item. + */ +- (instancetype)initWithTitle:(nullable NSString *)title + text:(nullable NSString *)text + tintColor:(nullable UIColor *)tintColor + value:(double)value + glyphType:(OCKGlyphType)glyphType + glyphFilename:(nullable NSString *)glyphFilename; + +/** + The ring value. + + This fills the ring up to the value. Value must be between 0 and 1. + */ +@property (nonatomic, readonly) double value; + +/** + The glpyh type. + This determines the icon used inside the ring. + + See the `OCKGlyphType` enum. + */ +@property (nonatomic, readonly) OCKGlyphType glyphType; + + +/** + The glpyh filename. + + This filename is only used if the glyph type is custom (see `OCKGlyphTypeCustom`). + */ +@property (nonatomic, copy, readonly) NSString *glyphFilename; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/CareKit/Insights/OCKRingItem.m b/CareKit/Insights/OCKRingItem.m new file mode 100644 index 000000000..b37a387d7 --- /dev/null +++ b/CareKit/Insights/OCKRingItem.m @@ -0,0 +1,103 @@ +/* + Copyright (c) 2016, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import "OCKRingItem.h" +#import "OCKHelpers.h" + + +@implementation OCKRingItem + +- (instancetype)initWithTitle:(NSString *)title + text:(NSString *)text + tintColor:(UIColor *)tintColor + value:(double)value + glyphType:(OCKGlyphType)glyphType + glyphFilename:(NSString *)glyphFilename { + NSParameterAssert(value >= 0.0 && value <= 1.0); + + self = [super init]; + if (self) { + self.title = [title copy]; + self.text = [text copy]; + self.tintColor = tintColor; + _value = value; + _glyphType = glyphType; + _glyphFilename = [glyphFilename copy]; + } + return self; +} + +- (BOOL)isEqual:(id)object { + BOOL isParentSame = [super isEqual:object]; + + __typeof(self) castObject = object; + return (isParentSame && + self.value == castObject.value && + self.glyphType == castObject.glyphType && + OCKEqualObjects(self.glyphFilename, castObject.glyphFilename)); +} + + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + OCK_DECODE_DOUBLE(aDecoder, value); + OCK_DECODE_ENUM(aDecoder, glyphType); + OCK_DECODE_OBJ_CLASS(aDecoder, glyphFilename, NSString); + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [super encodeWithCoder:aCoder]; + OCK_ENCODE_DOUBLE(aCoder, value); + OCK_ENCODE_ENUM(aCoder, glyphType); + OCK_ENCODE_OBJ(aCoder, glyphFilename); +} + + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone { + OCKRingItem *item = [super copyWithZone:zone]; + item->_value = self.value; + item->_glyphType = self.glyphType; + item->_glyphFilename = [self.glyphFilename copy]; + return item; +} + +@end diff --git a/CareKit/Localization/ar.lproj/CareKit.strings b/CareKit/Localization/ar.lproj/CareKit.strings index 7bda5f59f..2f89fbdb3 100644 --- a/CareKit/Localization/ar.lproj/CareKit.strings +++ b/CareKit/Localization/ar.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/ca.lproj/CareKit.strings b/CareKit/Localization/ca.lproj/CareKit.strings index 0f8b29f4c..36af772be 100644 --- a/CareKit/Localization/ca.lproj/CareKit.strings +++ b/CareKit/Localization/ca.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/cs.lproj/CareKit.strings b/CareKit/Localization/cs.lproj/CareKit.strings index d2b4fa3f1..4a7a77df8 100644 --- a/CareKit/Localization/cs.lproj/CareKit.strings +++ b/CareKit/Localization/cs.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/da.lproj/CareKit.strings b/CareKit/Localization/da.lproj/CareKit.strings index f33141378..d9524d5f7 100644 --- a/CareKit/Localization/da.lproj/CareKit.strings +++ b/CareKit/Localization/da.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/de.lproj/CareKit.strings b/CareKit/Localization/de.lproj/CareKit.strings index 04e35f06d..b480f98a1 100644 --- a/CareKit/Localization/de.lproj/CareKit.strings +++ b/CareKit/Localization/de.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/el.lproj/CareKit.strings b/CareKit/Localization/el.lproj/CareKit.strings index d9130e3d0..8352a1d76 100644 --- a/CareKit/Localization/el.lproj/CareKit.strings +++ b/CareKit/Localization/el.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/en.lproj/CareKit.strings b/CareKit/Localization/en.lproj/CareKit.strings index f93731e18..566c3bf6a 100644 --- a/CareKit/Localization/en.lproj/CareKit.strings +++ b/CareKit/Localization/en.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -29,23 +29,26 @@ */ +/* Common */ +"ACTIVITY_TYPE_OTHER_SECTION_HEADER" = "Other"; +"ACTIVITY_TYPE_OPTIONAL_SECTION_HEADER" = "Optional"; +"ACTIVITY_TYPE_READ_ONLY_SECTION_HEADER" = "Read Only"; +"HEADER_TITLE_CARE_OVERVIEW" = "Your care overview is %@ complete"; +"HEADER_TITLE_ACTIVITY_STATUS" = "Your activity status is %@ complete"; + /* Care Card */ -"CARE_CARD_HEADER_TITLE" = "Care Completion"; +"CARE_CARD_HEADER_TITLE" = "Care Overview"; "TODAY_BUTTON_TITLE" = "Today"; - -/* Care Card Detail View */ "CARE_CARD_INSTRUCTIONS_SECTION_TITLE" = "Instructions"; "CARE_CARD_ADDITIONAL_INFO_SECTION_TITLE" = "Additional Information"; /* Symptom Tracker */ -"SYMPTOM_TRACKER_HEADER_TITLE" = "Activity Completion"; +"SYMPTOM_TRACKER_HEADER_TITLE" = "Activity Status"; -/* Connect Master View */ +/* Connect */ "CARE_TEAM_SECTION_TITLE" = "Care Team"; "PERSONAL_SECTION_TITLE" = "Friends & Family"; "CONNECT_NO_CONTACTS_TITLE" = "No contacts"; - -/* Connect Detail View */ "CONTACT_INFO_SECTION_TITLE" = "Contact Info"; "CONTACT_SHARING_SECTION_TITLE" = "Sharing"; "CONTACT_INFO_PHONE_TITLE" = "phone"; @@ -53,9 +56,20 @@ "CONTACT_INFO_EMAIL_TITLE" = "email"; "CONTACT_INFO_VIDEO_TITLE" = "video"; "SHARING_CELL_TITLE" = "Send reports"; -"ERROR_TITLE" = "Error"; "MESSAGE_SEND_ERROR" = "Message send failed"; "EMAIL_SEND_ERROR" = "Email send failed"; +"CONNECT_MESSAGE_PLACEHOLDER" = "Type a message..."; +"CONNECT_SEND_BUTTON_TITLE" = "Send"; +"CONNECT_INBOX_TITLE" = "Inbox"; + +/* Patient */ +"PROFILE_VIEW_CONTROLLER_TITLE" = "Profile"; +"PERSONAL_INFORMATION_SECTION_HEADER_TITLE" = "Personal Information"; +"OTHER_INFORMATION_SECTION_HEADER_TITLE" = "Other Information"; + +/* Insight */ +"INSIGHTS_SECTION_HEADER_TITLE_INSIGHTS" = "Insights"; +"INSIGHTS_SECTION_HEADER_TITLE_THRESHOLD_ALERTS" = "Threshold Alerts"; /* ACCESSIBILITY */ "AX_WEEK_BUTTON_PROGRESS" = "%@ completed"; // '75% completed' diff --git a/CareKit/Localization/en_AU.lproj/CareKit.strings b/CareKit/Localization/en_AU.lproj/CareKit.strings index a45c67e4a..7f5f84ba9 100644 --- a/CareKit/Localization/en_AU.lproj/CareKit.strings +++ b/CareKit/Localization/en_AU.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/en_GB.lproj/CareKit.strings b/CareKit/Localization/en_GB.lproj/CareKit.strings index a45c67e4a..7f5f84ba9 100644 --- a/CareKit/Localization/en_GB.lproj/CareKit.strings +++ b/CareKit/Localization/en_GB.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/es.lproj/CareKit.strings b/CareKit/Localization/es.lproj/CareKit.strings index 31766ee9d..e35112098 100644 --- a/CareKit/Localization/es.lproj/CareKit.strings +++ b/CareKit/Localization/es.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/es_MX.lproj/CareKit.strings b/CareKit/Localization/es_MX.lproj/CareKit.strings index a2be9c908..ca94e5f51 100644 --- a/CareKit/Localization/es_MX.lproj/CareKit.strings +++ b/CareKit/Localization/es_MX.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/fi.lproj/CareKit.strings b/CareKit/Localization/fi.lproj/CareKit.strings index f86b42d6d..8a5d31f4d 100644 --- a/CareKit/Localization/fi.lproj/CareKit.strings +++ b/CareKit/Localization/fi.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/fr.lproj/CareKit.strings b/CareKit/Localization/fr.lproj/CareKit.strings index f8450ecb6..5b0929b4d 100644 --- a/CareKit/Localization/fr.lproj/CareKit.strings +++ b/CareKit/Localization/fr.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/fr_CA.lproj/CareKit.strings b/CareKit/Localization/fr_CA.lproj/CareKit.strings index d5e139f89..8ae9d099a 100644 --- a/CareKit/Localization/fr_CA.lproj/CareKit.strings +++ b/CareKit/Localization/fr_CA.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/he.lproj/CareKit.strings b/CareKit/Localization/he.lproj/CareKit.strings index bca5ca779..d5f08ebd7 100644 --- a/CareKit/Localization/he.lproj/CareKit.strings +++ b/CareKit/Localization/he.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/hi.lproj/CareKit.strings b/CareKit/Localization/hi.lproj/CareKit.strings index 51a5f1165..35884e8bb 100644 --- a/CareKit/Localization/hi.lproj/CareKit.strings +++ b/CareKit/Localization/hi.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/hr.lproj/CareKit.strings b/CareKit/Localization/hr.lproj/CareKit.strings index 417a0a8f3..5380076c8 100644 --- a/CareKit/Localization/hr.lproj/CareKit.strings +++ b/CareKit/Localization/hr.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/hu.lproj/CareKit.strings b/CareKit/Localization/hu.lproj/CareKit.strings index 845085374..6b5290154 100644 --- a/CareKit/Localization/hu.lproj/CareKit.strings +++ b/CareKit/Localization/hu.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/id.lproj/CareKit.strings b/CareKit/Localization/id.lproj/CareKit.strings index 187da7482..571f9e1ab 100644 --- a/CareKit/Localization/id.lproj/CareKit.strings +++ b/CareKit/Localization/id.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/it.lproj/CareKit.strings b/CareKit/Localization/it.lproj/CareKit.strings index bc51a4905..3394ef098 100644 --- a/CareKit/Localization/it.lproj/CareKit.strings +++ b/CareKit/Localization/it.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/ja.lproj/CareKit.strings b/CareKit/Localization/ja.lproj/CareKit.strings index 882cb02eb..81af10cf4 100644 --- a/CareKit/Localization/ja.lproj/CareKit.strings +++ b/CareKit/Localization/ja.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/ko.lproj/CareKit.strings b/CareKit/Localization/ko.lproj/CareKit.strings index ac531069c..c08304fc1 100644 --- a/CareKit/Localization/ko.lproj/CareKit.strings +++ b/CareKit/Localization/ko.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/ms.lproj/CareKit.strings b/CareKit/Localization/ms.lproj/CareKit.strings index 6cb8a38b3..36cbe002c 100644 --- a/CareKit/Localization/ms.lproj/CareKit.strings +++ b/CareKit/Localization/ms.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/nl.lproj/CareKit.strings b/CareKit/Localization/nl.lproj/CareKit.strings index ed7b4e3c6..98a8eef84 100644 --- a/CareKit/Localization/nl.lproj/CareKit.strings +++ b/CareKit/Localization/nl.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/no.lproj/CareKit.strings b/CareKit/Localization/no.lproj/CareKit.strings index d740ddc21..4566590dc 100644 --- a/CareKit/Localization/no.lproj/CareKit.strings +++ b/CareKit/Localization/no.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/pl.lproj/CareKit.strings b/CareKit/Localization/pl.lproj/CareKit.strings index afd35ad96..78403b70b 100644 --- a/CareKit/Localization/pl.lproj/CareKit.strings +++ b/CareKit/Localization/pl.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/pt.lproj/CareKit.strings b/CareKit/Localization/pt.lproj/CareKit.strings index 788dedcfc..24575811f 100644 --- a/CareKit/Localization/pt.lproj/CareKit.strings +++ b/CareKit/Localization/pt.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/pt_PT.lproj/CareKit.strings b/CareKit/Localization/pt_PT.lproj/CareKit.strings index 548371769..0fd2b866d 100644 --- a/CareKit/Localization/pt_PT.lproj/CareKit.strings +++ b/CareKit/Localization/pt_PT.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/ro.lproj/CareKit.strings b/CareKit/Localization/ro.lproj/CareKit.strings index 1c8ef6509..2fc4a9402 100644 --- a/CareKit/Localization/ro.lproj/CareKit.strings +++ b/CareKit/Localization/ro.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/ru.lproj/CareKit.strings b/CareKit/Localization/ru.lproj/CareKit.strings index c900415cc..ebaec8f96 100644 --- a/CareKit/Localization/ru.lproj/CareKit.strings +++ b/CareKit/Localization/ru.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/sk.lproj/CareKit.strings b/CareKit/Localization/sk.lproj/CareKit.strings index 443877937..e76d256ef 100644 --- a/CareKit/Localization/sk.lproj/CareKit.strings +++ b/CareKit/Localization/sk.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/sv.lproj/CareKit.strings b/CareKit/Localization/sv.lproj/CareKit.strings index f60386227..14cfe900c 100644 --- a/CareKit/Localization/sv.lproj/CareKit.strings +++ b/CareKit/Localization/sv.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/th.lproj/CareKit.strings b/CareKit/Localization/th.lproj/CareKit.strings index f751dc052..0c40437fc 100644 --- a/CareKit/Localization/th.lproj/CareKit.strings +++ b/CareKit/Localization/th.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/tr.lproj/CareKit.strings b/CareKit/Localization/tr.lproj/CareKit.strings index 07604a6d0..31c1f669e 100644 --- a/CareKit/Localization/tr.lproj/CareKit.strings +++ b/CareKit/Localization/tr.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/uk.lproj/CareKit.strings b/CareKit/Localization/uk.lproj/CareKit.strings index 2debdc154..7f60ef026 100644 --- a/CareKit/Localization/uk.lproj/CareKit.strings +++ b/CareKit/Localization/uk.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/vi.lproj/CareKit.strings b/CareKit/Localization/vi.lproj/CareKit.strings index 8e8551590..1b8d1c939 100644 --- a/CareKit/Localization/vi.lproj/CareKit.strings +++ b/CareKit/Localization/vi.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/zh_CN.lproj/CareKit.strings b/CareKit/Localization/zh_CN.lproj/CareKit.strings index 9db3023ef..c8b3f68a6 100644 --- a/CareKit/Localization/zh_CN.lproj/CareKit.strings +++ b/CareKit/Localization/zh_CN.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/zh_HK.lproj/CareKit.strings b/CareKit/Localization/zh_HK.lproj/CareKit.strings index b647bf73d..b55faf8be 100644 --- a/CareKit/Localization/zh_HK.lproj/CareKit.strings +++ b/CareKit/Localization/zh_HK.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/Localization/zh_TW.lproj/CareKit.strings b/CareKit/Localization/zh_TW.lproj/CareKit.strings index 5f3b8b48a..ea33fc783 100644 --- a/CareKit/Localization/zh_TW.lproj/CareKit.strings +++ b/CareKit/Localization/zh_TW.lproj/CareKit.strings @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKit/PDF/OCKDocument.m b/CareKit/PDF/OCKDocument.m index 153678111..a93ff6281 100644 --- a/CareKit/PDF/OCKDocument.m +++ b/CareKit/PDF/OCKDocument.m @@ -175,6 +175,7 @@ - (instancetype)copyWithZone:(NSZone *)zone { static NSString *imageTagFromView (UIView *view) { UIGraphicsBeginImageContextWithOptions(view.frame.size, YES, 0.0); + [view layoutIfNeeded]; [view.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); diff --git a/CareKit/Patient/OCKBadgePatientWidgetView.h b/CareKit/Patient/OCKBadgePatientWidgetView.h new file mode 100644 index 000000000..3baf2d75d --- /dev/null +++ b/CareKit/Patient/OCKBadgePatientWidgetView.h @@ -0,0 +1,37 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import "OCKPatientWidgetView.h" + + +@interface OCKBadgePatientWidgetView : OCKPatientWidgetView + +@end diff --git a/CareKit/Patient/OCKBadgePatientWidgetView.m b/CareKit/Patient/OCKBadgePatientWidgetView.m new file mode 100644 index 000000000..cc799e8e1 --- /dev/null +++ b/CareKit/Patient/OCKBadgePatientWidgetView.m @@ -0,0 +1,230 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import "OCKBadgePatientWidgetView.h" +#import "OCKPatientWidget_Internal.h" + + +@implementation OCKBadgePatientWidgetView { + UILabel *_titleLabel; + UILabel *_badgeLabel; + UIImageView *_imageView; + UIView *_containerView; + UIStackView *_stackView; + NSMutableArray *_constraints; +} + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self prepareView]; + } + return self; +} + +- (void)prepareView { + if (!_titleLabel) { + _titleLabel = [UILabel new]; + _titleLabel.numberOfLines = 1; + _titleLabel.lineBreakMode = NSLineBreakByTruncatingTail; + _titleLabel.font = [UIFont systemFontOfSize:14.0 weight:UIFontWeightThin]; + } + + if (!_badgeLabel) { + _badgeLabel = [UILabel new]; + _badgeLabel.textColor = [UIColor whiteColor]; + _badgeLabel.font = [UIFont systemFontOfSize:20.0]; + _badgeLabel.numberOfLines = 1; + _badgeLabel.lineBreakMode = NSLineBreakByTruncatingTail; + _badgeLabel.textAlignment = NSTextAlignmentCenter; + } + + if (!_imageView) { + _imageView = [UIImageView new]; + _imageView.layer.cornerRadius = 12.5; + _imageView.clipsToBounds = YES; + _imageView.contentMode = UIViewContentModeScaleAspectFill; + _imageView.backgroundColor = [UIColor lightGrayColor]; + } + + if (!_containerView) { + _containerView = [UIView new]; + [_containerView addSubview:_imageView]; + [_containerView addSubview:_badgeLabel]; + } + + if (!_stackView) { + _stackView = [[UIStackView alloc] initWithArrangedSubviews:@[_titleLabel, _containerView]]; + _stackView.axis = UILayoutConstraintAxisVertical; + _stackView.distribution = UIStackViewDistributionFillProportionally; + _stackView.alignment = UIStackViewAlignmentCenter; + _stackView.spacing = 2.0; + [self addSubview:_stackView]; + } + + [self updateView]; + [self setUpConstraints]; +} + +- (void)updateView { + _titleLabel.text = self.widget.primaryText; + + if (self.animate) { + CATransition *animation = [CATransition animation]; + animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + animation.type = kCATransitionFade; + animation.duration = 0.75; + [_badgeLabel.layer addAnimation:animation forKey:@"kCATransitionFade"]; + } + _badgeLabel.text = [NSString stringWithFormat:@"%@", self.widget.value]; + + _imageView.backgroundColor = self.shouldApplyTintColor ? self.widget.tintColor : [UIColor lightGrayColor]; +} + +- (void)setUpConstraints { + [NSLayoutConstraint deactivateConstraints:_constraints]; + + _constraints = [NSMutableArray new]; + + _stackView.translatesAutoresizingMaskIntoConstraints = NO; + _imageView.translatesAutoresizingMaskIntoConstraints = NO; + _badgeLabel.translatesAutoresizingMaskIntoConstraints = NO; + + [_constraints addObjectsFromArray:@[ + [NSLayoutConstraint constraintWithItem:_stackView + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_stackView + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_stackView + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_stackView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_imageView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:25.0], + [NSLayoutConstraint constraintWithItem:_imageView + attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:25.0], + [NSLayoutConstraint constraintWithItem:_imageView + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:_containerView + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_imageView + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:_containerView + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_imageView + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:_containerView + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_imageView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:_containerView + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_badgeLabel + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:_containerView + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_badgeLabel + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:_containerView + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_badgeLabel + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:_containerView + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_badgeLabel + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:_containerView + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:0.0] + ]]; + + [NSLayoutConstraint activateConstraints:_constraints]; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + [self setUpConstraints]; +} + +@end diff --git a/CareKit/Patient/OCKDefaultPatientWidgetView.h b/CareKit/Patient/OCKDefaultPatientWidgetView.h new file mode 100644 index 000000000..ee6ce691e --- /dev/null +++ b/CareKit/Patient/OCKDefaultPatientWidgetView.h @@ -0,0 +1,38 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import +#import "OCKPatientWidgetView.h" + + +@interface OCKDefaultPatientWidgetView : OCKPatientWidgetView + +@end diff --git a/CareKit/Patient/OCKDefaultPatientWidgetView.m b/CareKit/Patient/OCKDefaultPatientWidgetView.m new file mode 100644 index 000000000..74e4ced35 --- /dev/null +++ b/CareKit/Patient/OCKDefaultPatientWidgetView.m @@ -0,0 +1,139 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import "OCKDefaultPatientWidgetView.h" +#import "OCKPatientWidget_Internal.h" + + +@implementation OCKDefaultPatientWidgetView { + UILabel *_titleLabel; + UILabel *_textLabel; + UIStackView *_stackView; + NSMutableArray *_constraints; +} + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self prepareView]; + } + return self; +} + +- (void)prepareView { + if (!_titleLabel) { + _titleLabel = [UILabel new]; + _titleLabel.numberOfLines = 1; + _titleLabel.font = [UIFont systemFontOfSize:14.0 weight:UIFontWeightThin]; + _titleLabel.lineBreakMode = NSLineBreakByTruncatingTail; + } + + if (!_textLabel) { + _textLabel = [UILabel new]; + _textLabel.font = [UIFont systemFontOfSize:20.0]; + _textLabel.numberOfLines = 1; + _textLabel.lineBreakMode = NSLineBreakByTruncatingTail; + } + + if (!_stackView) { + _stackView = [[UIStackView alloc] initWithArrangedSubviews:@[_titleLabel, _textLabel]]; + _stackView.axis = UILayoutConstraintAxisVertical; + _stackView.distribution = UIStackViewDistributionFillProportionally; + _stackView.alignment = UIStackViewAlignmentCenter; + [self addSubview:_stackView]; + } + + [self updateView]; + [self setUpConstraints]; +} + +- (void)updateView { + _titleLabel.text = self.widget.primaryText; + + if (self.animate) { + CATransition *animation = [CATransition animation]; + animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + animation.type = kCATransitionFade; + animation.duration = 0.75; + [_textLabel.layer addAnimation:animation forKey:@"kCATransitionFade"]; + } + _textLabel.text = self.widget.secondaryText; + + _textLabel.textColor = self.shouldApplyTintColor ? self.widget.tintColor : [UIColor blackColor]; +} + +- (void)setUpConstraints { + [NSLayoutConstraint deactivateConstraints:_constraints]; + + _constraints = [NSMutableArray new]; + + _stackView.translatesAutoresizingMaskIntoConstraints = NO; + + [_constraints addObjectsFromArray:@[ + [NSLayoutConstraint constraintWithItem:_stackView + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_stackView + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_stackView + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_stackView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:0.0] + ]]; + + [NSLayoutConstraint activateConstraints:_constraints]; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + [self setUpConstraints]; +} + +@end diff --git a/CareKit/Patient/OCKImagePatientWidgetView.h b/CareKit/Patient/OCKImagePatientWidgetView.h new file mode 100644 index 000000000..3bdab7590 --- /dev/null +++ b/CareKit/Patient/OCKImagePatientWidgetView.h @@ -0,0 +1,37 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import "OCKPatientWidgetView.h" + + +@interface OCKImagePatientWidgetView : OCKPatientWidgetView + +@end diff --git a/CareKit/Patient/OCKImagePatientWidgetView.m b/CareKit/Patient/OCKImagePatientWidgetView.m new file mode 100644 index 000000000..3e2409c70 --- /dev/null +++ b/CareKit/Patient/OCKImagePatientWidgetView.m @@ -0,0 +1,153 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import "OCKImagePatientWidgetView.h" +#import "OCKPatientWidget_Internal.h" + + +@implementation OCKImagePatientWidgetView { + UILabel *_titleLabel; + UIImageView *_imageView; + UIStackView *_stackView; + NSMutableArray *_constraints; +} + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self prepareView]; + } + return self; +} + +- (void)prepareView { + if (!_titleLabel) { + _titleLabel = [UILabel new]; + _titleLabel.numberOfLines = 1; + _titleLabel.lineBreakMode = NSLineBreakByTruncatingTail; + _titleLabel.font = [UIFont systemFontOfSize:14.0 weight:UIFontWeightThin]; + } + + if (!_imageView) { + _imageView = [UIImageView new]; + _imageView.contentMode = UIViewContentModeScaleAspectFill; + } + + if (!_stackView) { + _stackView = [[UIStackView alloc] initWithArrangedSubviews:@[_titleLabel, _imageView]]; + _stackView.axis = UILayoutConstraintAxisVertical; + _stackView.distribution = UIStackViewDistributionFillProportionally; + _stackView.alignment = UIStackViewAlignmentCenter; + _stackView.spacing = 2.0; + [self addSubview:_stackView]; + } + + [self updateView]; + [self setUpConstraints]; +} + +- (void)updateView { + _titleLabel.text = self.widget.primaryText; + _titleLabel.textColor = self.shouldApplyTintColor ? self.widget.tintColor : [UIColor blackColor]; + + if (self.animate) { + CATransition *animation = [CATransition animation]; + animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + animation.type = kCATransitionFade; + animation.duration = 0.75; + [_imageView.layer addAnimation:animation forKey:@"kCATransitionFade"]; + } + _imageView.image = self.widget.primaryImage; +} + +- (void)setUpConstraints { + [NSLayoutConstraint deactivateConstraints:_constraints]; + + _constraints = [NSMutableArray new]; + + _stackView.translatesAutoresizingMaskIntoConstraints = NO; + _imageView.translatesAutoresizingMaskIntoConstraints = NO; + + [_constraints addObjectsFromArray:@[ + [NSLayoutConstraint constraintWithItem:_stackView + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_stackView + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_stackView + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_stackView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_imageView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:25.0], + [NSLayoutConstraint constraintWithItem:_imageView + attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:25.0] + + ]]; + + [NSLayoutConstraint activateConstraints:_constraints]; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + [self setUpConstraints]; +} + +@end diff --git a/CareKit/Patient/OCKPatient.h b/CareKit/Patient/OCKPatient.h new file mode 100644 index 000000000..044365ec5 --- /dev/null +++ b/CareKit/Patient/OCKPatient.h @@ -0,0 +1,139 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import + + +NS_ASSUME_NONNULL_BEGIN + +@class OCKContact, OCKCarePlanStore; + +/** + The `OCKPatient` class is an object that represents a patient. + */ +OCK_CLASS_AVAILABLE +@interface OCKPatient : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +/** + Returns an initialized patient using the specified values. + + @param identifier The identifier for the patient. + @param store The care plan store for the patient. + @param name The name for the patient. + @param detailInfo Additional information for the patient. + @param careTeamContacts The contacts in charge of the patients. + @param tintColor The tint color for the patient. + @param monogram A monogram for the patient. + @param image An image for the patient. + @param categories An array of categories of the patient. + + @return An initialized patient object. + */ +- (instancetype)initWithIdentifier:(NSString *)identifier + carePlanStore:(OCKCarePlanStore *)store + name:(NSString *)name + detailInfo:(nullable NSString *)detailInfo + careTeamContacts:(nullable NSArray *)careTeamContacts + tintColor:(nullable UIColor *)tintColor + monogram:(null_unspecified NSString *)monogram + image:(nullable UIImage *)image + categories:(nullable NSArray *)categories + userInfo:(nullable NSDictionary *)userInfo; + + +/** + The identifier for the patient. + */ +@property (nonatomic, copy, readonly) NSString *identifier; + +/** + The care plan store for the patient. + */ +@property (nonatomic, readonly) OCKCarePlanStore *store; + +/** + A string indicating the name for a patient. + */ +@property (nonatomic, copy, readonly) NSString *name; + +/** + A string indicating additional details for a patient. + + This can include date of birth, gender, etc. + */ +@property (nonatomic, copy, readonly, nullable) NSString *detailInfo; + +/** + The care team contacts that are repsonsible for this patient. + + Must be OCKContact objects of type OCKContactTypeCareTeam. + */ +@property (nonatomic, copy, readonly, nullable) NSArray *careTeamContacts; + +/** + The tint color for a patient. + + If the value is not specified, the app's tint color is used. + */ +@property (nonatomic, readonly, nullable) UIColor *tintColor; + +/** + A string indicating the monogram for a contact. + + If a monogram is not provided, it will be generated automatically. + If a monogram is available, it will be clipped to two glyphs. + */ +@property (nonatomic, readonly, null_resettable) NSString *monogram; + +/** + An image for a contact. + + If an image is not provided, a monogram will be used for the contact. + An image can be set after a contact object has been created. If an image + is available, it will be displayed instead of the monogram. + */ +@property (nonatomic, nullable) UIImage *image; + +/** + An array of strings indicating the categories of the patient (e.g. pediatric, cardiology). + */ +@property (nonatomic, copy, readonly, nullable) NSArray *categories; + +/** + Save any additional objects that comply with the NSCoding protocol. + */ +@property (nonatomic, copy, readonly, nullable) NSDictionary> *userInfo; + +@end + +NS_ASSUME_NONNULL_END diff --git a/CareKit/Patient/OCKPatient.m b/CareKit/Patient/OCKPatient.m new file mode 100644 index 000000000..a05b79fac --- /dev/null +++ b/CareKit/Patient/OCKPatient.m @@ -0,0 +1,181 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import "OCKPatient.h" +#import "OCKHelpers.h" + + +@implementation OCKPatient + ++ (instancetype)new { + OCKThrowMethodUnavailableException(); + return nil; +} + +- (instancetype)initWithIdentifier:(NSString *)identifier + carePlanStore:(OCKCarePlanStore *)store + name:(NSString *)name + detailInfo:(NSString *)detailInfo + careTeamContacts:(NSArray *)careTeamContacts + tintColor:(UIColor *)tintColor + monogram:(NSString *)monogram + image:(UIImage *)image + categories:(NSArray *)categories + userInfo:(NSDictionary *)userInfo { + self = [super init]; + if (self) { + _identifier = [identifier copy]; + _store = store; + _name = [name copy]; + _detailInfo = [detailInfo copy]; + _careTeamContacts = OCKArrayCopyObjects(careTeamContacts); + _tintColor = tintColor; + self.monogram = [self clippedMonogramForString:monogram]; + _image = image; + _categories = OCKArrayCopyObjects(categories); + _userInfo = userInfo; + } + return self; +} + +- (BOOL)isEqual:(id)object { + BOOL isParentSame = [super isEqual:object]; + + __typeof(self) castObject = object; + return (isParentSame && + OCKEqualObjects(self.identifier, castObject.identifier) && + OCKEqualObjects(self.store, castObject.store) && + OCKEqualObjects(self.name, castObject.name) && + OCKEqualObjects(self.detailInfo, castObject.detailInfo) && + OCKEqualObjects(self.careTeamContacts, castObject.careTeamContacts) && + OCKEqualObjects(self.tintColor, castObject.tintColor) && + OCKEqualObjects(self.monogram, castObject.monogram) && + OCKEqualObjects(self.image, castObject.image) && + OCKEqualObjects(self.categories, castObject.categories) && + OCKEqualObjects(self.userInfo, castObject.userInfo)); +} + + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self) { + OCK_DECODE_OBJ_CLASS(aDecoder, identifier, NSString); + OCK_DECODE_OBJ_CLASS(aDecoder, store, OCKCarePlanStore); + OCK_DECODE_OBJ_CLASS(aDecoder, name, NSString); + OCK_DECODE_OBJ_CLASS(aDecoder, detailInfo, NSString); + OCK_DECODE_OBJ_ARRAY(aDecoder, careTeamContacts, NSArray); + OCK_DECODE_OBJ_CLASS(aDecoder, tintColor, UIColor); + OCK_DECODE_OBJ_CLASS(aDecoder, monogram, NSString); + OCK_DECODE_IMAGE(aDecoder, image); + OCK_DECODE_OBJ_CLASS(aDecoder, categories, NSArray); + OCK_DECODE_OBJ_CLASS(aDecoder, userInfo, NSDictionary); + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + OCK_ENCODE_OBJ(aCoder, identifier); + OCK_ENCODE_OBJ(aCoder, store); + OCK_ENCODE_OBJ(aCoder, name); + OCK_ENCODE_OBJ(aCoder, detailInfo); + OCK_ENCODE_OBJ(aCoder, careTeamContacts); + OCK_ENCODE_OBJ(aCoder, tintColor); + OCK_ENCODE_OBJ(aCoder, monogram); + OCK_ENCODE_IMAGE(aCoder, image); + OCK_ENCODE_OBJ(aCoder, categories); + OCK_ENCODE_OBJ(aCoder, userInfo); +} + + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone { + OCKPatient *patient = [[[self class] allocWithZone:zone] init]; + patient->_identifier = [self.identifier copy]; + patient->_store = self.store; + patient->_name = [self.name copy]; + patient->_detailInfo = [self.detailInfo copy]; + patient->_careTeamContacts = [self.careTeamContacts copy]; + patient->_tintColor = self.tintColor; + patient->_monogram = [self.monogram copy]; + patient->_image = self.image; + patient->_categories = [self.categories copy]; + patient->_userInfo = self.userInfo; + return patient; +} + + +#pragma mark - Monogram + +- (NSString *)clippedMonogramForString:(NSString *)string { + NSRange stringRange = {0, MIN([string length], 2)}; + stringRange = [string rangeOfComposedCharacterSequencesForRange:stringRange]; + return [string substringWithRange:stringRange]; +} + +- (void)setMonogram:(NSString *)monogram { + if (!monogram) { + monogram = [self generateMonogram:_name]; + } + _monogram = [monogram copy]; +} + +- (NSString *)generateMonogram:(NSString *)name { + NSAssert((name != nil), @"A name must be supplied"); + NSAssert((name.length > 0), @"A name must have > 0 chars"); + + NSMutableArray *candidateWords = [NSMutableArray arrayWithArray:[name componentsSeparatedByString:@" "]]; + + NSString *first = @""; + NSString *last = @""; + + if (candidateWords.count > 0) { + first = [NSString stringWithFormat:@"%c", [candidateWords[0] characterAtIndex:0]]; + if (candidateWords.count > 1) { + last = [NSString stringWithFormat:@"%c", [candidateWords[candidateWords.count-1] characterAtIndex:0]]; + } + + } else { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"name %@ has no candidates to generate a monogram", name] userInfo:nil]; + } + + candidateWords = nil; + + return [NSString stringWithFormat:@"%@%@",[first uppercaseString],[last uppercaseString]]; +} + +@end diff --git a/CareKit/Patient/OCKPatientHeaderView.h b/CareKit/Patient/OCKPatientHeaderView.h new file mode 100644 index 000000000..bf90bef02 --- /dev/null +++ b/CareKit/Patient/OCKPatientHeaderView.h @@ -0,0 +1,41 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import + + +@interface OCKPatientHeaderView : UITableViewHeaderFooterView + +@property (nonatomic) OCKPatient *patient; + +@property (nonatomic) BOOL showSeparator; + +@end diff --git a/CareKit/CareCard/OCKCareCardTableViewHeader.m b/CareKit/Patient/OCKPatientHeaderView.m similarity index 77% rename from CareKit/CareCard/OCKCareCardTableViewHeader.m rename to CareKit/Patient/OCKPatientHeaderView.m index c242f6cb5..fcbfd95d8 100644 --- a/CareKit/CareCard/OCKCareCardTableViewHeader.m +++ b/CareKit/Patient/OCKPatientHeaderView.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -29,33 +29,31 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE */ -#import "OCKCareCardTableViewHeader.h" -#import "OCKHeartView.h" -#import "OCKDefines_Private.h" -#import "OCKHelpers.h" +#import "OCKPatientHeaderView.h" #import "OCKLabel.h" +#import "OCKHelpers.h" -static const CGFloat TopMargin = 25.0; -static const CGFloat BottomMargin = 25.0; -static const CGFloat TrailingMargin = 20.0; +static const CGFloat VerticalMargin = 10.0; static const CGFloat HorizontalMargin = 10.0; -static const CGFloat HeartViewSize = 110.0; +static const CGFloat TopMargin = 12.5; +static const CGFloat BottomMargin = 12.5; +static const CGFloat ImageViewSize = 125.0; -@implementation OCKCareCardTableViewHeader { - UIView *_containerView; - OCKLabel *_titleLabel; - OCKLabel *_dateLabel; - OCKLabel *_valuePercentageLabel; - UIView *_topEdge; +@implementation OCKPatientHeaderView { + UIImageView *_imageView; + OCKLabel *_monogramLabel; + OCKLabel *_nameLabel; + OCKLabel *_detailLabel; UIView *_bottomEdge; - NSNumberFormatter *_numberFormatter; NSMutableArray *_constraints; } - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { + _showSeparator = YES; + if (!UIAccessibilityIsReduceTransparencyEnabled()) { self.contentView.backgroundColor = [UIColor clearColor]; @@ -73,33 +71,45 @@ - (instancetype)initWithFrame:(CGRect)frame { return self; } +- (void)setPatient:(OCKPatient *)patient { + _patient = patient; + self.tintColor = _patient.tintColor; + [self updateView]; +} + +- (void)setShowSeparator:(BOOL)showSeparator { + _showSeparator = showSeparator; + _bottomEdge.hidden = !_showSeparator; +} + - (void)prepareView { - if (!_heartView) { - _heartView = [[OCKHeartView alloc] initWithFrame:CGRectMake(0, 0, HeartViewSize, HeartViewSize)]; - [self addSubview:_heartView]; + if (!_imageView) { + _imageView = [UIImageView new]; + _imageView.layer.cornerRadius = ImageViewSize/2; + _imageView.clipsToBounds = YES; + _imageView.contentMode = UIViewContentModeScaleAspectFill; + [self addSubview:_imageView]; } - - if (!_titleLabel) { - _titleLabel = [OCKLabel new]; - _titleLabel.textStyle = UIFontTextStyleHeadline; - _titleLabel.lineBreakMode = NSLineBreakByWordWrapping; - _titleLabel.numberOfLines = 0; - [self addSubview:_titleLabel]; + + if (!_monogramLabel) { + _monogramLabel = [OCKLabel new]; + _monogramLabel.textColor = [UIColor whiteColor]; + _monogramLabel.textAlignment = NSTextAlignmentCenter; + _monogramLabel.font = [UIFont boldSystemFontOfSize:20.0]; + _monogramLabel.adjustsFontSizeToFitWidth = YES; + [self addSubview:_monogramLabel]; } - if (!_dateLabel) { - _dateLabel = [OCKLabel new]; - _dateLabel.textStyle = UIFontTextStyleSubheadline; - _dateLabel.textColor = [UIColor darkGrayColor]; - _dateLabel.lineBreakMode = NSLineBreakByWordWrapping; - _dateLabel.numberOfLines = 0; - [self addSubview:_dateLabel]; + if (!_nameLabel) { + _nameLabel = [OCKLabel new]; + _nameLabel.textStyle = UIFontTextStyleHeadline; + [self addSubview:_nameLabel]; } - if (!_valuePercentageLabel) { - _valuePercentageLabel = [OCKLabel new]; - _valuePercentageLabel.textStyle = UIFontTextStyleTitle1; - [self addSubview:_valuePercentageLabel]; + if (!_detailLabel) { + _detailLabel = [OCKLabel new]; + _detailLabel.textStyle = UIFontTextStyleSubheadline; + [self addSubview:_detailLabel]; } if (!_bottomEdge) { @@ -108,179 +118,176 @@ - (void)prepareView { [self addSubview:_bottomEdge]; } - if (!_topEdge) { - _topEdge = [UIView new]; - _topEdge.backgroundColor = [UIColor groupTableViewBackgroundColor]; - [self addSubview:_topEdge]; - } - - [self updateView]; [self setUpConstraints]; } - (void)updateView { - _titleLabel.text = self.title; - _dateLabel.text = self.date; - self.heartView.value = self.value; - self.heartView.tintColor = self.tintColor; - _valuePercentageLabel.text = self.valuePercentageString; - _valuePercentageLabel.textColor = self.tintColor; + if (self.patient.image) { + _imageView.image = self.patient.image; + _monogramLabel.text = nil; + } else { + _imageView.image = nil; + _monogramLabel.text = self.patient.monogram; + } + + [self updateImageViewBackgroundColor]; + + _nameLabel.text = self.patient.name; + _detailLabel.text = self.patient.detailInfo; } - (void)setUpConstraints { [NSLayoutConstraint deactivateConstraints:_constraints]; - _constraints = [NSMutableArray new]; - _dateLabel.translatesAutoresizingMaskIntoConstraints = NO; - self.heartView.translatesAutoresizingMaskIntoConstraints = NO; - _titleLabel.translatesAutoresizingMaskIntoConstraints = NO; - _valuePercentageLabel.translatesAutoresizingMaskIntoConstraints = NO; - _topEdge.translatesAutoresizingMaskIntoConstraints = NO; + _imageView.translatesAutoresizingMaskIntoConstraints = NO; + _monogramLabel.translatesAutoresizingMaskIntoConstraints = NO; + _nameLabel.translatesAutoresizingMaskIntoConstraints = NO; + _detailLabel.translatesAutoresizingMaskIntoConstraints = NO; _bottomEdge.translatesAutoresizingMaskIntoConstraints = NO; [_constraints addObjectsFromArray:@[ - [NSLayoutConstraint constraintWithItem:self.heartView - attribute:NSLayoutAttributeTrailing + [NSLayoutConstraint constraintWithItem:_imageView + attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self - attribute:NSLayoutAttributeCenterX + attribute:NSLayoutAttributeTop multiplier:1.0 - constant:-25.0], - [NSLayoutConstraint constraintWithItem:self.heartView - attribute:NSLayoutAttributeCenterY + constant:TopMargin], + [NSLayoutConstraint constraintWithItem:_imageView + attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeCenterY + toItem:_nameLabel + attribute:NSLayoutAttributeTop multiplier:1.0 - constant:0.0], - [NSLayoutConstraint constraintWithItem:self.heartView - attribute:NSLayoutAttributeHeight + constant:-VerticalMargin], + [NSLayoutConstraint constraintWithItem:_detailLabel + attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute + toItem:_nameLabel + attribute:NSLayoutAttributeBottom multiplier:1.0 - constant:HeartViewSize], - [NSLayoutConstraint constraintWithItem:self.heartView - attribute:NSLayoutAttributeWidth - relatedBy:NSLayoutRelationEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute + constant:0.0], + [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationGreaterThanOrEqual + toItem:_detailLabel + attribute:NSLayoutAttributeBottom multiplier:1.0 - constant:HeartViewSize], - [NSLayoutConstraint constraintWithItem:_titleLabel - attribute:NSLayoutAttributeTop + constant:BottomMargin], + [NSLayoutConstraint constraintWithItem:_nameLabel + attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:self - attribute:NSLayoutAttributeTop - multiplier:1.0 - constant:TopMargin], - [NSLayoutConstraint constraintWithItem:_titleLabel attribute:NSLayoutAttributeLeading - relatedBy:NSLayoutRelationEqual - toItem:self.heartView - attribute:NSLayoutAttributeTrailing multiplier:1.0 - constant:HorizontalMargin], - [NSLayoutConstraint constraintWithItem:_titleLabel + constant:0.0], + [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeTrailing - relatedBy:NSLayoutRelationEqual - toItem:self + relatedBy:NSLayoutRelationGreaterThanOrEqual + toItem:_nameLabel attribute:NSLayoutAttributeTrailing multiplier:1.0 - constant:-TrailingMargin], - [NSLayoutConstraint constraintWithItem:_titleLabel - attribute:NSLayoutAttributeTrailing + constant:0.0], + [NSLayoutConstraint constraintWithItem:_nameLabel + attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self - attribute:NSLayoutAttributeTrailing + attribute:NSLayoutAttributeCenterX multiplier:1.0 - constant:-TrailingMargin], - [NSLayoutConstraint constraintWithItem:_dateLabel + constant:0.0], + [NSLayoutConstraint constraintWithItem:_detailLabel attribute:NSLayoutAttributeLeading - relatedBy:NSLayoutRelationEqual - toItem:_titleLabel + relatedBy:NSLayoutRelationGreaterThanOrEqual + toItem:self attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0.0], - [NSLayoutConstraint constraintWithItem:_dateLabel + [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeTrailing - relatedBy:NSLayoutRelationEqual - toItem:self + relatedBy:NSLayoutRelationGreaterThanOrEqual + toItem:_detailLabel attribute:NSLayoutAttributeTrailing multiplier:1.0 - constant:-TrailingMargin], - [NSLayoutConstraint constraintWithItem:_dateLabel - attribute:NSLayoutAttributeCenterY + constant:0.0], + [NSLayoutConstraint constraintWithItem:_detailLabel + attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual - toItem:self.heartView - attribute:NSLayoutAttributeCenterY + toItem:self + attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0], - [NSLayoutConstraint constraintWithItem:_titleLabel - attribute:NSLayoutAttributeBottom + [NSLayoutConstraint constraintWithItem:_imageView + attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual - toItem:_dateLabel - attribute:NSLayoutAttributeTop + toItem:self + attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0], - [NSLayoutConstraint constraintWithItem:_valuePercentageLabel + [NSLayoutConstraint constraintWithItem:_imageView attribute:NSLayoutAttributeLeading - relatedBy:NSLayoutRelationEqual - toItem:_titleLabel + relatedBy:NSLayoutRelationGreaterThanOrEqual + toItem:self attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0.0], - [NSLayoutConstraint constraintWithItem:_valuePercentageLabel - attribute:NSLayoutAttributeTop - relatedBy:NSLayoutRelationEqual - toItem:_dateLabel - attribute:NSLayoutAttributeBottom + [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationGreaterThanOrEqual + toItem:_imageView + attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0.0], - [NSLayoutConstraint constraintWithItem:_valuePercentageLabel - attribute:NSLayoutAttributeBottom + [NSLayoutConstraint constraintWithItem:_imageView + attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeBottom + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 - constant:-BottomMargin], - [NSLayoutConstraint constraintWithItem:_bottomEdge - attribute:NSLayoutAttributeBottom + constant:ImageViewSize], + [NSLayoutConstraint constraintWithItem:_imageView + attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeBottom + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:ImageViewSize], + [NSLayoutConstraint constraintWithItem:_monogramLabel + attribute:NSLayoutAttributeCenterX + relatedBy:NSLayoutRelationEqual + toItem:_imageView + attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0], - [NSLayoutConstraint constraintWithItem:_bottomEdge - attribute:NSLayoutAttributeHeight + [NSLayoutConstraint constraintWithItem:_monogramLabel + attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute + toItem:_imageView + attribute:NSLayoutAttributeCenterY multiplier:1.0 - constant:1.0], - [NSLayoutConstraint constraintWithItem:_bottomEdge + constant:0.0], + [NSLayoutConstraint constraintWithItem:_monogramLabel attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual - toItem:self + toItem:_imageView attribute:NSLayoutAttributeWidth multiplier:1.0 - constant:0.0], - [NSLayoutConstraint constraintWithItem:_topEdge - attribute:NSLayoutAttributeTop + constant:-HorizontalMargin], + [NSLayoutConstraint constraintWithItem:_bottomEdge + attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self - attribute:NSLayoutAttributeTop + attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0], - [NSLayoutConstraint constraintWithItem:_topEdge + [NSLayoutConstraint constraintWithItem:_bottomEdge attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:1.0], - [NSLayoutConstraint constraintWithItem:_topEdge + [NSLayoutConstraint constraintWithItem:_bottomEdge attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self @@ -292,14 +299,8 @@ - (void)setUpConstraints { [NSLayoutConstraint activateConstraints:_constraints]; } -- (void)setValue:(double)value { - _value = value; - [self updateView]; -} - -- (void)setDate:(NSString *)date { - _date = date; - [self updateView]; +- (void)updateImageViewBackgroundColor { + _imageView.backgroundColor = self.patient.image ? [UIColor clearColor] : OCKSystemGrayColor(); } - (void)tintColorDidChange { @@ -307,27 +308,4 @@ - (void)tintColorDidChange { [self updateView]; } - -#pragma mark - Helpers - -- (NSString *)valuePercentageString { - if (!_numberFormatter) { - _numberFormatter = [NSNumberFormatter new]; - _numberFormatter.numberStyle = NSNumberFormatterPercentStyle; - _numberFormatter.maximumFractionDigits = 0; - } - return [_numberFormatter stringFromNumber:@(self.value)]; -} - - -#pragma mark - Accessibility - -- (BOOL)isAccessibilityElement { - return YES; -} - -- (NSString *)accessibilityLabel { - return OCKAccessibilityStringForVariables(_valuePercentageLabel, _titleLabel, _dateLabel); -} - @end diff --git a/CareKit/Patient/OCKPatientWidget.h b/CareKit/Patient/OCKPatientWidget.h new file mode 100644 index 000000000..815c3012a --- /dev/null +++ b/CareKit/Patient/OCKPatientWidget.h @@ -0,0 +1,127 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import + + +NS_ASSUME_NONNULL_BEGIN + +/** + The `OCKPatientWidget` class is an object that represents a patient widget to be presented on `OCKInsightsViewController`. + + Some patient widgets can be populated using an activity identifier. + For these widgets, the content is automatically populated using the activity title and threshold values. + + All patient widgets can be manually populated with content also. + */ +OCK_CLASS_AVAILABLE +@interface OCKPatientWidget : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +/** + Returns a default patient widget using the specified activity identifier. + The widget content is automatically populated with activity title and threshold value. + + The tint color is only applied when a threshold has been triggered. + + @param activityIdentifier The widget activity identifier. + @param tintColor The widget tint color. + + @return An initialized patient widget object. + */ ++ (OCKPatientWidget *)defaultWidgetWithActivityIdentifier:(NSString *)activityIdentifier + tintColor:(nullable UIColor *)tintColor; + + +/** + Returns a default patient widget using the specified values. + A default widget includes a title label and a text label. + + @param title The widget title. + @param text The widget text. + @param tintColor The widget tint color. + + @return An initialized patient widget object. + */ ++ (OCKPatientWidget *)defaultWidgetWithTitle:(NSString *)title + text:(NSString *)text + tintColor:(nullable UIColor *)tintColor; + +/** + Returns a stacked patient widget using the specified values. + A stacked widget includes a 2x2 grid with an icon and text label stacked. + + @param primaryText The widget primary text. + @param primaryIcon The widget primary icon. + @param secondaryText The widget seconday text. + @param secondaryIcon The widget seconday icon. + @param tintColor The widget tint color. + + @return An initialized patient widget object. + */ ++ (OCKPatientWidget *)stackedWidgetWithPrimaryText:(NSString *)primaryText + primaryIcon:(UIImage *)primaryIcon + secondaryText:(nullable NSString *)secondaryText + secondaryIcon:(nullable UIImage *)secondaryIcon + tintColor:(nullable UIColor *)tintColor; + +/** + Returns a badge patient widget using the specified values. + A badge widget includes a title label and a numeric label. + + @param title The widget title. + @param value The widget numeric value. + @param tintColor The widget tint color. + + @return An initialized patient widget object. + */ ++ (OCKPatientWidget *)badgeWidgetWithTitle:(NSString *)title + value:(NSNumber *)value + tintColor:(nullable UIColor *)tintColor; + +/** + Returns an image patient widget using the specified values. + An image widget includes a title label and a text label. + + @param title The widget title. + @param image The widget image. + @param tintColor The widget tint color. + + @return An initialized patient widget object. + */ ++ (OCKPatientWidget *)imageWidgetWithTitle:(NSString *)title + image:(UIImage *)image + tintColor:(nullable UIColor *)tintColor; + +@end + +NS_ASSUME_NONNULL_END diff --git a/CareKit/Patient/OCKPatientWidget.m b/CareKit/Patient/OCKPatientWidget.m new file mode 100644 index 000000000..48da188bf --- /dev/null +++ b/CareKit/Patient/OCKPatientWidget.m @@ -0,0 +1,208 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import "OCKPatientWidget.h" +#import "OCKPatientWidget_Internal.h" +#import "OCKHelpers.h" + + +@implementation OCKPatientWidget + ++ (instancetype)new { + OCKThrowMethodUnavailableException(); + return nil; +} + ++ (OCKPatientWidget *)defaultWidgetWithActivityIdentifier:(NSString *)activityIdentifier + tintColor:(UIColor *)tintColor { + return [[OCKPatientWidget alloc] initWithWidgetType:OCKPatientWidgetTypeDefault + primaryActivityIdentifier:activityIdentifier + secondaryActivityIdentifier:nil + primaryText:nil + secondaryText:nil + primaryImage:nil + secondaryImage:nil + value:nil + tintColor:tintColor]; + +} + ++ (OCKPatientWidget *)defaultWidgetWithTitle:(NSString *)title + text:(NSString *)text + tintColor:(UIColor *)tintColor { + return [[OCKPatientWidget alloc] initWithWidgetType:OCKPatientWidgetTypeDefault + primaryActivityIdentifier:nil + secondaryActivityIdentifier:nil + primaryText:title + secondaryText:text + primaryImage:nil + secondaryImage:nil + value:nil + tintColor:tintColor]; +} + ++ (OCKPatientWidget *)stackedWidgetWithPrimaryText:(NSString *)primaryText + primaryIcon:(UIImage *)primaryIcon + secondaryText:(NSString *)secondaryText + secondaryIcon:(UIImage *)secondaryIcon + tintColor:(UIColor *)tintColor { + return [[OCKPatientWidget alloc] initWithWidgetType:OCKPatientWidgetTypeStacked + primaryActivityIdentifier:nil + secondaryActivityIdentifier:nil + primaryText:primaryText + secondaryText:secondaryText + primaryImage:primaryIcon + secondaryImage:secondaryIcon + value:nil + tintColor:tintColor]; +} + ++ (OCKPatientWidget *)imageWidgetWithTitle:(NSString *)title + image:(UIImage *)image + tintColor:(UIColor *)tintColor { + return [[OCKPatientWidget alloc] initWithWidgetType:OCKPatientWidgetTypeImage + primaryActivityIdentifier:nil + secondaryActivityIdentifier:nil + primaryText:title + secondaryText:nil + primaryImage:image + secondaryImage:nil + value:nil + tintColor:tintColor]; +} + ++ (OCKPatientWidget *)badgeWidgetWithTitle:(NSString *)title + value:(NSNumber *)value + tintColor:(UIColor *)tintColor { + return [[OCKPatientWidget alloc] initWithWidgetType:OCKPatientWidgetTypeBadge + primaryActivityIdentifier:nil + secondaryActivityIdentifier:nil + primaryText:title + secondaryText:nil + primaryImage:nil + secondaryImage:nil + value:value + tintColor:tintColor]; +} + +- (instancetype)initWithWidgetType:(OCKPatientWidgetType)type + primaryActivityIdentifier:(NSString *)primaryIdentifier + secondaryActivityIdentifier:(NSString *)secondaryIdentifier + primaryText:(NSString *)primaryText + secondaryText:(NSString *)secondaryText + primaryImage:(UIImage *)primaryImage + secondaryImage:(UIImage *)secondaryImage + value:(NSNumber *)value + tintColor:(UIColor *)tintColor { + self = [super init]; + if (self) { + _type = type; + _primaryIdentifier = [primaryIdentifier copy]; + _secondaryIdentifier = [secondaryIdentifier copy]; + _primaryText = [primaryText copy]; + _secondaryText = [secondaryText copy]; + _primaryImage = primaryImage; + _secondaryImage = secondaryImage; + _value = value; + _tintColor = tintColor; + } + return self; +} + +- (BOOL)isEqual:(id)object { + BOOL isParentSame = [super isEqual:object]; + + __typeof(self) castObject = object; + return (isParentSame && + self.type == castObject.type && + OCKEqualObjects(self.primaryIdentifier, castObject.primaryIdentifier) && + OCKEqualObjects(self.secondaryIdentifier, castObject.secondaryIdentifier) && + OCKEqualObjects(self.primaryText, castObject.primaryText) && + OCKEqualObjects(self.secondaryText, castObject.secondaryText) && + OCKEqualObjects(self.primaryImage, castObject.primaryImage) && + OCKEqualObjects(self.secondaryImage, castObject.secondaryImage) && + OCKEqualObjects(self.value, castObject.value) && + OCKEqualObjects(self.tintColor, castObject.tintColor)); +} + + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self) { + OCK_DECODE_ENUM(aDecoder, type); + OCK_DECODE_OBJ_CLASS(aDecoder, primaryIdentifier, NSString); + OCK_DECODE_OBJ_CLASS(aDecoder, secondaryIdentifier, NSString); + OCK_DECODE_OBJ_CLASS(aDecoder, primaryText, NSString); + OCK_DECODE_OBJ_CLASS(aDecoder, secondaryText, NSString); + OCK_DECODE_IMAGE(aDecoder, primaryImage); + OCK_DECODE_IMAGE(aDecoder, secondaryImage); + OCK_DECODE_OBJ_CLASS(aDecoder, value, NSNumber); + OCK_DECODE_OBJ_CLASS(aDecoder, tintColor, UIColor); + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + OCK_ENCODE_ENUM(aCoder, type); + OCK_ENCODE_OBJ(aCoder, primaryIdentifier); + OCK_ENCODE_OBJ(aCoder, secondaryIdentifier); + OCK_ENCODE_OBJ(aCoder, primaryText); + OCK_ENCODE_OBJ(aCoder, secondaryText); + OCK_ENCODE_IMAGE(aCoder, primaryImage); + OCK_ENCODE_IMAGE(aCoder, secondaryImage); + OCK_ENCODE_OBJ(aCoder, value); + OCK_ENCODE_OBJ(aCoder, tintColor); +} + + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone { + OCKPatientWidget *widget = [[[self class] allocWithZone:zone] init]; + widget->_type = self.type; + widget->_primaryIdentifier = [self.primaryIdentifier copy]; + widget->_secondaryIdentifier = [self.secondaryIdentifier copy]; + widget->_primaryText = [self.primaryText copy]; + widget->_secondaryText = [self.secondaryText copy]; + widget->_primaryImage = self.primaryImage; + widget->_secondaryImage = self.secondaryImage; + widget->_value = self.value; + widget->_tintColor = self.tintColor; + return widget; +} + +@end diff --git a/CareKit/SymptomTracker/OCKSymptomTrackerTableViewHeader.h b/CareKit/Patient/OCKPatientWidgetView.h similarity index 82% rename from CareKit/SymptomTracker/OCKSymptomTrackerTableViewHeader.h rename to CareKit/Patient/OCKPatientWidgetView.h index afd09069c..61bdf2ca8 100644 --- a/CareKit/SymptomTracker/OCKSymptomTrackerTableViewHeader.h +++ b/CareKit/Patient/OCKPatientWidgetView.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -32,20 +32,14 @@ #import -NS_ASSUME_NONNULL_BEGIN +@interface OCKPatientWidgetView : UIView -@class OCKRingView; ++ (OCKPatientWidgetView *)viewForWidget:(OCKPatientWidget *)widget; -@interface OCKSymptomTrackerTableViewHeader : UIView +@property (nonatomic, weak) OCKPatientWidget *widget; -@property (nonatomic) double value; +@property (nonatomic) BOOL shouldApplyTintColor; -@property (nonatomic, readonly) OCKRingView *ringView; - -@property (nonatomic, copy) NSString *title; - -@property (nonatomic, copy) NSString *date; +@property (nonatomic) BOOL animate; @end - -NS_ASSUME_NONNULL_END diff --git a/CareKit/Patient/OCKPatientWidgetView.m b/CareKit/Patient/OCKPatientWidgetView.m new file mode 100644 index 000000000..914745467 --- /dev/null +++ b/CareKit/Patient/OCKPatientWidgetView.m @@ -0,0 +1,82 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import "OCKPatientWidgetView.h" +#import "OCKPatientWidget_Internal.h" +#import "OCKDefaultPatientWidgetView.h" +#import "OCKStackedPatientWidgetView.h" +#import "OCKImagePatientWidgetView.h" +#import "OCKBadgePatientWidgetView.h" + + +@implementation OCKPatientWidgetView + ++ (OCKPatientWidgetView *)viewForWidget:(OCKPatientWidget *)widget { + OCKPatientWidgetView *view; + + switch (widget.type) { + case OCKPatientWidgetTypeDefault: + view = [[OCKDefaultPatientWidgetView alloc] initWithFrame:CGRectZero]; + break; + + case OCKPatientWidgetTypeStacked: + view = [[OCKStackedPatientWidgetView alloc] initWithFrame:CGRectZero]; + break; + + case OCKPatientWidgetTypeImage: + view = [[OCKImagePatientWidgetView alloc] initWithFrame:CGRectZero]; + break; + + case OCKPatientWidgetTypeBadge: + view = [[OCKBadgePatientWidgetView alloc] initWithFrame:CGRectZero]; + break; + + default: + break; + } + + return view; +} + +- (void)setWidget:(OCKPatientWidget *)widget { + _widget = widget; + [self updateView]; +} + +- (void)setShouldApplyTintColor:(BOOL)shouldApplyTintColor { + _shouldApplyTintColor = shouldApplyTintColor; + [self updateView]; +} + +- (void)updateView { +} + +@end diff --git a/CareKit/Patient/OCKPatientWidget_Internal.h b/CareKit/Patient/OCKPatientWidget_Internal.h new file mode 100644 index 000000000..8c9651f4d --- /dev/null +++ b/CareKit/Patient/OCKPatientWidget_Internal.h @@ -0,0 +1,95 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import "OCKPatientWidget.h" +#import "OCKPatientWidgetView.h" + + +NS_ASSUME_NONNULL_BEGIN + +@interface OCKPatientWidget() + +/** + An enumeration of the types of patient widgets available. + */ +typedef NS_ENUM(NSUInteger, OCKPatientWidgetType) { + /** + A default widget with a title and text. + */ + OCKPatientWidgetTypeDefault = 0, + + /** + A 2x2 widget with an icon and text stacked. + */ + OCKPatientWidgetTypeStacked, + + /** + A widget with a title and image. + */ + OCKPatientWidgetTypeImage, + + /** + A widget with a title and numeric badge. + */ + OCKPatientWidgetTypeBadge +}; + +- (instancetype)initWithWidgetType:(OCKPatientWidgetType)type + primaryActivityIdentifier:(nullable NSString *)primaryIdentifier + secondaryActivityIdentifier:(nullable NSString *)secondaryIdentifier + primaryText:(nullable NSString *)primaryText + secondaryText:(nullable NSString *)secondaryText + primaryImage:(nullable UIImage *)primaryImage + secondaryImage:(nullable UIImage *)secondaryImage + value:(nullable NSNumber *)value + tintColor:(nullable UIColor *)tintColor; + +@property (nonatomic, readonly) OCKPatientWidgetType type; + +@property (nonatomic, copy, readonly, nullable) NSString *primaryIdentifier; + +@property (nonatomic, copy, readonly, nullable) NSString *secondaryIdentifier; + +@property (nonatomic, copy, readonly, nullable) NSString *primaryText; + +@property (nonatomic, copy, readonly, nullable) NSString *secondaryText; + +@property (nonatomic, readonly, nullable) UIImage *primaryImage; + +@property (nonatomic, readonly, nullable) UIImage *secondaryImage; + +@property (nonatomic, readonly, nullable) NSNumber *value; + +@property (nonatomic, readonly, nullable) UIColor *tintColor; + +@end + +NS_ASSUME_NONNULL_END diff --git a/CareKit/Patient/OCKStackedPatientWidgetView.h b/CareKit/Patient/OCKStackedPatientWidgetView.h new file mode 100644 index 000000000..35ec15755 --- /dev/null +++ b/CareKit/Patient/OCKStackedPatientWidgetView.h @@ -0,0 +1,37 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import "OCKPatientWidgetView.h" + + +@interface OCKStackedPatientWidgetView : OCKPatientWidgetView + +@end diff --git a/CareKit/Patient/OCKStackedPatientWidgetView.m b/CareKit/Patient/OCKStackedPatientWidgetView.m new file mode 100644 index 000000000..ae143e5c1 --- /dev/null +++ b/CareKit/Patient/OCKStackedPatientWidgetView.m @@ -0,0 +1,206 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import "OCKStackedPatientWidgetView.h" +#import "OCKPatientWidget_Internal.h" + + +@implementation OCKStackedPatientWidgetView { + UIImageView *_primaryIconImageView; + UILabel *_primaryTextLabel; + UIImageView *_secondaryIconImageView; + UILabel *_secondaryTextLabel; + UIStackView *_verticalStackView; + UIStackView *_primaryHorizontalStackView; + UIStackView *_secondaryHorizontalStackView; + NSMutableArray *_constraints; +} + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self prepareView]; + } + return self; +} + +- (void)prepareView { + if (!_primaryIconImageView) { + _primaryIconImageView = [UIImageView new]; + _primaryIconImageView.contentMode = UIViewContentModeScaleAspectFill; + } + + if (!_primaryTextLabel) { + _primaryTextLabel = [UILabel new]; + _primaryTextLabel.numberOfLines = 1; + _primaryTextLabel.lineBreakMode = NSLineBreakByTruncatingTail; + _primaryTextLabel.font = [UIFont systemFontOfSize:14.0]; + } + + if (!_primaryHorizontalStackView) { + _primaryHorizontalStackView = [[UIStackView alloc] initWithArrangedSubviews:@[_primaryIconImageView, _primaryTextLabel]]; + _primaryHorizontalStackView.distribution = UIStackViewDistributionFillProportionally; + _primaryHorizontalStackView.alignment = UIStackViewAlignmentCenter; + _primaryHorizontalStackView.spacing = 10.0; + } + + if (!_secondaryIconImageView) { + _secondaryIconImageView = [UIImageView new]; + _secondaryIconImageView.contentMode = UIViewContentModeScaleAspectFill; + } + + if (!_secondaryTextLabel) { + _secondaryTextLabel = [UILabel new]; + _secondaryTextLabel.numberOfLines = 1; + _secondaryTextLabel.lineBreakMode = NSLineBreakByTruncatingTail; + _secondaryTextLabel.font = [UIFont systemFontOfSize:14.0]; + } + + if (!_secondaryHorizontalStackView) { + _secondaryHorizontalStackView = [[UIStackView alloc] initWithArrangedSubviews:@[_secondaryIconImageView, _secondaryTextLabel]]; + _secondaryHorizontalStackView.distribution = UIStackViewDistributionFillProportionally; + _secondaryHorizontalStackView.alignment = UIStackViewAlignmentCenter; + _secondaryHorizontalStackView.spacing = 10.0; + } + + if (!_verticalStackView) { + _verticalStackView = [[UIStackView alloc] initWithArrangedSubviews:@[_primaryHorizontalStackView, _secondaryHorizontalStackView]]; + _verticalStackView.axis = UILayoutConstraintAxisVertical; + _verticalStackView.distribution = UIStackViewDistributionFillEqually; + _verticalStackView.alignment = UIStackViewAlignmentLeading; + _verticalStackView.spacing = 2.0; + [self addSubview:_verticalStackView]; + } + + [self updateView]; + [self setUpConstraints]; +} + +- (void)updateView { + + if (self.animate) { + CATransition *animation = [CATransition animation]; + animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + animation.type = kCATransitionFade; + animation.duration = 0.75; + [_primaryIconImageView.layer addAnimation:animation forKey:@"kCATransitionFade"]; + [_secondaryIconImageView.layer addAnimation:animation forKey:@"kCATransitionFade"]; + [_primaryTextLabel.layer addAnimation:animation forKey:@"kCATransitionFade"]; + [_secondaryTextLabel.layer addAnimation:animation forKey:@"kCATransitionFade"]; + } + + _primaryIconImageView.image = self.widget.primaryImage; + _secondaryIconImageView.image = self.widget.secondaryImage; + _primaryTextLabel.text = self.widget.primaryText; + _secondaryTextLabel.text = self.widget.secondaryText; + + _primaryTextLabel.textColor = self.shouldApplyTintColor ? self.widget.tintColor : [UIColor blackColor]; + _secondaryTextLabel.textColor = self.shouldApplyTintColor ? self.widget.tintColor : [UIColor blackColor]; +} + +- (void)setUpConstraints { + [NSLayoutConstraint deactivateConstraints:_constraints]; + + _constraints = [NSMutableArray new]; + + _verticalStackView.translatesAutoresizingMaskIntoConstraints = NO; + _primaryIconImageView.translatesAutoresizingMaskIntoConstraints = NO; + _secondaryIconImageView.translatesAutoresizingMaskIntoConstraints = NO; + + [_constraints addObjectsFromArray:@[ + [NSLayoutConstraint constraintWithItem:_verticalStackView + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_verticalStackView + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_verticalStackView + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_verticalStackView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_primaryIconImageView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:10.0], + [NSLayoutConstraint constraintWithItem:_primaryIconImageView + attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:10.0], + [NSLayoutConstraint constraintWithItem:_secondaryIconImageView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:10.0], + [NSLayoutConstraint constraintWithItem:_secondaryIconImageView + attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:10.0], + + ]]; + + [NSLayoutConstraint activateConstraints:_constraints]; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + [self setUpConstraints]; +} + +@end diff --git a/CareKit/SymptomTracker/OCKRingButton.h b/CareKit/SymptomTracker/OCKRingButton.h index f43a75075..78903135e 100644 --- a/CareKit/SymptomTracker/OCKRingButton.h +++ b/CareKit/SymptomTracker/OCKRingButton.h @@ -36,8 +36,10 @@ @interface OCKRingButton : UIButton +// Initial value is 0 @property (nonatomic) double value; +// Ring view for every day of the week @property (nonatomic) OCKRingView *ringView; @end diff --git a/CareKit/SymptomTracker/OCKRingView.h b/CareKit/SymptomTracker/OCKRingView.h index 50ddf8c49..562ef0b43 100644 --- a/CareKit/SymptomTracker/OCKRingView.h +++ b/CareKit/SymptomTracker/OCKRingView.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -30,12 +30,15 @@ #import +#import "CareKit/CareKit.h" +NS_ASSUME_NONNULL_BEGIN + @interface OCKRingView : UIView -// Default value is NO -@property (nonatomic) BOOL hideLabel; +- (instancetype)initWithFrame:(CGRect)frame + useSmallRing:(BOOL)useSmallRing; // Default value is NO @property (nonatomic) BOOL disableAnimation; @@ -43,4 +46,18 @@ // Initial value is 0 @property (nonatomic) double value; +// Forwarded to ringView (see OCKGlyphType). +@property (nonatomic) OCKGlyphType glyphType; + +//Boolean true for OCKCareCardViewController, false for OCKSymptomTrackerViewCOntroller +@property (nonatomic) BOOL isCareCard; + +// Image required to show activity. +@property (nonatomic, nullable) UIImage *glyphImage; + +// Boolean value to specify the size of the ring view. +@property (nonatomic, readonly) BOOL useSmallRing; + @end + +NS_ASSUME_NONNULL_END diff --git a/CareKit/SymptomTracker/OCKRingView.m b/CareKit/SymptomTracker/OCKRingView.m index 771422482..f5615eb91 100644 --- a/CareKit/SymptomTracker/OCKRingView.m +++ b/CareKit/SymptomTracker/OCKRingView.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -27,99 +27,198 @@ 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 "OCKRingView.h" +#import "OCKGlyph_Internal.h" +#import "OCKHeaderView.h" +#import "OCKRingButton.h" static const double VALUE_MIN = 0.0; static const double VALUE_MAX = 1.0; + @implementation OCKRingView { CAShapeLayer *_circleLayer; CAShapeLayer *_backgroundLayer; - CAShapeLayer *_checkmarkLayer; - UILabel *_label; + CAShapeLayer *_activeGlyphLayer; + CAShapeLayer *_filledCircleLayer; + NSNumberFormatter *_numberFormatter; NSUUID *_transactionID; + + UIImageView *_glyphImageView; } -- (instancetype)initWithFrame:(CGRect)frame { +- (instancetype)initWithFrame:(CGRect)frame + useSmallRing:(BOOL)useSmallRing { self = [super initWithFrame:frame]; if (self) { _value = VALUE_MIN; - _label = [self createLabel]; + + _useSmallRing = useSmallRing; _numberFormatter = [[NSNumberFormatter alloc] init]; [_numberFormatter setNumberStyle:NSNumberFormatterPercentStyle]; [_numberFormatter setMaximumFractionDigits:0]; [_numberFormatter setMultiplier:@100]; - [self updateLabel]; - _backgroundLayer = [self createShapeLayerWithValue:VALUE_MAX]; + _backgroundLayer.borderWidth = 10; + _backgroundLayer.borderColor = self.tintColor.CGColor; _backgroundLayer.strokeColor = [UIColor groupTableViewBackgroundColor].CGColor; [self.layer addSublayer:_backgroundLayer]; - _checkmarkLayer = [self createCheckMarkLayer]; - _checkmarkLayer.strokeEnd = 0.0; - [self.layer addSublayer:_checkmarkLayer]; - [self updateCheckmarkForValue:_value]; + _activeGlyphLayer = [self createActiveGlyphLayer]; + _activeGlyphLayer.strokeEnd = 0.0; + + [self.layer addSublayer:_activeGlyphLayer]; + _glyphImageView = [self createGlyphView]; + + _filledCircleLayer = [self filledCircleLayer]; + + [self addSubview:_glyphImageView]; + } return self; } +- (void)setValue:(double)value { + if (value != _value) { + + double oldValue = _value; + _value = value; + + if (_disableAnimation) { + [_circleLayer removeFromSuperlayer]; + _circleLayer = [self createShapeLayerWithValue:_value]; + [self.layer insertSublayer:_circleLayer below:_activeGlyphLayer]; + [self updateGlyphForValue:_value animate:NO]; + + } else { + + if (oldValue == VALUE_MAX && _value < VALUE_MAX) { + [self updateGlyphForValue:_value animate:NO]; + } + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + NSNumber *presentationLayerValue; + if (![_circleLayer.presentationLayer animationForKey:@"strokeStart"]) { + presentationLayerValue = @(1.0 - oldValue); + } else { + presentationLayerValue = [_circleLayer.presentationLayer valueForKey:@"strokeStart"]; + [_circleLayer removeAllAnimations]; + } + + NSUUID *caid = [NSUUID UUID]; + _transactionID = caid; + + [CATransaction begin]; + + [_circleLayer removeFromSuperlayer]; + _circleLayer = [self createShapeLayerWithValue:VALUE_MAX]; + [self.layer insertSublayer:_circleLayer below:_activeGlyphLayer]; + + CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeStart"]; + animation.fromValue = @([presentationLayerValue doubleValue]); + animation.toValue = @(1.0 - value); + animation.beginTime = 0.0; + animation.duration = 1.25; + animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + animation.fillMode = kCAFillModeBoth; + animation.removedOnCompletion = NO; + + [CATransaction setCompletionBlock:^{ + if([caid isEqual:_transactionID]){ + if ((oldValue == VALUE_MAX && _value < VALUE_MAX) || (oldValue < VALUE_MAX && _value == VALUE_MAX)) { + [self updateGlyphForValue:value animate:YES]; + } + if (_value == VALUE_MIN) { + [_circleLayer removeFromSuperlayer]; + } + } + }]; + + [_circleLayer addAnimation:animation forKey:animation.keyPath]; + [CATransaction commit]; + }); + } + + } else { + if (value != VALUE_MAX) { + if(![self.superview isKindOfClass:[OCKHeaderView class]]){ + _glyphImageView.image = nil; + _backgroundLayer.fillColor = [UIColor clearColor].CGColor; + } else{ + [_glyphImageView setImage:[self getGlyphImage]]; + _backgroundLayer.fillColor = [UIColor clearColor].CGColor; + [_filledCircleLayer removeFromSuperlayer]; + + CAShapeLayer *maskLayer = [self getGlyphMaskLayer]; + _glyphImageView.layer.mask = maskLayer; + } + } + } +} + +- (UIImageView *)createGlyphView { + NSInteger viewSize = self.useSmallRing ? 15.0 : 75.0; + CGRect glyphViewFrame = CGRectMake(self.frame.origin.x, self.frame.origin.y, viewSize, viewSize); + UIImageView *glyphImageView = [[UIImageView alloc] initWithFrame:glyphViewFrame]; + glyphImageView.center = [self ringCenter]; + return glyphImageView; +} + - (CAShapeLayer *)createShapeLayerWithValue:(double)value { - - CGFloat diameter = MIN(self.frame.size.height, self.frame.size.width) * 0.9; + CGFloat diameter = [self ringRadius] * 2; CAShapeLayer *layer = [CAShapeLayer layer]; + layer.lineCap = kCALineCapRound; layer.path = [self createPathWithValue:value].CGPath; layer.fillColor = [UIColor clearColor].CGColor; layer.strokeColor = self.tintColor.CGColor; layer.lineWidth = diameter * 0.1; - return layer; } - (UIBezierPath *)createPathWithValue:(double)value { - CGFloat radius = [self ringRadius]; UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:[self ringCenter] radius:radius startAngle:2 * M_PI * (value - 0.25) endAngle:-M_PI_2 clockwise:NO]; - return path; } -- (CAShapeLayer *)createCheckMarkLayer { +- (CAShapeLayer *)createActiveGlyphLayer { CGPoint ringCenter = [self ringCenter]; UIBezierPath *path = [UIBezierPath new]; + [path moveToPoint:CGPointMake(ringCenter.x*37.0/61.0, ringCenter.x*65.0/61.0)]; [path addLineToPoint:CGPointMake(ringCenter.x*50.0/61.0, ringCenter.x*78.0/61.0)]; [path addLineToPoint:CGPointMake(ringCenter.x*87.0/61.0, ringCenter.x*42.0/61.0)]; path.lineCapStyle = kCGLineCapSquare; path.lineWidth = [self ringRadius]*2/9; + CAShapeLayer *shapeLayer = [CAShapeLayer new]; shapeLayer.path = path.CGPath; shapeLayer.lineWidth = path.lineWidth; + shapeLayer.lineCap = kCALineCapRound; shapeLayer.lineJoin = kCALineJoinRound; shapeLayer.frame = self.layer.bounds; shapeLayer.strokeColor = [UIColor whiteColor].CGColor; shapeLayer.backgroundColor = [UIColor clearColor].CGColor; shapeLayer.fillColor = nil; - return shapeLayer; } - (CGFloat)ringRadius { - CGFloat radius = MIN(self.frame.size.height, self.frame.size.width) * 0.9 * 0.5; - return radius; + return self.useSmallRing ? 13.5 : 50.0; } - (CGPoint)ringCenter { @@ -128,130 +227,115 @@ - (CGPoint)ringCenter { return CGPointMake(radius + OuterMargin, radius + OuterMargin); } -- (UILabel *)createLabel { - UILabel *label = [[UILabel alloc] init]; - CGFloat radius = [self ringRadius]; - label.frame = CGRectMake(0, 0, radius * 1.414, radius); - label.center = [self ringCenter]; - label.textAlignment = NSTextAlignmentCenter; - - label.adjustsFontSizeToFitWidth = YES; - label.numberOfLines = 1; - - label.font = [UIFont fontWithName:label.font.fontName size:radius/2.2]; - - return label; -} - - (void)tintColorDidChange { [super tintColorDidChange]; _circleLayer.strokeColor = self.tintColor.CGColor; if (_circleLayer.fillColor != [UIColor clearColor].CGColor) { _circleLayer.fillColor = self.tintColor.CGColor; } + _backgroundLayer.borderColor = self.tintColor.CGColor; } -- (void)setHideLabel:(BOOL)hideLabel { - _hideLabel = hideLabel; - [self updateLabel]; +- (void)updateGlyphForValue:(double)value animate:(BOOL)animate { + _glyphImageView.alpha = 1.0; + if (value == VALUE_MAX) { + _glyphImageView.layer.mask = nil; + _backgroundLayer.strokeColor = self.tintColor.CGColor; + + if(![self.superview isKindOfClass:[OCKHeaderView class]]) { + UIImage *smallImage = [self getCompletionImage]; + _glyphImageView.image = smallImage; + _backgroundLayer.fillColor = self.tintColor.CGColor; + } else { + UIImage *bigImage = [self getCompletionImage]; + if (animate) { + [UIView transitionWithView:_glyphImageView duration:0.25f options:UIViewAnimationOptionTransitionCrossDissolve animations:^{ + [self ringAnimation]; + _glyphImageView.image = bigImage; + } completion:nil]; + + } else { + _backgroundLayer.fillColor = self.tintColor.CGColor; + _glyphImageView.image = bigImage; + } + } + } else { + CAShapeLayer *maskLayer = [self getGlyphMaskLayer]; + _glyphImageView.layer.mask = maskLayer; + + _backgroundLayer.strokeColor = [UIColor groupTableViewBackgroundColor].CGColor; + + if(![self.superview isKindOfClass:[OCKHeaderView class]]){ + _glyphImageView.image = nil; + _backgroundLayer.fillColor = [UIColor clearColor].CGColor; + }else{ + UIImage *glyphImage = [self getGlyphImage]; + + + + _backgroundLayer.fillColor = [UIColor clearColor].CGColor; + [_filledCircleLayer removeFromSuperlayer]; + + + + _glyphImageView.image = glyphImage; + _glyphImageView.alpha = 1.0; + } + } } -- (void)updateLabel { - if (_hideLabel) { - [_label removeFromSuperview]; - } else { - _label.text = [_numberFormatter stringFromNumber:@(_value)]; - [self addSubview:_label]; - } +- (CAShapeLayer *)filledCircleLayer { + CAShapeLayer *filledCircle = [CAShapeLayer layer]; + CGRect bounds = CGRectMake(5, 5, 2*[self ringRadius], 2*[self ringRadius]); + UIBezierPath *maskLayerPath = [UIBezierPath bezierPathWithRoundedRect:bounds cornerRadius:[self ringRadius]]; + filledCircle.path = maskLayerPath.CGPath; + filledCircle.fillColor = [UIColor whiteColor].CGColor; + return filledCircle; } -- (void)updateCheckmarkForValue:(double)value { +- (void)ringAnimation { + [self.layer insertSublayer:_filledCircleLayer above:_backgroundLayer]; + _backgroundLayer.fillColor = self.tintColor.CGColor; - [CATransaction begin]; - if (value == VALUE_MAX) { - [CATransaction setDisableActions:_disableAnimation]; - [_label removeFromSuperview]; - _circleLayer.fillColor = self.tintColor.CGColor; - _checkmarkLayer.strokeEnd = 1.0; - } else { - [CATransaction setDisableActions:YES]; - _circleLayer.fillColor = [UIColor clearColor].CGColor; - _checkmarkLayer.strokeEnd = 0.0; - } - [CATransaction commit]; + UIBezierPath *endShape = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(55, 55, 0, 0) + cornerRadius:[self ringRadius]]; + CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"]; + animation.toValue = (__bridge id _Nullable)(endShape.CGPath); + animation.duration = 0.5; + animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; + animation.fillMode = kCAFillModeBoth; + animation.removedOnCompletion = NO; + + [_filledCircleLayer addAnimation:animation forKey:animation.keyPath]; } -- (void)setValue:(double)value { - - value = MAX(MIN(value, VALUE_MAX), VALUE_MIN); - - if (value != _value) { - double oldValue = _value; - _value = value; - - if (_disableAnimation) { - [self updateLabel]; - [_circleLayer removeFromSuperlayer]; - _circleLayer = [self createShapeLayerWithValue:_value]; - [self.layer insertSublayer:_circleLayer below:_checkmarkLayer]; - [self updateCheckmarkForValue:_value]; - } else { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - - [_circleLayer removeAllAnimations]; - NSUUID *caid = [NSUUID UUID]; - _transactionID = caid; +- (UIImage *)getCompletionImage { + UIImage *image; + if (_isCareCard) { + image = [UIImage imageNamed:@"star" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil]; + } + else { + image = [UIImage imageNamed:@"checkmark" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil]; + } + return image; +} - [self updateCheckmarkForValue:oldValue]; - [self updateLabel]; - - [CATransaction begin]; - - BOOL reverse = oldValue > _value; - double delta = ABS(_value - oldValue); - double maxValue = MAX(oldValue, _value); - - [_circleLayer removeFromSuperlayer]; - _circleLayer = [self createShapeLayerWithValue:maxValue]; - [self.layer insertSublayer:_circleLayer below:_checkmarkLayer]; - - CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeStart"]; - - if (reverse) { - animation.fromValue = @(VALUE_MIN); - animation.toValue = @(delta/maxValue); - } else { - animation.fromValue = @(delta/maxValue); - animation.toValue = @(VALUE_MIN); - } - - animation.beginTime = 0.0; - animation.duration = 1.25; - animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; - animation.fillMode = kCAFillModeBoth; - animation.removedOnCompletion = NO; - - [CATransaction setCompletionBlock:^{ - if ([caid isEqual:_transactionID]) { - [self updateLabel]; - [self updateCheckmarkForValue:_value]; - if (_value == VALUE_MIN) { - [_circleLayer removeFromSuperlayer]; - } - } - }]; - - [_circleLayer addAnimation:animation forKey:animation.keyPath]; - [CATransaction commit]; - }); - } - +- (UIImage *)getGlyphImage { + UIImage *image; + if (self.glyphType == OCKGlyphTypeCustom) { + image = [self.glyphImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + } else { + image = [[OCKGlyph glyphImageForType:self.glyphType] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; } + return image; } -- (NSString *)accessibilityLabel { - return [_label accessibilityLabel]; +- (CAShapeLayer *)getGlyphMaskLayer { + UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect: _glyphImageView.bounds]; + CAShapeLayer *maskLayer = [CAShapeLayer layer]; + maskLayer.path = path.CGPath; + + return maskLayer; } @end - diff --git a/CareKit/SymptomTracker/OCKSymptomTrackerTableViewCell.m b/CareKit/SymptomTracker/OCKSymptomTrackerTableViewCell.m index a67df3426..eb80c0463 100644 --- a/CareKit/SymptomTracker/OCKSymptomTrackerTableViewCell.m +++ b/CareKit/SymptomTracker/OCKSymptomTrackerTableViewCell.m @@ -61,10 +61,14 @@ - (void)setAssessmentEvent:(OCKCarePlanEvent *)assessmentEvent { [self prepareView]; } +- (BOOL)shouldShowDisclosureIndicator { + return !(self.assessmentEvent.state == OCKCarePlanEventStateCompleted && !self.assessmentEvent.activity.resultResettable); +} + - (void)prepareView { [super prepareView]; - self.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + self.accessoryType = [self shouldShowDisclosureIndicator] ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone; if (!_titleLabel) { _titleLabel = [OCKLabel new]; @@ -122,7 +126,7 @@ - (void)setUpConstraints { _valueLabel.translatesAutoresizingMaskIntoConstraints = NO; CGFloat LeadingMargin = self.separatorInset.left; - CGFloat TrailingMargin = (self.separatorInset.right > 0) ? self.separatorInset.right + 25 : 40; + CGFloat TrailingMargin = (self.separatorInset.right > 0) ? self.separatorInset.right + 25 : 30; CGFloat unitLabelOffset = 0; @@ -163,6 +167,20 @@ - (void)setUpConstraints { } [_constraints addObjectsFromArray:@[ + [NSLayoutConstraint constraintWithItem:_valueLabel + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationGreaterThanOrEqual + toItem:_titleLabel + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:HorizontalMargin], + [NSLayoutConstraint constraintWithItem:_valueLabel + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationGreaterThanOrEqual + toItem:_textLabel + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:HorizontalMargin], [NSLayoutConstraint constraintWithItem:_titleLabel attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual @@ -191,13 +209,6 @@ - (void)setUpConstraints { attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0], - [NSLayoutConstraint constraintWithItem:_valueLabel - attribute:NSLayoutAttributeLeading - relatedBy:NSLayoutRelationGreaterThanOrEqual - toItem:_textLabel - attribute:NSLayoutAttributeTrailing - multiplier:1.0 - constant:2*HorizontalMargin], [NSLayoutConstraint constraintWithItem:_textLabel attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual @@ -205,13 +216,6 @@ - (void)setUpConstraints { attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-BottomMargin], - [NSLayoutConstraint constraintWithItem:_valueLabel - attribute:NSLayoutAttributeLeading - relatedBy:NSLayoutRelationGreaterThanOrEqual - toItem:_titleLabel - attribute:NSLayoutAttributeTrailing - multiplier:1.0 - constant:HorizontalMargin], [NSLayoutConstraint constraintWithItem:_valueLabel attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual diff --git a/CareKit/SymptomTracker/OCKSymptomTrackerViewController.h b/CareKit/SymptomTracker/OCKSymptomTrackerViewController.h index cebd182f0..abc604b11 100644 --- a/CareKit/SymptomTracker/OCKSymptomTrackerViewController.h +++ b/CareKit/SymptomTracker/OCKSymptomTrackerViewController.h @@ -1,6 +1,6 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. - Copyright (c) 2016, Erik Hornberger. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. + Copyright (c) 2017, Erik Hornberger. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -123,7 +123,7 @@ OCK_CLASS_AVAILABLE If the value is not specified, the app's tint color is used. */ -@property (nonatomic, null_resettable) UIColor *progressRingTintColor; +@property (nonatomic, null_resettable) UIColor *glyphTintColor; /** The string that will be used as the Symptom Tracker header title. @@ -133,11 +133,15 @@ OCK_CLASS_AVAILABLE @property (nonatomic, null_resettable) NSString *headerTitle; /** - A boolean to show the edge indicators. - - The default value is NO. + The glyph type for the header view (see OCKGlyphType). + */ +@property (nonatomic) OCKGlyphType glyphType; + +/** + Image name string if using a custom image. Cannot access image name once image has been created + and we need a way to access that to send the custom image name string to the watch */ -@property (nonatomic) BOOL showEdgeIndicators; +@property (nonatomic) NSString *customGlyphImageName; @end diff --git a/CareKit/SymptomTracker/OCKSymptomTrackerViewController.m b/CareKit/SymptomTracker/OCKSymptomTrackerViewController.m index 5e9f5ecee..fce32de74 100644 --- a/CareKit/SymptomTracker/OCKSymptomTrackerViewController.m +++ b/CareKit/SymptomTracker/OCKSymptomTrackerViewController.m @@ -1,6 +1,6 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. - Copyright (c) 2016, Erik Hornberger. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. + Copyright (c) 2017, Erik Hornberger. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -32,15 +32,15 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE #import "OCKSymptomTrackerViewController.h" #import "OCKWeekViewController.h" -#import "OCKSymptomTrackerWeekView.h" #import "NSDateComponents+CarePlanInternal.h" -#import "OCKSymptomTrackerTableViewHeader.h" +#import "OCKWeekView.h" +#import "OCKHeaderView.h" #import "OCKSymptomTrackerTableViewCell.h" -#import "OCKHeartView.h" #import "OCKWeekLabelsView.h" #import "OCKCarePlanStore_Internal.h" #import "OCKHelpers.h" #import "OCKDefines_Private.h" +#import "OCKGlyph_Internal.h" @interface OCKSymptomTrackerViewController() @@ -51,13 +51,19 @@ @interface OCKSymptomTrackerViewController() *_events; + UITableView *_tableView; + NSMutableArray *> *_events; NSMutableArray *_weekValues; - OCKSymptomTrackerTableViewHeader *_headerView; + OCKHeaderView *_headerView; UIPageViewController *_pageViewController; OCKWeekViewController *_weekViewController; NSCalendar *_calendar; NSMutableArray *_constraints; + NSMutableArray *_sectionTitles; + NSMutableArray *> *> *_tableViewData; + NSString *_otherString; + NSString *_optionalString; + } - (instancetype)init { @@ -69,22 +75,28 @@ - (instancetype)initWithCarePlanStore:(OCKCarePlanStore *)store { self = [super init]; if (self) { _store = store; - _showEdgeIndicators = NO; _calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian]; + _glyphType = OCKGlyphTypeStethoscope; + _glyphTintColor = nil; } return self; } - (void)viewDidLoad { [super viewDidLoad]; + _otherString = OCKLocalizedString(@"ACTIVITY_TYPE_OTHER_SECTION_HEADER", nil); + _optionalString = OCKLocalizedString(@"ACTIVITY_TYPE_OPTIONAL_SECTION_HEADER", nil); + self.view.backgroundColor = [UIColor groupTableViewBackgroundColor]; + + self.store.symptomTrackerUIDelegate = self; - _store.symptomTrackerUIDelegate = self; + [self setGlyphTintColor: _glyphTintColor]; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:OCKLocalizedString(@"TODAY_BUTTON_TITLE", nil) style:UIBarButtonItemStylePlain target:self action:@selector(showToday:)]; - self.navigationItem.rightBarButtonItem.tintColor = self.progressRingTintColor; + self.navigationItem.rightBarButtonItem.tintColor = self.glyphTintColor; _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; _tableView.dataSource = self; @@ -97,15 +109,20 @@ - (void)viewDidLoad { _tableView.estimatedRowHeight = 90.0; _tableView.rowHeight = UITableViewAutomaticDimension; - _tableView.estimatedSectionHeaderHeight = 100; - _tableView.sectionHeaderHeight = UITableViewAutomaticDimension; + _tableView.tableFooterView = [UIView new]; + _tableView.estimatedSectionHeaderHeight = 0; + _tableView.estimatedSectionFooterHeight = 0; + + self.navigationController.navigationBar.translucent = NO; + [self.navigationController.navigationBar setBarTintColor:[UIColor colorWithRed:245.0/255.0 green:244.0/255.0 blue:246.0/255.0 alpha:1.0]]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; NSAssert(self.navigationController, @"OCKSymptomTrackerViewController must be embedded in a navigation controller."); - _weekViewController.symptomTrackerWeekView.delegate = self; + + _weekViewController.weekView.delegate = self; } - (void)showToday:(id)sender { @@ -115,14 +132,22 @@ - (void)showToday:(id)sender { - (void)prepareView { if (!_headerView) { - _headerView = [[OCKSymptomTrackerTableViewHeader alloc] initWithFrame:CGRectZero]; + _headerView = [[OCKHeaderView alloc] initWithFrame:CGRectZero]; + [self.view addSubview:_headerView]; + + } + _headerView.tintColor = self.glyphTintColor; + if (self.glyphType == OCKGlyphTypeCustom) { + UIImage *glyphImage = [self createCustomImageName:self.customGlyphImageName]; + _headerView.glyphImage = glyphImage; } if ([_headerTitle length] == 0) { _headerTitle = OCKLocalizedString(@"SYMPTOM_TRACKER_HEADER_TITLE", nil); } _headerView.title = _headerTitle; - _headerView.tintColor = self.progressRingTintColor; - + _headerView.isCareCard = NO; + _headerView.glyphType = self.glyphType; + if (!_pageViewController) { _pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal @@ -130,16 +155,31 @@ - (void)prepareView { _pageViewController.dataSource = self; _pageViewController.delegate = self; - OCKWeekViewController *weekController = [[OCKWeekViewController alloc] initWithShowCareCardWeekView:NO]; - weekController.symptomTrackerWeekView.delegate = _weekViewController.symptomTrackerWeekView.delegate; - weekController.symptomTrackerWeekView.tintColor = self.progressRingTintColor; + if (!UIAccessibilityIsReduceTransparencyEnabled()) { + _pageViewController.view.backgroundColor = [UIColor groupTableViewBackgroundColor]; + + UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleProminent]; + UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect]; + blurEffectView.frame = _pageViewController.view.bounds; + blurEffectView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [_pageViewController.view insertSubview:blurEffectView atIndex:_pageViewController.view.subviews.count-1]; + } + else { + _pageViewController.view.backgroundColor = [UIColor whiteColor]; + } + + OCKWeekViewController *weekController = [OCKWeekViewController new]; + weekController.weekView.delegate = _weekViewController.weekView.delegate; + weekController.weekView.ringTintColor = self.glyphTintColor; + weekController.weekView.isCareCard = NO; + weekController.weekView.glyphType = self.glyphType; _weekViewController = weekController; [_pageViewController setViewControllers:@[weekController] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil]; + [self.view addSubview:_pageViewController.view]; } - _tableView.tableHeaderView = _pageViewController.view; - _tableView.tableFooterView = [UIView new]; + _tableView.showsVerticalScrollIndicator = NO; [self setUpConstraints]; } @@ -151,12 +191,55 @@ - (void)setUpConstraints { _tableView.translatesAutoresizingMaskIntoConstraints = NO; _pageViewController.view.translatesAutoresizingMaskIntoConstraints = NO; + _headerView.translatesAutoresizingMaskIntoConstraints = NO; [_constraints addObjectsFromArray:@[ - [NSLayoutConstraint constraintWithItem:_tableView + [NSLayoutConstraint constraintWithItem:_pageViewController.view attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.topLayoutGuide + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_pageViewController.view + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:_headerView + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:10.0], + [NSLayoutConstraint constraintWithItem:_pageViewController.view + attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_pageViewController.view + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:_pageViewController.view + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:65.0], + [NSLayoutConstraint constraintWithItem:_headerView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:140.0], + [NSLayoutConstraint constraintWithItem:_headerView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:_tableView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0], @@ -181,43 +264,43 @@ - (void)setUpConstraints { attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0.0], - [NSLayoutConstraint constraintWithItem:_pageViewController.view - attribute:NSLayoutAttributeWidth + [NSLayoutConstraint constraintWithItem:_headerView + attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view - attribute:NSLayoutAttributeWidth + attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0.0], - [NSLayoutConstraint constraintWithItem:_pageViewController.view - attribute:NSLayoutAttributeHeight + [NSLayoutConstraint constraintWithItem:_headerView + attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute + toItem:self.view + attribute:NSLayoutAttributeTrailing multiplier:1.0 - constant:60.0] + constant:0.0] ]]; [NSLayoutConstraint activateConstraints:_constraints]; } - (void)setSelectedDate:(NSDateComponents *)selectedDate { - NSDateComponents *today = [self today]; - _selectedDate = [selectedDate isLaterThan:today] ? today : selectedDate; + _selectedDate = selectedDate; - _weekViewController.symptomTrackerWeekView.selectedIndex = self.selectedDate.weekday - 1; + _weekViewController.weekView.isToday = [[self today] isEqualToDate:selectedDate]; + _weekViewController.weekView.selectedIndex = self.selectedDate.weekday - 1; [self fetchEvents]; } -- (void)setProgressRingTintColor:(UIColor *)progressRingTintColor { - _progressRingTintColor = progressRingTintColor; - if (!_progressRingTintColor) { - _progressRingTintColor = self.view.tintColor; +- (void)setGlyphTintColor:(UIColor *)glyphTintColor { + _glyphTintColor = glyphTintColor; + if (!_glyphTintColor) { + _glyphTintColor = [OCKGlyph defaultColorForGlyph:self.glyphType]; } - _weekViewController.symptomTrackerWeekView.tintColor = _progressRingTintColor; - _headerView.tintColor = _progressRingTintColor; - self.navigationItem.rightBarButtonItem.tintColor = _progressRingTintColor; + _weekViewController.weekView.tintColor = _glyphTintColor; + _headerView.tintColor = _glyphTintColor; + self.navigationItem.rightBarButtonItem.tintColor = _glyphTintColor; } - (void)setHeaderTitle:(NSString *)headerTitle { @@ -229,38 +312,32 @@ - (void)setHeaderTitle:(NSString *)headerTitle { } } -- (void)setShowEdgeIndicators:(BOOL)showEdgeIndicators { - _showEdgeIndicators = showEdgeIndicators; - [_tableView reloadData]; -} - #pragma mark - Helpers - (void)fetchEvents { - [_store eventsOnDate:_selectedDate - type:OCKCarePlanActivityTypeAssessment - completion:^(NSArray *> * _Nonnull eventsGroupedByActivity, NSError * _Nonnull error) { - NSAssert(!error, error.localizedDescription); - dispatch_async(dispatch_get_main_queue(), ^{ - if (_delegate && - [_delegate respondsToSelector:@selector(symptomTrackerViewController:willDisplayEvents:dateComponents:)]) { - [_delegate symptomTrackerViewController:self willDisplayEvents:[eventsGroupedByActivity copy] dateComponents:_selectedDate]; - } - - _events = [NSMutableArray new]; - - for (NSArray *events in eventsGroupedByActivity) { - for (OCKCarePlanEvent *event in events) { - [_events addObject:event]; + [self.store eventsOnDate:self.selectedDate + type:OCKCarePlanActivityTypeAssessment + completion:^(NSArray *> *eventsGroupedByActivity, NSError *error) { + NSAssert(!error, error.localizedDescription); + dispatch_async(dispatch_get_main_queue(), ^{ + _events = [NSMutableArray new]; + for (NSArray *events in eventsGroupedByActivity) { + [_events addObject:[events mutableCopy]]; } - } - - [self updateHeaderView]; - [self updateWeekView]; - [_tableView reloadData]; - }); - }]; + + if (self.delegate && + [self.delegate respondsToSelector:@selector(symptomTrackerViewController:willDisplayEvents:dateComponents:)]) { + [self.delegate symptomTrackerViewController:self willDisplayEvents:[_events copy] dateComponents:_selectedDate]; + } + + [self createGroupedEventDictionaryForEvents:_events]; + + [self updateHeaderView]; + [self updateWeekView]; + [_tableView reloadData]; + }); + }]; } - (void)updateHeaderView { @@ -268,20 +345,40 @@ - (void)updateHeaderView { dateStyle:NSDateFormatterLongStyle timeStyle:NSDateFormatterNoStyle]; - NSInteger totalEvents = _events.count; - NSInteger completedEvents = 0; - for (OCKCarePlanEvent *event in _events) { - if (event.state == OCKCarePlanEventStateCompleted) { - completedEvents++; - } - } + NSMutableArray *values = [NSMutableArray new]; - float progress = (totalEvents > 0) ? (float)completedEvents/totalEvents : 1; - _headerView.value = progress; + [self.store dailyCompletionStatusWithType:OCKCarePlanActivityTypeAssessment + startDate:self.selectedDate + endDate:self.selectedDate + handler:^(NSDateComponents *date, NSUInteger completedEvents, NSUInteger totalEvents) { + if (totalEvents == 0) { + [values addObject:@(1)]; + } else { + [values addObject:@((float)completedEvents/totalEvents)]; + } + } completion:^(BOOL completed, NSError *error) { + NSAssert(!error, error.localizedDescription); + dispatch_async(dispatch_get_main_queue(), ^{ + NSInteger selectedIndex = _weekViewController.weekView.selectedIndex; + [_weekValues replaceObjectAtIndex:selectedIndex withObject:values.firstObject]; + _weekViewController.weekView.values = _weekValues; + + _headerView.value = [values.firstObject doubleValue]; + }); + }]; +} + +- (UIImage *)createCustomImageName:(NSString*)customImageName { + UIImage *customImageToReturn; + if (customImageName != nil) { + NSBundle *bundle = [NSBundle mainBundle]; + customImageToReturn = [UIImage imageNamed: customImageName inBundle:bundle compatibleWithTraitCollection:nil]; + } else { + OCKGlyphType defaultGlyph = OCKGlyphTypeStethoscope; + customImageToReturn = [[OCKGlyph glyphImageForType:defaultGlyph] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + } - NSInteger selectedIndex = _weekViewController.symptomTrackerWeekView.selectedIndex; - [_weekValues replaceObjectAtIndex:selectedIndex withObject:@(progress)]; - _weekViewController.symptomTrackerWeekView.values = _weekValues; + return customImageToReturn; } - (void)updateWeekView { @@ -296,25 +393,22 @@ - (void)updateWeekView { NSMutableArray *values = [NSMutableArray new]; - [_store dailyCompletionStatusWithType:OCKCarePlanActivityTypeAssessment - startDate:[NSDateComponents ock_componentsWithDate:startOfWeek calendar:_calendar] - endDate:[NSDateComponents ock_componentsWithDate:endOfWeek calendar:_calendar] - handler:^(NSDateComponents * _Nonnull date, NSUInteger completedEvents, NSUInteger totalEvents) { - if ([date isLaterThan:[self today]]) { - [values addObject:@(0)]; - } else if (totalEvents == 0) { - [values addObject:@(1)]; - } else { - [values addObject:@((float)completedEvents/totalEvents)]; - } - } completion:^(BOOL completed, NSError * _Nullable error) { - NSAssert(!error, error.localizedDescription); - dispatch_async(dispatch_get_main_queue(), ^{ - _weekViewController.symptomTrackerWeekView.values = values; - _weekValues = [values mutableCopy]; - }); - }]; - + [self.store dailyCompletionStatusWithType:OCKCarePlanActivityTypeAssessment + startDate:[NSDateComponents ock_componentsWithDate:startOfWeek calendar:_calendar] + endDate:[NSDateComponents ock_componentsWithDate:endOfWeek calendar:_calendar] + handler:^(NSDateComponents *date, NSUInteger completedEvents, NSUInteger totalEvents) { + if (totalEvents == 0) { + [values addObject:@(1)]; + } else { + [values addObject:@((float)completedEvents/totalEvents)]; + } + } completion:^(BOOL completed, NSError *error) { + NSAssert(!error, error.localizedDescription); + dispatch_async(dispatch_get_main_queue(), ^{ + _weekViewController.weekView.values = values; + _weekValues = [values mutableCopy]; + }); + }]; } - (NSDateComponents *)dateFromSelectedIndex:(NSInteger)index { @@ -332,39 +426,101 @@ - (NSDateComponents *)today { return [NSDateComponents ock_componentsWithDate:[NSDate date] calendar:_calendar]; } +- (void)createGroupedEventDictionaryForEvents:(NSArray *> *)events { + NSMutableDictionary *groupedEvents = [NSMutableDictionary new]; + + for (NSArray *activityEvents in events) { + OCKCarePlanEvent *firstEvent = activityEvents.firstObject; + NSString *groupIdentifier = firstEvent.activity.groupIdentifier ? firstEvent.activity.groupIdentifier : _otherString; + + if (firstEvent.activity.optional) { + groupIdentifier = _optionalString; + } + + if (groupedEvents[groupIdentifier]) { + NSMutableArray *objects = [groupedEvents[groupIdentifier] mutableCopy]; + [objects addObject:activityEvents]; + groupedEvents[groupIdentifier] = objects; + } else { + NSMutableArray *objects = [[NSMutableArray alloc] initWithArray:activityEvents]; + groupedEvents[groupIdentifier] = @[objects]; + } + } + + NSMutableArray *sortedKeys = [[groupedEvents.allKeys sortedArrayUsingSelector:@selector(compare:)] mutableCopy]; + if ([sortedKeys containsObject:_otherString]) { + [sortedKeys removeObject:_otherString]; + [sortedKeys addObject:_otherString]; + } + + if ([sortedKeys containsObject:_optionalString]) { + [sortedKeys removeObject:_optionalString]; + [sortedKeys addObject:_optionalString]; + } + + _sectionTitles = [sortedKeys copy]; + + NSMutableArray *array = [NSMutableArray new]; + for (NSString *key in _sectionTitles) { + NSMutableArray *groupArray = [NSMutableArray new]; + NSArray *groupedEventsArray = groupedEvents[key]; + + NSMutableDictionary *activitiesDictionary = [NSMutableDictionary new]; + for (NSArray *events in groupedEventsArray) { + NSString *activityTitle = events.firstObject.activity.title; + activitiesDictionary[activityTitle] = events; + } + + NSArray *sortedActivitiesKeys = [activitiesDictionary.allKeys sortedArrayUsingSelector:@selector(compare:)]; + for (NSString *activityKey in sortedActivitiesKeys) { + [groupArray addObject:activitiesDictionary[activityKey]]; + } + + [array addObject:groupArray]; + } + + _tableViewData = [array mutableCopy]; +} + #pragma mark - OCKWeekViewDelegate - (void)weekViewSelectionDidChange:(UIView *)weekView { - OCKSymptomTrackerWeekView *progressCardWeekView = (OCKSymptomTrackerWeekView *)weekView; - NSDateComponents *selectedDate = [self dateFromSelectedIndex:progressCardWeekView.selectedIndex]; + OCKWeekView *currentWeekView = (OCKWeekView *)weekView; + NSDateComponents *selectedDate = [self dateFromSelectedIndex:currentWeekView.selectedIndex]; self.selectedDate = selectedDate; } -- (BOOL)weekViewCanSelectDayAtIndex:(NSUInteger)index { - NSDateComponents *today = [self today]; - NSDateComponents *selectedDate = [self dateFromSelectedIndex:index]; - return ![selectedDate isLaterThan:today]; -} - #pragma mark - OCKCarePlanStoreDelegate - (void)carePlanStore:(OCKCarePlanStore *)store didReceiveUpdateOfEvent:(OCKCarePlanEvent *)event { - for (int i = 0; i < _events.count; i++) { - OCKCarePlanEvent *eventInArray = _events[i]; - if ([eventInArray.activity isEqual:event.activity] && - (eventInArray.numberOfDaysSinceStart == event.numberOfDaysSinceStart) && - (eventInArray.occurrenceIndexOfDay == event.occurrenceIndexOfDay)) { - [_events replaceObjectAtIndex:i withObject:event]; - [self updateHeaderView]; + for (int i = 0; i < _tableViewData.count; i++) { + NSMutableArray *> *groupedEvents = _tableViewData[i]; + + for (int j = 0; j < groupedEvents.count; j++) { + NSMutableArray *events = groupedEvents[j]; + + if ([events.firstObject.activity.identifier isEqualToString:event.activity.identifier]) { + if (events[event.occurrenceIndexOfDay].numberOfDaysSinceStart == event.numberOfDaysSinceStart) { + [events replaceObjectAtIndex:event.occurrenceIndexOfDay withObject:event]; + _tableViewData[i][j] = events; + + [self updateHeaderView]; + + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:j inSection:i]; + OCKSymptomTrackerTableViewCell *cell = [_tableView cellForRowAtIndexPath:indexPath]; + cell.assessmentEvent = event; + } + break; + } - NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0]; - OCKSymptomTrackerTableViewCell *cell = [_tableView cellForRowAtIndexPath:indexPath]; - cell.assessmentEvent = event; - cell.showEdgeIndicator = cell.showEdgeIndicator; - break; } + + } + + if ([event.date isInSameWeekAsDate: self.selectedDate]) { + [self updateWeekView]; } } @@ -378,7 +534,7 @@ - (void)carePlanStoreActivityListDidChange:(OCKCarePlanStore *)store { - (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed { if (completed) { OCKWeekViewController *controller = (OCKWeekViewController *)pageViewController.viewControllers.firstObject; - controller.symptomTrackerWeekView.delegate = _weekViewController.symptomTrackerWeekView.delegate; + controller.weekView.delegate = _weekViewController.weekView.delegate; NSDateComponents *components = [NSDateComponents new]; components.day = (controller.weekIndex > _weekViewController.weekIndex) ? 7 : -7; @@ -393,28 +549,36 @@ - (void)pageViewController:(UIPageViewController *)pageViewController didFinishA #pragma mark - UIPageViewControllerDataSource - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController { - OCKWeekViewController *controller = [[OCKWeekViewController alloc] initWithShowCareCardWeekView:NO]; + OCKWeekViewController *controller = [OCKWeekViewController new]; controller.weekIndex = ((OCKWeekViewController *)viewController).weekIndex - 1; - controller.symptomTrackerWeekView.tintColor = self.progressRingTintColor; + controller.weekView.tintColor = self.glyphTintColor; + controller.weekView.isCareCard = NO; + controller.weekView.glyphType = self.glyphType; return controller; } - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController { - OCKWeekViewController *controller = [[OCKWeekViewController alloc] initWithShowCareCardWeekView:NO]; + OCKWeekViewController *controller = [OCKWeekViewController new]; controller.weekIndex = ((OCKWeekViewController *)viewController).weekIndex + 1; - controller.symptomTrackerWeekView.tintColor = self.progressRingTintColor; + controller.weekView.tintColor = self.glyphTintColor; + controller.weekView.isCareCard = NO; + controller.weekView.glyphType = self.glyphType; return (![self.selectedDate isInSameWeekAsDate:[self today]]) ? controller : nil; } #pragma mark - UITableViewDelegate -- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { - return _headerView; +- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { + NSString *sectionTitle = _sectionTitles[section]; + if ([sectionTitle isEqualToString:_otherString] && (_sectionTitles.count == 1 || (_sectionTitles.count == 2 && [_sectionTitles containsObject:_optionalString]))) { + sectionTitle = nil; + } + return sectionTitle; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - OCKCarePlanEvent *selectedEvent = _events[indexPath.row]; + OCKCarePlanEvent *selectedEvent = _tableViewData[indexPath.section][indexPath.row].firstObject; _lastSelectedAssessmentEvent = selectedEvent; if (_delegate && @@ -425,11 +589,20 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath [tableView deselectRowAtIndexPath:indexPath animated:NO]; } +- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath { + OCKCarePlanEvent *selectedEvent = _tableViewData[indexPath.section][indexPath.row].firstObject; + return !(selectedEvent.state == OCKCarePlanEventStateCompleted && !selectedEvent.activity.resultResettable); +} + #pragma mark - UITableViewDataSource +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return _tableViewData.count; +} + - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return _events.count; + return _tableViewData[section].count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { @@ -439,8 +612,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N cell = [[OCKSymptomTrackerTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; } - cell.assessmentEvent = _events[indexPath.row]; - cell.showEdgeIndicator = self.showEdgeIndicators; + cell.assessmentEvent = _tableViewData[indexPath.section][indexPath.row].firstObject; return cell; } diff --git a/CareKit/CareCard/OCKCareCardWeekView.h b/CareKit/SymptomTracker/OCKWeekView.h similarity index 88% rename from CareKit/CareCard/OCKCareCardWeekView.h rename to CareKit/SymptomTracker/OCKWeekView.h index 8d424d3f2..f6c517d87 100644 --- a/CareKit/CareCard/OCKCareCardWeekView.h +++ b/CareKit/SymptomTracker/OCKWeekView.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -36,15 +36,21 @@ NS_ASSUME_NONNULL_BEGIN @protocol OCKWeekViewDelegate; -@interface OCKCareCardWeekView : UIView +@interface OCKWeekView : UIView @property (nonatomic, weak, nullable) id delegate; @property (nonatomic, copy) NSArray *values; +@property (nonatomic) UIColor * ringTintColor; + @property (nonatomic) NSInteger selectedIndex; -@property (nonatomic) UIImage *smallMaskImage; +@property (nonatomic) BOOL isCareCard; + +@property (nonatomic) OCKGlyphType glyphType; + +@property (nonatomic) BOOL isToday; @end diff --git a/CareKit/SymptomTracker/OCKSymptomTrackerWeekView.m b/CareKit/SymptomTracker/OCKWeekView.m similarity index 88% rename from CareKit/SymptomTracker/OCKSymptomTrackerWeekView.m rename to CareKit/SymptomTracker/OCKWeekView.m index dd7fc6db0..c543fa4cf 100644 --- a/CareKit/SymptomTracker/OCKSymptomTrackerWeekView.m +++ b/CareKit/SymptomTracker/OCKWeekView.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -29,7 +29,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE */ -#import "OCKSymptomTrackerWeekView.h" +#import "OCKWeekView.h" #import "OCKWeekLabelsView.h" #import "OCKRingButton.h" #import "OCKRingView.h" @@ -43,26 +43,39 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE static const CGFloat LeadingMargin = 15.0; static const CGFloat TrailingMargin = 15.0; -@implementation OCKSymptomTrackerWeekView { +@implementation OCKWeekView { OCKWeekLabelsView *_weekView; NSMutableArray *_ringButtons; UIStackView *_stackView; NSMutableArray *_constraints; } -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - self.backgroundColor = [UIColor whiteColor]; - [self prepareView]; - } - return self; + +- (void)setGlyphType:(OCKGlyphType)glyphType{ + _glyphType = glyphType; + [self prepareView]; } - (void)prepareView { + + if (!UIAccessibilityIsReduceTransparencyEnabled()) { + self.backgroundColor = [UIColor groupTableViewBackgroundColor]; + + UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleProminent]; + UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect]; + blurEffectView.frame = self.bounds; + blurEffectView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [self addSubview:blurEffectView]; + } + else { + self.backgroundColor = [UIColor whiteColor]; + } + if (!_weekView) { _weekView = [[OCKWeekLabelsView alloc] initWithFrame:CGRectZero]; + _weekView.tintColor = self.ringTintColor; [self addSubview:_weekView]; + } if (!_ringButtons) { @@ -70,25 +83,29 @@ - (void)prepareView { for (int i = 0; i < 7; i++) { OCKRingButton *ringButton = [[OCKRingButton alloc] initWithFrame:CGRectMake(0, 0, RingButtonSize, RingButtonSize)]; - OCKRingView *ringView = [[OCKRingView alloc] initWithFrame:CGRectMake(0, 25, RingButtonSize + 10, RingButtonSize + 10)]; + OCKRingView *ringView = [[OCKRingView alloc] initWithFrame:CGRectMake(0, 25, RingButtonSize + 10, RingButtonSize + 10) + useSmallRing:YES]; + ringView.disableAnimation = NO; ringView.userInteractionEnabled = NO; - ringView.disableAnimation = YES; - ringView.hideLabel = YES; - ringButton.ringView = ringView; + ringView.tintColor = self.ringTintColor; + ringView.isCareCard = self.isCareCard; + ringView.glyphType = self.glyphType; + ringButton.ringView = ringView; [ringButton addTarget:self action:@selector(updateDayOfWeek:) forControlEvents:UIControlEventTouchDown]; UILabel *dayLabel = (UILabel *)_weekView.weekLabels[i]; ringButton.accessibilityLabel = [dayLabel accessibilityLabel]; + [_ringButtons addObject:ringButton]; } } if (!_stackView) { _stackView = [[UIStackView alloc] initWithArrangedSubviews:_ringButtons]; - _stackView.distribution = UIStackViewDistributionEqualSpacing; + _stackView.distribution = UIStackViewDistributionEqualCentering; [self addSubview:_stackView]; } @@ -167,21 +184,15 @@ - (void)setValues:(NSArray *)values { NSString *progressString = [OCKPercentFormatter(0, 0) stringFromNumber:[NSNumber numberWithFloat:value]]; _ringButtons[i].accessibilityValue = [NSString stringWithFormat:OCKLocalizedString(@"AX_WEEK_BUTTON_PROGRESS", nil), progressString]; - - if (self.delegate && - [self.delegate respondsToSelector:@selector(weekViewCanSelectDayAtIndex:)]) { - if (![self.delegate weekViewCanSelectDayAtIndex:(NSUInteger)i]) { - _ringButtons[i].accessibilityTraits |= UIAccessibilityTraitNotEnabled; - } - } } } - (void)setSelectedIndex:(NSInteger)selectedIndex { _selectedIndex = selectedIndex; + _weekView.isToday = self.isToday; [_weekView highlightDay:selectedIndex]; - for (int i = 0; i < [_ringButtons count]; i++ ) { + for (int i = 0; i < [_ringButtons count]; i++) { UIAccessibilityTraits axTraits = UIAccessibilityTraitButton | (i == selectedIndex ? UIAccessibilityTraitSelected : 0); [_ringButtons[i] setAccessibilityTraits:axTraits]; } @@ -204,12 +215,4 @@ - (void)layoutSubviews { [self setUpConstraints]; } -- (void)tintColorDidChange { - [super tintColorDidChange]; - for (OCKRingButton *button in _ringButtons) { - button.ringView.tintColor = self.tintColor; - } - _weekView.tintColor = self.tintColor; -} - @end diff --git a/CareKit/Utilities/OCKDefines_Private.h b/CareKit/Utilities/OCKDefines_Private.h index 53f2c2850..69aba200a 100644 --- a/CareKit/Utilities/OCKDefines_Private.h +++ b/CareKit/Utilities/OCKDefines_Private.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/$Template$.plist b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/$Template$.plist new file mode 100644 index 000000000..da091461c --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/$Template$.plist @@ -0,0 +1,211 @@ + + + + + Patient Info + + identifier + **This is a mandatory field** + name + **This is a mandatory field** + detailInfo + **This is a mandatory field** + monogram + **This is a mandatory field** Restrict this field to 2 characters. + image + *This is an optional field* Provide the name of the image that has been added to Media Files -> Media.xcassets + + Care Contents + + + type + **This is a mandatory field** Acceptable values - [ intervention, assessment, readOnly ] + identifier + **This is a mandatory field** This should be a unique value + groupIdentifier + *This is an optional field* + title + **This is a mandatory field** + text + **This is a mandatory field** + tintColor + 0 + instructions + *This is an optional field* + imageURL + *This is an optional field* Provide the name of the file with the extension which has been added to Media Files -> filesForImageURL + schedule + + activityStartDate + + mm + 0 + dd + 0 + yyyy + 0 + + activityEndDate + + mm + 0 + dd + 0 + yyyy + 0 + + occurencesOnEachDay + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + weeksToSkip + 0 + + thresholds + + + type + **This is a mandatory field** Acceptable values - [ adherence, greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual, equal, rangeInclusive, rangeExclusive ] + value + 0 + title + **This is a mandatory field** + upperValue + 0 + + + optional + + + + Insights + + message + + + title + **This is a mandatory field** + text + **This is a mandatory field** + tintColor + 0 + type + **This is a mandatory field** Acceptable values - [tip, alert, plain] + + + patientWidgets + + + title + **This is a mandatory field** + text + **This is a mandatory field** + tintColor + 0 + + + rings + + + title + *This is an optional field* + text + *This is an optional field* + tintColor + 0 + value + 0 + glyphType + *This is an optional field* Acceptable values - [heart, accessibility, activeLife, adultLearning, awareness, blood, bloodPressure, cardio, childLearning, dentalHealth, femaleHealth, hearing, homeCare, infantCare, laboratory, maleHealth, maternalHealth, medication, mentalHealth, neuro, nutrition, optometry, pediatrics, physicalTherapy, podiatry, respiratoryHealth, scale, stethoscope, syringe ] + + + charts + + numberOfCharts + 0 + numberOfSeries + 0 + numberOfSets + 0 + minimumScaleRange + 0 + maximumScaleRange + 0 + + + Contacts + + + type + **This is a mandatory field** acceptable values - [ careTeam, personal ] + name + **This is a mandatory field** + relation + **This is a mandatory field** + contactInfoItems + + phone + *This is an optional field* + sms + *This is an optional field* + email + *This is an optional field* + facetimeAudio + *This is an optional field* + facetimeVideo + *This is an optional field* + + tintColor + 0 + monogram + **This is a mandatory field** + image + *This is an optional field* Provide the name of the image that has been added to Media Files -> Media.xcassets + + + UI Customization + + careContentUI + + title + *This is an optional field* + image + *This is an optional field* Provide the name of the image that has been added to Media Files -> Media.xcassets + selectedImage + *This is an optional field* Provide the name of the image that has been added to Media Files -> Media.xcassets + glyphType + *This is an optional field* Acceptable values - [heart, accessibility, activeLife, adultLearning, awareness, blood, bloodPressure, cardio, childLearning, dentalHealth, femaleHealth, hearing, homeCare, infantCare, laboratory, maleHealth, maternalHealth, medication, mentalHealth, neuro, nutrition, optometry, pediatrics, physicalTherapy, podiatry, respiratoryHealth, scale, stethoscope, syringe ] + glyphTintColor + 0 + readOnlyHeader + *This is an optional field* + optionalHeader + *This is an optional field* + + insightsUI + + title + *This is an optional field* + image + *This is an optional field* Provide the name of the image that has been added to Media Files -> Media.xcassets + selectedImage + *This is an optional field* Provide the name of the image that has been added to Media Files -> Media.xcassets + + connectUI + + title + *This is an optional field* + image + *This is an optional field* Provide the name of the image that has been added to Media Files -> Media.xcassets + selectedImage + *This is an optional field* Provide the name of the image that has been added to Media Files -> Media.xcassets + + + + diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/APIExtensions.swift b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/APIExtensions.swift new file mode 100644 index 000000000..868fdd553 --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/APIExtensions.swift @@ -0,0 +1,479 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +import Foundation +import CareKit + + +//MARK: OCKCareContentsViewController +extension OCKCareContentsViewController { + /** + A convenience initializer for creating an instance of OCKCareContentsViewController. + - Parameter store: Reference to the CarePlanStore accessible using CarePlanStoreHelper singleton. + - Parameter object: Represents the `careContentUI` section of the Plist file. + */ + convenience init(withCarePlanStore store: OCKCarePlanStore, andCustomizationObject object: [String : Any]?) { + self.init(carePlanStore: store) + self.title = object?[PlistConstants.Customization.Keys.title] as? String ?? String.generateTitle() + self.tabBarItem = UITabBarItem(title: self.title, + image: UIImage(named: object?[PlistConstants.Customization.Keys.image] as? String ?? ""), + selectedImage: UIImage(named: object?[PlistConstants.Customization.Keys.selectedImage] as? String ?? "")) + let glyphTypeString = (object?[PlistConstants.Customization.CareContentUI.glyphType] as? String ?? "").lowercased() + self.glyphType = OCKGlyphType.glyphType(forString: glyphTypeString) + self.glyphTintColor = UIColor(netHex: object?[PlistConstants.Customization.CareContentUI.glyphTintColor] as? Int ?? 0) + self.readOnlySectionHeader = object?[PlistConstants.Customization.CareContentUI.readOnlyHeader] as? String + self.optionalSectionHeader = object?[PlistConstants.Customization.CareContentUI.optionalHeader] as? String + } +} + +//MARK: OCKGlyphType +extension OCKGlyphType { + static let all = [ "heart", + "accessibility", + "activelife", + "adultlearning", + "awareness", + "blood", + "bloodpressure", + "cardio", + "childlearning", + "dentalhealth", + "femalehealth", + "hearing", + "homecare", + "infantcare", + "laboratory", + "malehealth", + "maternalhealth", + "medication", + "mentalhealth", + "neuro", + "nutrition", + "optometry", + "pediatrics", + "physicaltherapy", + "podiatry", + "respiratoryhealth", + "scale", + "stethoscope", + "syringe" + ] + + /** + A helper function to map a string to a OCKGlyphType + - Parameter forString: Represents the string value entered in the `glyphType` section of the CareContentsUI. + */ + static func glyphType(forString enteredString: String) -> OCKGlyphType { + return OCKGlyphType(rawValue: OCKGlyphType.all.index(of: enteredString) ?? 0)! + } +} + +//MARK: OCKCarePlanThresholdType +extension OCKCarePlanThresholdType { + static let all = [ "adherence", + "greaterthan", + "greaterthanorequal", + "lessthan", + "lessthanorequal", + "equal", + "rangeinclusive", + "rangeexclusive" + ] + + /** + A helper function to map a string to a OCKCarePlanThresholdType + - Parameter forString: Represents the string value entered in the `type` field of the `thresholds` section in CareContents. + */ + static func thresholdType(forString enteredString: String) -> OCKCarePlanThresholdType { + return OCKCarePlanThresholdType(rawValue: OCKCarePlanThresholdType.all.index(of: enteredString) ?? 0)! + } +} + +//MARK: OCKInsightsViewController +extension OCKInsightsViewController { + /** + A convenience initializer for creating an instance of OCKCareContentsViewController. + - Parameter store: Reference to the CarePlanStore accessible using CarePlanStoreHelper singleton. + - Parameter insightObject: Represents the `Insights` section of the Plist file. + - Parameter object: Represents the `insightsUI` section of the Plist file. + */ + convenience init(withCarePlanStore store: OCKCarePlanStore, insightObject: [String : Any]?, andCustomizationObject object: [String : String]?) { + var insightItemArray = [OCKInsightItem]() + var patientWidgetArray = [OCKPatientWidget]() + + if insightObject != nil { + // Parse the insightObject for `rings` section and then use it to create OCKRingItem + let ringItem = insightObject?[PlistConstants.Insights.rings] as? [Any] + for case let ring as [String : Any] in ringItem ?? [] { + insightItemArray.append(OCKRingItem(title: ring[PlistConstants.Insights.Rings.title] as? String, + text: ring[PlistConstants.Insights.Rings.text] as? String, + tintColor: UIColor(netHex: ring[PlistConstants.Insights.Rings.tintColor] as? Int ?? 0), + value: ring[PlistConstants.Insights.Rings.value] as! Double, + glyphType: OCKGlyphType.glyphType(forString: (ring[PlistConstants.Insights.Rings.glyphType] as? String ?? "").lowercased()), + glyphFilename: nil)) + } + // Parse the insightObject for `message` section and then use it to create OCKMessageItem + let messageItem = insightObject?[PlistConstants.Insights.message] as? [Any] + for case let objectMessage as [String : Any] in messageItem ?? [] { + insightItemArray.append(OCKMessageItem(withObject: objectMessage)) + } + // Parse the insightObject for `charts` section and then use it to create OCKBarChart + let chartItem = insightObject?[PlistConstants.Insights.charts] as? [String : Int] + for _ in 0.. 3 { + fatalError("We do not support more than 3 widgets, please remove extra widget elements from the `widgets` sections under `Insights` from the Plist file.") + } + var widgetCount = 0 + for case let widgets as [String : Any] in widgetItem ?? [] { + guard let widgetTitle = widgets[PlistConstants.Insights.Widgets.title] as? String, widgetTitle != "" else { + fatalError("No title was provided for the widget element in position - \(widgetCount) under the `patientWidgets` -> `Insights` section of the Plist file.") + } + guard let widgetText = widgets[PlistConstants.Insights.Widgets.text] as? String, widgetText != "" else { + fatalError("No text was provided for the widget element in position - \(widgetCount) under the `patientWidgets` -> `Insights` section of the Plist file.") + } + widgetCount += 1 + patientWidgetArray.append(OCKPatientWidget.defaultWidget(withTitle: widgetTitle, + text: widgetText, + tintColor: UIColor(netHex: widgets[PlistConstants.Insights.Widgets.tintColor] as? Int ?? 0))) + } + } + + var thresholdActivityIdentifiers = [String]() + // block till you get the necessary activity identifiers + let semaphore = DispatchSemaphore(value: 0) + store.activities { (_, activities, error) in + if let err = error { + NSLog(err.localizedDescription) + } + for activity in activities { + if activity.thresholds != nil { + thresholdActivityIdentifiers.append(activity.identifier) + } + } + semaphore.signal() + } + let _ = semaphore.wait(timeout: .distantFuture) + + self.init(insightItems: insightItemArray, + patientWidgets: patientWidgetArray, + thresholds: thresholdActivityIdentifiers, + store: store) + self.title = object?[PlistConstants.Customization.Keys.title] ?? String.generateTitle() + self.tabBarItem = UITabBarItem(title: self.title, + image: UIImage(named: object?[PlistConstants.Customization.Keys.image] ?? ""), + selectedImage: UIImage(named: object?[PlistConstants.Customization.Keys.selectedImage] ?? "")) + } +} + +//MARK: OCKConnectViewController +extension OCKConnectViewController { + /** + A convenience initializer for creating an instance of OCKConnectViewController. + - Parameter contacts: An array of OCKContact objects which will be displayed in the OCKConnectViewController. + - Parameter patient: OCKPatient object constructed using the `Patient Info` section of the Plist file. + - Parameter object: Represents the `connectUI` section of the Plist file. + */ + convenience init(contacts: [OCKContact]?, patient: OCKPatient?, andCustomizationObject object: [String : String]?) { + self.init(contacts: contacts, + patient: patient) + self.title = object?[PlistConstants.Customization.Keys.title] ?? String.generateTitle() + self.tabBarItem = UITabBarItem(title: self.title, + image: UIImage(named: object?[PlistConstants.Customization.Keys.image] ?? ""), + selectedImage: UIImage(named: object?[PlistConstants.Customization.Keys.selectedImage] ?? "")) + } +} + +//MARK: OCKBarChart +extension OCKBarChart { + /** + A convenience initializer for creating an instance of OCKBarChart. + - Parameter object: Represents the `charts` section under `Insights` section of the Plist file. + */ + convenience init(withObject object: [String : Int]?) { + var chartSeries = [OCKBarSeries]() + var axisTitles = [String]() + var axisSubtitles = [String]() + let numberOfDataPoints = Int(object?[PlistConstants.Insights.Charts.numberOfSets] ?? 0) + for _ in 0.. OCKMessageItemType { + return OCKMessageItemType(rawValue: OCKMessageItemType.all.index(of: enteredString) ?? 0)! + } +} + +//MARK: OCKPatient +extension OCKPatient { + /** + A convenience initializer for creating an instance of OCKPatient. + - Parameter patientInfo: Represents the `Patient Info` section of the Plist file. + - Parameter store: Reference to the CarePlanStore accessible using CarePlanStoreHelper singleton. + */ + convenience init(withObject patientInfo: [String : Any]?, andStore store: OCKCarePlanStore) { + guard let patientIdentifier = patientInfo?[PlistConstants.PatientInfo.Keys.identifier] as? String, patientIdentifier != "" else { + fatalError("No value for `identifier` was provided under the `Patient Info` section of the Plist file.") + } + guard let patientName = patientInfo?[PlistConstants.PatientInfo.Keys.name] as? String, patientName != "" else { + fatalError("No value for `name` was provided under the `Patient Info` section of the Plist file.") + } + guard let patientMonogram = patientInfo?[PlistConstants.PatientInfo.Keys.monogram] as? String, patientMonogram != "" else { + fatalError("No value for `monogram` was provided under the `Patient Info` section of the Plist file.") + } + self.init(identifier: patientIdentifier, + carePlanStore: store, + name: patientName, + detailInfo: patientInfo?[PlistConstants.PatientInfo.Keys.detailInfo] as? String, + careTeamContacts: nil, + tintColor: UIColor(netHex: patientInfo?[PlistConstants.PatientInfo.Keys.tintColor] as? Int ?? 0), + monogram: patientMonogram, + image: UIImage(named: patientInfo?[PlistConstants.PatientInfo.Keys.image] as? String ?? ""), + categories: nil, + userInfo: [String.generateTitle(): String.generateTitle()]) + } +} + +//MARK: OCKContact +extension OCKContact { + /** + A convenience initializer for creating an instance of OCKContact. + - Parameter object: Represents the `Contacts` section of the Plist file. + */ + convenience init(withObject object: [String : Any]?) { + var contactInfoItems = [OCKContactInfo]() + if let contactInfoObject = object?[PlistConstants.Contacts.contactInfoItems] as? [String : String] + { + if let email = contactInfoObject[PlistConstants.Contacts.ContactInfoItems.email], email != "" { + contactInfoItems.append(OCKContactInfo.email(email)) + } + if let phone = contactInfoObject[PlistConstants.Contacts.ContactInfoItems.phone], phone != "" { + contactInfoItems.append(OCKContactInfo.phone(phone)) + } + if let sms = contactInfoObject[PlistConstants.Contacts.ContactInfoItems.sms], sms != "" { + contactInfoItems.append(OCKContactInfo.sms(sms)) + } + if let facetimeAudio = contactInfoObject[PlistConstants.Contacts.ContactInfoItems.facetimeAudio], facetimeAudio != "" { + contactInfoItems.append(OCKContactInfo.facetimeAudio(facetimeAudio, display: facetimeAudio)) + } + if let facetimeVideo = contactInfoObject[PlistConstants.Contacts.ContactInfoItems.facetimeVideo], facetimeVideo != "" { + contactInfoItems.append(OCKContactInfo.facetimeVideo(facetimeVideo, display: facetimeVideo)) + } + } + guard let contactTypeString = object?[PlistConstants.Contacts.type] as? String, contactTypeString != "" else { + fatalError("No value for `type` was provided under the `Contacts` section of the Plist file.") + } + guard let contactName = object?[PlistConstants.Contacts.name] as? String, contactName != "" else { + fatalError("No value for `name` was provided under the `Contacts` section of the Plist file.") + } + guard let contactRelation = object?[PlistConstants.Contacts.relation] as? String, contactRelation != "" else { + fatalError("No value for `relation` was provided under the `Contacts` section of the Plist file.") + } + guard let contactMonogram = object?[PlistConstants.Contacts.monogram] as? String, contactMonogram != "" else { + fatalError("No value for `monogram` was provided under the `Contacts` section of the Plist file.") + } + self.init(contactType: OCKContactType(rawValue: contactTypeString == "personal" ? 1 : 0)!, + name: contactName, + relation: contactRelation, + contactInfoItems: contactInfoItems, + tintColor: UIColor(netHex: object?[PlistConstants.Contacts.tintColor] as? Int ?? 0), + monogram: contactMonogram, + image: UIImage(named: object?[PlistConstants.Contacts.image] as? String ?? "")) + } +} + +// MARK:- OCKCarePlanActivity +extension OCKCarePlanActivity { + /** + A convenience initializer for creating an instance of OCKCarePlanActivity. + - Parameter object: Represents the `Care Contents` section of the Plist file. + */ + convenience init(withObject object: [String : Any]?) { + var type = OCKCarePlanActivityType.intervention + guard let typeString = object?[PlistConstants.Activity.Keys.type] as? String, typeString != "" else { + fatalError("No value for `type` was provided under the `Care Contents` section of the Plist file.") + } + switch typeString.lowercased() { + case "assessment": + type = .assessment + case "intervention": + type = .intervention + case "readonly": + type = .readOnly + default: + fatalError("The entered value for `type` is \(typeString), this does NOT comform to the following allowed types - assessment, intervention, assessmentReadOnly and interventionReadOnly") + } + let imageString = object?[PlistConstants.Activity.Keys.imageURL] as? String + var url: URL? + if let image = imageString, image != "" { + let split = image.components(separatedBy: ".") + if split.count != 2 { + fatalError("Please provide the file type for the image mentioned in imageURL as well, ex- `HeartIcon.png`") + } + let imageName = split[0] + let imageExtn = split[1] + if let path = Bundle.main.path(forResource: imageName, ofType: imageExtn) { + url = URL(fileURLWithPath: path) + } else { + fatalError("The `imageURL` entered was - \(image), this file was not found. Please verify that the file exists under the 'fileForImageURL' folder.") + } + } + var thresholds = [OCKCarePlanThreshold]() + let tempThreshold = object?[PlistConstants.Activity.Keys.thresholds] as? [Any] + for case let object as [String : Any] in tempThreshold ?? [] { + let thresholdTypeString = object[PlistConstants.Activity.Thresholds.type] as? String ?? "" + thresholds.append(OCKCarePlanThreshold.numericThreshold(withValue: object[PlistConstants.Activity.Thresholds.value] as! NSNumber, + type: OCKCarePlanThresholdType.thresholdType(forString: thresholdTypeString.lowercased()), + upperValue: object[PlistConstants.Activity.Thresholds.upperValue] as? NSNumber, + title: object[PlistConstants.Activity.Thresholds.title] as? String)) + } + + let scheduleObject = object?[PlistConstants.Activity.Keys.schedule] as? [String : Any] + guard let objectStartDate = (scheduleObject?[PlistConstants.Activity.Schedule.startDate] as? [String: Int]) else { + fatalError("No value for `activityStartDate` in `schedule` was provided under the `Care Contents` section of the Plist file.") + } + let startDate = DateComponents(year: objectStartDate[PlistConstants.Activity.Schedule.StartDate.year]!, + month: objectStartDate[PlistConstants.Activity.Schedule.StartDate.month]!, + day: objectStartDate[PlistConstants.Activity.Schedule.StartDate.day]!) + if startDate.month! == 0 || startDate.day! == 0 || startDate.year! == 0 { + fatalError("No valid value for `activityStartDate` in `schedule` was provided under the `Care Contents` section of the Plist file.") + } + var endDate: DateComponents? + if let objectEndDate = (scheduleObject?[PlistConstants.Activity.Schedule.endDate] as? [String: Int]) { + endDate = DateComponents(year: objectEndDate[PlistConstants.Activity.Schedule.StartDate.year]!, + month: objectEndDate[PlistConstants.Activity.Schedule.StartDate.month]!, + day: objectEndDate[PlistConstants.Activity.Schedule.StartDate.day]!) + if endDate?.month! == 0 || endDate?.day! == 0 || endDate?.year! == 0 { + endDate = nil + } + } + let occurences = scheduleObject?[PlistConstants.Activity.Schedule.occurences] as! [NSNumber] + let weeksToSkip = scheduleObject?[PlistConstants.Activity.Schedule.weeksToSkip] as? UInt ?? 0 + + guard let activityIdentifier = object?[PlistConstants.Activity.Keys.identifier] as? String, activityIdentifier != "" else { + fatalError("No valid value for `identifier` was provided under the `Care Contents` section of the Plist file.") + } + guard let activityTitle = object?[PlistConstants.Activity.Keys.title] as? String, activityTitle != "" else { + fatalError("No valid value for `title` was provided under the `Care Contents` section of the Plist file.") + } + self.init(identifier: activityIdentifier, + groupIdentifier: object?[PlistConstants.Activity.Keys.groupIdentifier] as? String, + type: type, + title: activityTitle, + text: object?[PlistConstants.Activity.Keys.text] as? String, + tintColor: UIColor(netHex: object?[PlistConstants.Activity.Keys.tintColor] as? Int ?? 0), + instructions: object?[PlistConstants.Activity.Keys.instructions] as? String, + imageURL: url, + schedule: OCKCareSchedule.weeklySchedule(withStartDate: startDate, + occurrencesOnEachDay: occurences, + weeksToSkip: weeksToSkip, + endDate: endDate), + resultResettable: object?[PlistConstants.Activity.Keys.resettable] as? Bool ?? false, + userInfo: nil, + thresholds: [thresholds], + optional: type == .readOnly ? true : object?[PlistConstants.Activity.Keys.optional] as! Bool) + } +} + +//MARK: OCKDocument +extension OCKDocument { + /** + A convenience initializer for creating an instance of OCKDocument. + - Parameter object: Represents the `charts` section under `Insights` section of the Plist file. + */ + convenience init(withObject object: [String : Any]?) { + let subtitle = OCKDocumentElementSubtitle(subtitle: "First subtitle") + let paragraph = OCKDocumentElementParagraph(content: "Lorem ipsum dolor sit amet, vim primis noster sententiae ne, et albucius apeirian accusata mea, vim at dicunt laoreet. Eu probo omnes inimicus ius, duo at veritus alienum. Nostrud facilisi id pro. Putant oporteat id eos. Admodum antiopam mel in, at per everti quaeque. Lorem ipsum dolor sit amet, vim primis noster sententiae ne, et albucius apeirian accusata mea, vim at dicunt laoreet. Eu probo omnes inimicus ius, duo at veritus alienum. Nostrud facilisi id pro. Putant oporteat id eos. Admodum antiopam mel in, at per everti quaeque. Lorem ipsum dolor sit amet, vim primis noster sententiae ne, et albucius apeirian accusata mea, vim at dicunt laoreet. Eu probo omnes inimicus ius, duo at veritus alienum. Nostrud facilisi id pro. Putant oporteat id eos. Admodum antiopam mel in, at per everti quaeque.") + let image = OCKDocumentElementImage(image: UIImage(named: "HeartIcon.png")!) + let table = OCKDocumentElementTable(headers: [String.generateTitle(), String.generateTitle()], rows: [[String.generateTitle(), String.generateTitle()], [String.generateTitle(), String.generateTitle()]]) + let chartItem = object?[PlistConstants.Insights.charts] as? [String : Int] + if chartItem != nil { + let chart = OCKDocumentElementChart(chart: OCKBarChart(withObject: chartItem)) + self.init(title: "Sample Document Title", elements: [subtitle, paragraph, chart, table, image]) + } else { + self.init(title: "Sample Document Title", elements: [subtitle, paragraph, table, image]) + } + } +} + diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/AppDelegate.swift b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/AppDelegate.swift new file mode 100644 index 000000000..1800e1a9c --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/AppDelegate.swift @@ -0,0 +1,43 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +import UIKit + + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + var window: UIWindow? + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + window?.tintColor = .red + return true + } +} + diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Base.lproj/LaunchScreen.storyboard b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000..70eacdfb2 --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Base.lproj/Main.storyboard b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Base.lproj/Main.storyboard new file mode 100644 index 000000000..f948e5fc1 --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Base.lproj/Main.storyboard @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/CarePlanStoreManager.swift b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/CarePlanStoreManager.swift new file mode 100644 index 000000000..ea8963f14 --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/CarePlanStoreManager.swift @@ -0,0 +1,127 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +import CareKit + + +/** + @brief Singleton class which handles creation and initialization of CarePlanStore with activities parsed from the provided Plist file. + */ +class CarePlanStoreManager { + // shared instance of the singleton class + static let shared = CarePlanStoreManager() + + private var plistObject: PlistParser? + private var store: OCKCarePlanStore? + + /** + This function accepts the name of the Plist file, creates a CarePlanStore associated with the plist name, adds all the + activity objects parsed from the plist file into the CarePlanStore and returns a reference to the CarePlanStore. + - Parameter forPlist: Name of the plist file which needs to be parsed. + - returns: A reference to the CarePlanStore which was created for the corresponding plist name. + */ + func createAndSetupStore(forPlist plistName: String) -> OCKCarePlanStore? { + let fileManager = FileManager.default + guard let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).last else { + fatalError("We were unable to get a URL from document directory!") + } + let storeURL = documentDirectory.appendingPathComponent(plistName) + var willAddActivites = true + guard let bundleVersionFromPlist = Bundle.main.object(forInfoDictionaryKey: kCFBundleVersionKey as String) as? String, bundleVersionFromPlist != "" else { + fatalError("Unable to access the contents of Info.plist file.") + } + if let bundleVersionFromContainer = UserDefaults.standard.value(forKey: "bundleVersionInContainer") as? String { + if bundleVersionFromContainer != bundleVersionFromPlist { + // Remove the CarePlanStore during every build and run, this avoids displaying any stale data. + try? fileManager.removeItem(at: storeURL) + } else { + // Do not re-add activities while re-launching the app after force-kill. + willAddActivites = false + } + } + do { + try fileManager.createDirectory(at: storeURL, withIntermediateDirectories: true, attributes: nil) + } catch let error { + fatalError("Failed to create a directory at the specified URL due to - \(error.localizedDescription)") + } + UserDefaults.standard.set(bundleVersionFromPlist, forKey: "bundleVersionInContainer") + RandomNumberGeneratorHelper.shared.generateRandomDistribution() + store = OCKCarePlanStore(persistenceDirectoryURL: storeURL) + if willAddActivites { + plistObject = PlistParser(withPlist: plistName) + addActivitiesToStore() + } + return store + } + + /** + A private helper functions which add all the activities parsed from the plist file to the CarePlanStore. + */ + private func addActivitiesToStore() { + guard let activities = plistObject?.activities else { + fatalError("Failed to extract the `Care Contents` object from the plist file, please verify if the `Care Contents` field in populated correctly.") + } + var listOfUniqueIdentifiers = [String]() + var positionOfActivity = 0 + for case var object as [String : Any] in activities { + positionOfActivity += 1 + // This is to allow copy pasting of activities in the plist, each object needs a UUID. + guard let objectID = object[PlistConstants.Activity.Keys.identifier] as? String, objectID != "" else { + fatalError("Failed to extract the `indentifier` field in the plist file for the activity element in position - \(positionOfActivity), please verify that a valid value was entered.") + } + guard let objectTitle = object[PlistConstants.Activity.Keys.title] as? String, objectTitle != "" else { + fatalError("Failed to extract the `title` field in the plist file for the activity element in position - \(positionOfActivity), please verify that a valid value was entered.") + } + if listOfUniqueIdentifiers.contains(objectID) { + let randomID = String.generateTitle() + object[PlistConstants.Activity.Keys.identifier] = randomID + object[PlistConstants.Activity.Keys.title] = (objectTitle + "- \(RandomNumberGeneratorHelper.shared.getSequenceCount())") + } else { + listOfUniqueIdentifiers.append(objectID) + } + let activity = OCKCarePlanActivity(withObject: object) + store?.activity(forIdentifier: activity.identifier, completion: { [unowned self] (_, foundActivity, error) in + if let err = error { + NSLog(err.localizedDescription) + } else { + // Only add an activity if it doesn't already exist in the CarePlanStore. + if foundActivity == nil { + self.store?.add(activity, completion: { (_, error) in + if let err = error { + NSLog(err.localizedDescription) + } + }) + } + } + }) + } + } +} diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/DocumentsDisplayViewController.swift b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/DocumentsDisplayViewController.swift new file mode 100644 index 000000000..5a963eedb --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/DocumentsDisplayViewController.swift @@ -0,0 +1,60 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +import UIKit +import CareKit + + +/** + * A view controller which will display the OCKDocument in HTML format inside a web view, it also vends a `Done` and `Share` button. + */ +class DocumentsDisplayViewController: UIViewController { + @IBOutlet weak var htmlDisplay: UIWebView! + var documentObject: OCKDocument? + + override func viewDidLoad() { + super.viewDidLoad() + htmlDisplay.loadHTMLString(documentObject!.htmlContent, baseURL: nil) + } + + @IBAction func done(_ sender: UIBarButtonItem) { + self.dismiss(animated: true, completion: nil) + } + + @IBAction func share(_ sender: UIBarButtonItem) { + documentObject?.createPDFData(completion: { (data, _) in + let activityVC = UIActivityViewController(activityItems: [data], applicationActivities: nil) + DispatchQueue.main.async { + self.present(activityVC, animated: true, completion: nil) + } + }) + } +} diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/HeartIcon.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/HeartIcon.png new file mode 100644 index 000000000..f76308bbf Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/HeartIcon.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Info.plist b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Info.plist new file mode 100644 index 000000000..46cdbfc46 --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Info.plist @@ -0,0 +1,57 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + CareKit Prototyper Plist Name + Patient + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UIStatusBarTintParameters + + UINavigationBar + + Style + UIBarStyleDefault + Translucent + + + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Contents.json b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..ce9cc0668 --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,164 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x-1.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x-2.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-Homescreen-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x-2.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x-1.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x-1.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "24x24", + "idiom" : "watch", + "filename" : "Icon-Homescreen-24x24@2x.png", + "scale" : "2x", + "role" : "notificationCenter", + "subtype" : "38mm" + }, + { + "size" : "27.5x27.5", + "idiom" : "watch", + "filename" : "Icon-Homescreen-27.5x27.5@2x.png", + "scale" : "2x", + "role" : "notificationCenter", + "subtype" : "42mm" + }, + { + "size" : "29x29", + "idiom" : "watch", + "filename" : "Icon-Homescreen-29x29@2x.png", + "role" : "companionSettings", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "watch", + "filename" : "Icon-Homescreen-29x29@3x-1.png", + "role" : "companionSettings", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "watch", + "filename" : "Icon-Homescreen-40x40@2x.png", + "scale" : "2x", + "role" : "appLauncher", + "subtype" : "38mm" + }, + { + "size" : "86x86", + "idiom" : "watch", + "filename" : "Icon-Homescreen-86x86@2x.png", + "scale" : "2x", + "role" : "quickLook", + "subtype" : "38mm" + }, + { + "size" : "98x98", + "idiom" : "watch", + "filename" : "Icon-Homescreen-98x98@2x.png", + "scale" : "2x", + "role" : "quickLook", + "subtype" : "42mm" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 000000000..daa9bbff6 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-20x20@2x-1.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-20x20@2x-1.png new file mode 100644 index 000000000..1bb713d93 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-20x20@2x-1.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 000000000..1bb713d93 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 000000000..c0b60daf6 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 000000000..d9713f4ed Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-1.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-1.png new file mode 100644 index 000000000..4304be2dd Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-1.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-2.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-2.png new file mode 100644 index 000000000..4304be2dd Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-2.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 000000000..c6c7ac650 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-1.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-1.png new file mode 100644 index 000000000..2c7e44438 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-1.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-2.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-2.png new file mode 100644 index 000000000..2c7e44438 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-2.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 000000000..77acdb91b Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 000000000..f76308bbf Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 000000000..9be3e3b35 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 000000000..bd3131bbb Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 000000000..b813b5efc Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 000000000..386f05564 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-Homescreen-24x24@2x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-Homescreen-24x24@2x.png new file mode 100644 index 000000000..8a1260ed2 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-Homescreen-24x24@2x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-Homescreen-27.5x27.5@2x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-Homescreen-27.5x27.5@2x.png new file mode 100644 index 000000000..fdfd04226 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-Homescreen-27.5x27.5@2x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-Homescreen-29x29@2x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-Homescreen-29x29@2x.png new file mode 100644 index 000000000..b2b095770 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-Homescreen-29x29@2x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-Homescreen-29x29@3x-1.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-Homescreen-29x29@3x-1.png new file mode 100644 index 000000000..043aace3d Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-Homescreen-29x29@3x-1.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-Homescreen-29x29@3x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-Homescreen-29x29@3x.png new file mode 100644 index 000000000..043aace3d Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-Homescreen-29x29@3x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-Homescreen-40x40@2x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-Homescreen-40x40@2x.png new file mode 100644 index 000000000..f65312612 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-Homescreen-40x40@2x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-Homescreen-86x86@2x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-Homescreen-86x86@2x.png new file mode 100644 index 000000000..645134dcc Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-Homescreen-86x86@2x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-Homescreen-98x98@2x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-Homescreen-98x98@2x.png new file mode 100644 index 000000000..e0d2b55ca Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/AppIcon.appiconset/Icon-Homescreen-98x98@2x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Contents.json b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Contents.json new file mode 100644 index 000000000..da4a164c9 --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/Contents.json b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/Contents.json new file mode 100644 index 000000000..da4a164c9 --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/carecard-filled.imageset/CareCard-OFF@1x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/carecard-filled.imageset/CareCard-OFF@1x.png new file mode 100644 index 000000000..f2674a02b Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/carecard-filled.imageset/CareCard-OFF@1x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/carecard-filled.imageset/CareCard-OFF@2x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/carecard-filled.imageset/CareCard-OFF@2x.png new file mode 100644 index 000000000..6ed5c59b5 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/carecard-filled.imageset/CareCard-OFF@2x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/carecard-filled.imageset/CareCard-OFF@3x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/carecard-filled.imageset/CareCard-OFF@3x.png new file mode 100644 index 000000000..545683b52 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/carecard-filled.imageset/CareCard-OFF@3x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/carecard-filled.imageset/Contents.json b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/carecard-filled.imageset/Contents.json new file mode 100644 index 000000000..55ab21813 --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/carecard-filled.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "CareCard-OFF@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "CareCard-OFF@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "CareCard-OFF@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/carecard.imageset/CareCard-OFF@1x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/carecard.imageset/CareCard-OFF@1x.png new file mode 100644 index 000000000..f2674a02b Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/carecard.imageset/CareCard-OFF@1x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/carecard.imageset/CareCard-OFF@2x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/carecard.imageset/CareCard-OFF@2x.png new file mode 100644 index 000000000..6ed5c59b5 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/carecard.imageset/CareCard-OFF@2x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/carecard.imageset/CareCard-OFF@3x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/carecard.imageset/CareCard-OFF@3x.png new file mode 100644 index 000000000..545683b52 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/carecard.imageset/CareCard-OFF@3x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/carecard.imageset/Contents.json b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/carecard.imageset/Contents.json new file mode 100644 index 000000000..55ab21813 --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/carecard.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "CareCard-OFF@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "CareCard-OFF@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "CareCard-OFF@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/connect-filled.imageset/Connect-ON@2x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/connect-filled.imageset/Connect-ON@2x.png new file mode 100644 index 000000000..1222b4191 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/connect-filled.imageset/Connect-ON@2x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/connect-filled.imageset/Connect-ON@3x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/connect-filled.imageset/Connect-ON@3x.png new file mode 100644 index 000000000..553fc1ae7 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/connect-filled.imageset/Connect-ON@3x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/connect-filled.imageset/Contents.json b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/connect-filled.imageset/Contents.json new file mode 100644 index 000000000..31616a14b --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/connect-filled.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Connect-ON@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Connect-ON@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/connect.imageset/Connect-OFF@2x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/connect.imageset/Connect-OFF@2x.png new file mode 100644 index 000000000..c31719b9e Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/connect.imageset/Connect-OFF@2x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/connect.imageset/Connect-OFF@3x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/connect.imageset/Connect-OFF@3x.png new file mode 100644 index 000000000..95a8b8d3c Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/connect.imageset/Connect-OFF@3x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/connect.imageset/Contents.json b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/connect.imageset/Contents.json new file mode 100644 index 000000000..aabd5a676 --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/connect.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Connect-OFF@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Connect-OFF@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/insights-filled.imageset/Contents.json b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/insights-filled.imageset/Contents.json new file mode 100644 index 000000000..3646aec71 --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/insights-filled.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Insight-ON@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Insight-ON@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Insight-ON@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/insights-filled.imageset/Insight-ON@1x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/insights-filled.imageset/Insight-ON@1x.png new file mode 100644 index 000000000..3a432b38e Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/insights-filled.imageset/Insight-ON@1x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/insights-filled.imageset/Insight-ON@2x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/insights-filled.imageset/Insight-ON@2x.png new file mode 100644 index 000000000..bfeed2910 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/insights-filled.imageset/Insight-ON@2x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/insights-filled.imageset/Insight-ON@3x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/insights-filled.imageset/Insight-ON@3x.png new file mode 100644 index 000000000..0fe06de9a Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/insights-filled.imageset/Insight-ON@3x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/insights.imageset/Contents.json b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/insights.imageset/Contents.json new file mode 100644 index 000000000..8a4cae05e --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/insights.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Insight-OFF@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Insight-OFF@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Insight-OFF@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/insights.imageset/Insight-OFF@1x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/insights.imageset/Insight-OFF@1x.png new file mode 100644 index 000000000..95b2b11aa Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/insights.imageset/Insight-OFF@1x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/insights.imageset/Insight-OFF@2x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/insights.imageset/Insight-OFF@2x.png new file mode 100644 index 000000000..d4c9c439f Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/insights.imageset/Insight-OFF@2x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/insights.imageset/Insight-OFF@3x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/insights.imageset/Insight-OFF@3x.png new file mode 100644 index 000000000..39b08f46a Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/insights.imageset/Insight-OFF@3x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/symptoms-filled.imageset/Contents.json b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/symptoms-filled.imageset/Contents.json new file mode 100644 index 000000000..ddd4fb757 --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/symptoms-filled.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Symptom-ON@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Symptom-ON@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Symptom-ON@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/symptoms-filled.imageset/Symptom-ON@1x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/symptoms-filled.imageset/Symptom-ON@1x.png new file mode 100644 index 000000000..6692f45b0 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/symptoms-filled.imageset/Symptom-ON@1x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/symptoms-filled.imageset/Symptom-ON@2x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/symptoms-filled.imageset/Symptom-ON@2x.png new file mode 100644 index 000000000..52aa8d888 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/symptoms-filled.imageset/Symptom-ON@2x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/symptoms-filled.imageset/Symptom-ON@3x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/symptoms-filled.imageset/Symptom-ON@3x.png new file mode 100644 index 000000000..eb7bd4fdb Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/symptoms-filled.imageset/Symptom-ON@3x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/symptoms.imageset/Contents.json b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/symptoms.imageset/Contents.json new file mode 100644 index 000000000..de19f2f1b --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/symptoms.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Symptom-OFF@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Symptom-OFF@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Symptom-OFF@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/symptoms.imageset/Symptom-OFF@1x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/symptoms.imageset/Symptom-OFF@1x.png new file mode 100644 index 000000000..7785df5d8 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/symptoms.imageset/Symptom-OFF@1x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/symptoms.imageset/Symptom-OFF@2x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/symptoms.imageset/Symptom-OFF@2x.png new file mode 100644 index 000000000..b397f6943 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/symptoms.imageset/Symptom-OFF@2x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/symptoms.imageset/Symptom-OFF@3x.png b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/symptoms.imageset/Symptom-OFF@3x.png new file mode 100644 index 000000000..7816cc8c5 Binary files /dev/null and b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Media.xcassets/Tab Bar Item Images - Set 1/symptoms.imageset/Symptom-OFF@3x.png differ diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Patient.plist b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Patient.plist new file mode 100644 index 000000000..6528ff6e0 --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/Patient.plist @@ -0,0 +1,358 @@ + + + + + Patient Info + + identifier + john appleseed + name + John Appleseed + detailInfo + 70 M + monogram + JA + image + + + Care Contents + + + type + intervention + identifier + acetaminophen + groupIdentifier + Pain Medication + title + Acetaminophen + text + 325mg + tintColor + 49151 + instructions + take 1 tablet every 4-6 hours (not to exceed 4 tablets daily) + imageURL + + schedule + + activityStartDate + + mm + 6 + dd + 4 + yyyy + 2017 + + activityEndDate + + mm + 6 + dd + 12 + yyyy + 2019 + + occurencesOnEachDay + + 4 + 4 + 4 + 4 + 4 + 4 + 4 + + weeksToSkip + 0 + + optional + + + + type + assessment + identifier + pain + groupIdentifier + Care Plan Survey + title + Pain (lower back) + text + Scale 1-10 (10 being very high) + tintColor + 8388736 + instructions + + imageURL + + schedule + + activityStartDate + + mm + 6 + dd + 4 + yyyy + 2017 + + activityEndDate + + mm + 6 + dd + 12 + yyyy + 2019 + + occurencesOnEachDay + + 1 + 0 + 1 + 0 + 1 + 0 + 1 + + weeksToSkip + 0 + + thresholds + + + type + greaterThan + value + 7 + title + High Pain. Please avoid any intense physical activities for the next few days. + upperValue + 0 + + + optional + + + + type + readOnly + identifier + bandage + groupIdentifier + + title + Keep bandage dry + text + Do not change gauze + tintColor + 0 + instructions + If the bandage gets wet, it can cause irritation. + imageURL + + schedule + + activityStartDate + + mm + 6 + dd + 4 + yyyy + 2017 + + activityEndDate + + mm + 6 + dd + 12 + yyyy + 2019 + + occurencesOnEachDay + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + + weeksToSkip + 0 + + optional + + + + Insights + + message + + + title + Care Plan Update + text + Your care plan adherence was 83% last week. + tintColor + 0 + type + plain + + + patientWidgets + + + title + Pain + text + 8 of 10 + tintColor + 16711680 + + + title + Weight + text + 175 lbs + tintColor + 255 + + + rings + + charts + + numberOfCharts + 1 + numberOfSeries + 2 + numberOfSets + 7 + minimumScaleRange + 0 + maximumScaleRange + 100 + + + Contacts + + + type + careTeam + name + Dr. Maria Ruiz + relation + Physician + contactInfoItems + + phone + 800-555-5555 + sms + 888-555-5512 + email + mruiz@icloud.com + facetimeAudio + 888-555-5512 + facetimeVideo + 888-555-5512 + + tintColor + 0 + monogram + MR + image + + + + type + careTeam + name + Bill James, RN + relation + Nurse + contactInfoItems + + phone + 800-555-5555 + + tintColor + 0 + monogram + BJ + image + + + + type + personal + name + Tom Clark + relation + Friend + contactInfoItems + + phone + 888-555-5512 + sms + 888-555-5512 + email + tom.clark@icloud.com + facetimeAudio + 888-555-5512 + facetimeVideo + tom.clark@icloud.com + + tintColor + 0 + monogram + TC + image + + + + UI Customization + + careContentUI + + title + Care Contents + image + carecard + selectedImage + carecard-filled + glyphType + homeCare + glyphTintColor + 16716947 + readOnlyHeader + Recommendations + optionalHeader + Take as needed + + insightsUI + + title + Insights + image + insights + selectedImage + insights-filled + + connectUI + + title + Connect + image + connect + selectedImage + connect-filled + + + + diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/PlistConstants.swift b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/PlistConstants.swift new file mode 100644 index 000000000..17377d0e4 --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/PlistConstants.swift @@ -0,0 +1,163 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +import Foundation + + +struct PlistConstants { + struct Root { + static let patient = "Patient Info" + static let activities = "Care Contents" + static let insight = "Insights" + static let connect = "Contacts" + static let viewControllerCustomization = "UI Customization" + } + + struct Customization { + static let careContent = "careContentUI" + static let insights = "insightsUI" + static let connect = "connectUI" + + struct Keys { + static let title = "title" + static let image = "image" + static let selectedImage = "selectedImage" + } + struct CareContentUI { + static let glyphType = "glyphType" + static let glyphTintColor = "glyphTintColor" + static let readOnlyHeader = "readOnlyHeader" + static let optionalHeader = "optionalHeader" + } + } + + struct PatientInfo { + struct Keys { + static let identifier = "identifier" + static let name = "name" + static let detailInfo = "detailInfo" + static let tintColor = "tintColor" + static let monogram = "monogram" + static let image = "image" + } + } + + struct Activity { + struct Keys { + static let type = "type" + static let identifier = "identifier" + static let groupIdentifier = "groupIdentifier" + static let title = "title" + static let text = "text" + static let tintColor = "tintColor" + static let instructions = "instructions" + static let imageURL = "imageURL" + static let optional = "optional" + static let schedule = "schedule" + static let thresholds = "thresholds" + static let resettable = "resultResettable" + } + struct Schedule { + static let startDate = "activityStartDate" + static let endDate = "activityEndDate" + static let occurences = "occurencesOnEachDay" + static let weeksToSkip = "weeksToSkip" + struct StartDate { + static let month = "mm" + static let day = "dd" + static let year = "yyyy" + } + struct EndDate { + static let month = "mm" + static let day = "dd" + static let year = "yyyy" + } + } + struct Thresholds { + static let value = "value" + static let type = "type" + static let upperValue = "upperValue" + static let title = "title" + } + } + + struct Insights { + static let message = "message" + static let widgets = "patientWidgets" + static let charts = "charts" + static let rings = "rings" + struct Message { + static let title = "title" + static let text = "text" + static let tintColor = "tintColor" + static let type = "type" + } + struct Widgets { + static let title = "title" + static let text = "text" + static let tintColor = "tintColor" + } + struct Charts { + static let numberOfCharts = "numberOfCharts" + static let numberOfSeries = "numberOfSeries" + static let numberOfSets = "numberOfSets" + static let minimumScaleRange = "miminumScaleRange" + static let maximumScaleRange = "maximumScaleRange" + } + struct Rings { + static let title = "title" + static let text = "text" + static let tintColor = "tintColor" + static let value = "value" + static let glyphType = "glyphType" + } + } + + struct Contacts { + static let type = "type" + static let name = "name" + static let relation = "relation" + static let contactInfoItems = "contactInfoItems" + static let tintColor = "tintColor" + static let monogram = "monogram" + static let image = "image" + struct ContactInfoItems { + static let phone = "phone" + static let email = "email" + static let sms = "sms" + static let facetimeAudio = "facetimeAudio" + static let facetimeVideo = "facetimeVideo" + } + } + + +} + diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/PlistParser.swift b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/PlistParser.swift new file mode 100644 index 000000000..238a8a883 --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/PlistParser.swift @@ -0,0 +1,70 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +import Foundation + + +/** + Create an object of this class by passing in the name of the plist file which needs to be parsed to the initializer. The public properties of this class + represent different sections of the Plist file which correspond to the key View Controllers provided by CareKit. + */ +class PlistParser { + + var activities: [Any]? + var contacts: [Any]? + var insights: [String : Any]? + var patient: [String : Any]? + + // these objects define UI properties like title, image, etc for the View Controllers. + var careContentUI: [String : Any]? + var insightsUI: [String : String]? + var connectUI: [String : String]? + + init(withPlist plist: String) { + if let path = Bundle.main.path(forResource: plist, ofType: "plist"), let plistObject = NSDictionary(contentsOfFile: path) as? [String : Any] { + + patient = plistObject[PlistConstants.Root.patient] as? [String : Any] + activities = plistObject[PlistConstants.Root.activities] as? [Any] + insights = plistObject[PlistConstants.Root.insight] as? [String : Any] + contacts = plistObject[PlistConstants.Root.connect] as? [Any] + + guard let tabBarItemInfo = plistObject[PlistConstants.Root.viewControllerCustomization] as? [String : Any] else { + NSLog("UI Customization field in the Plist file was not found.") + return + } + careContentUI = tabBarItemInfo[PlistConstants.Customization.careContent] as? [String : Any] + insightsUI = tabBarItemInfo[PlistConstants.Customization.insights] as? [String : String] + connectUI = tabBarItemInfo[PlistConstants.Customization.connect] as? [String : String] + } else { + fatalError("Failed to locate the Plist file, please verify the name of plist file in Info.plist.") + } + } +} diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/RandomNumberGeneratorHelper.swift b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/RandomNumberGeneratorHelper.swift new file mode 100644 index 000000000..32f2509d4 --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/RandomNumberGeneratorHelper.swift @@ -0,0 +1,57 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +import Foundation +import GameKit + + +class RandomNumberGeneratorHelper { + static let shared = RandomNumberGeneratorHelper() + private var randomDistribution: GKRandomDistribution? + private var count = 0 + let minValue = 0 + let maxValue = 100 + + func generateRandomDistribution() { + let randomSource = GKMersenneTwisterRandomSource() + if let bundleVersionFromContainer = UserDefaults.standard.value(forKey: "bundleVersionInContainer") as? String { + randomSource.seed = UInt64(bundleVersionFromContainer)! + } + randomDistribution = GKRandomDistribution(randomSource: randomSource, lowestValue: minValue, highestValue: maxValue) + } + func getRandomValue() -> Int { + return randomDistribution?.nextInt() ?? 0 + } + func getSequenceCount() -> Int { + count = count + 1 + return count + } +} diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/StringHelper.swift b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/StringHelper.swift new file mode 100644 index 000000000..d0b86c1c7 --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/StringHelper.swift @@ -0,0 +1,39 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +import Foundation + + +extension String { + static func generateTitle() -> String { + return "Title-\(RandomNumberGeneratorHelper.shared.getSequenceCount())" + } +} diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/TabBarViewController.swift b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/TabBarViewController.swift new file mode 100644 index 000000000..4c3f34117 --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/TabBarViewController.swift @@ -0,0 +1,195 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +import UIKit +import CareKit + + +class TabBarViewController: UITabBarController { + + fileprivate var storeReference: OCKCarePlanStore? + fileprivate var plistObjectReference: PlistParser? + + /** + Dictionary which maps a OCKContact to the messages exchanged between the contact and the user. + */ + fileprivate var messages: [String: [OCKConnectMessageItem]] = [:] + fileprivate let dateFormatter = DateFormatter() + fileprivate var initialTimeStamp: String? + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + + // Extracting the name of the Plist file which needs to be parsed from Info.plist + guard let plistName = Bundle.main.infoDictionary?["CareKit Prototyper Plist Name"] as? String, plistName != "" else { + fatalError("Could not get the name of the Plist file, please verify the value of `CareKit Prototyper Plist Name` in the Info.plist file.") + } + + guard let store = CarePlanStoreManager.shared.createAndSetupStore(forPlist: plistName) else { + fatalError("failed to create a Care Plan Store!") + } + storeReference = store + storeReference?.delegate = self + let plistObject = PlistParser(withPlist: plistName) + plistObjectReference = plistObject + + dateFormatter.dateFormat = "MM-dd-yyyy HH:mm:ss" + initialTimeStamp = dateFormatter.string(from: Date()) + + // Initialize CareContents View Controller with the corresponding careContentUI block from plistObject + let careContentsVC = OCKCareContentsViewController(withCarePlanStore: store, + andCustomizationObject: plistObject.careContentUI) + careContentsVC.delegate = self + self.viewControllers = [(UINavigationController(rootViewController: careContentsVC))] + + // Initialize Insights View Controller only if the corresponding insights object was retrieved from the plist + if plistObject.insights != nil { + let insightsVC = OCKInsightsViewController(withCarePlanStore: store, + insightObject: plistObject.insights, + andCustomizationObject: plistObject.insightsUI) + self.viewControllers?.append(UINavigationController(rootViewController: insightsVC)) + } + + // Initialize Contacts View Controller only if the corresponding contacts object was retrieved from the plist + if let contactsObject = plistObject.contacts { + var contacts = [OCKContact]() + for object in contactsObject { + contacts.append(OCKContact(withObject: object as? [String : Any])) + } + let patient = plistObject.patient == nil ? nil : OCKPatient(withObject: plistObject.patient, + andStore: store) + let contactsVC = OCKConnectViewController(contacts: contacts.count == 0 ? nil : contacts, + patient: patient, + andCustomizationObject: plistObject.connectUI) + contactsVC.dataSource = self + contactsVC.delegate = self + self.viewControllers?.append(UINavigationController(rootViewController: contactsVC)) + } + } +} + +//MARK: OCKCareContentsViewControllerDelegate +extension TabBarViewController: OCKCareContentsViewControllerDelegate { + func careContentsViewController(_ viewController: OCKCareContentsViewController, didSelectRowWithAssessmentEvent assessmentEvent: OCKCarePlanEvent) { + let alert = UIAlertController(title: "Enter a value", message: assessmentEvent.activity.text, preferredStyle: .alert) + alert.addTextField { textField in + textField.keyboardType = .decimalPad + } + let doneAction = UIAlertAction(title: "Done", style: .default) { [unowned self] _ in + let valueOfTextField = alert.textFields![0] + if let enteredValue = valueOfTextField.text, enteredValue != "" { + let result = OCKCarePlanEventResult(valueString: enteredValue, unitString: "", userInfo: nil, values: [NSNumber(value: Double(enteredValue)!)]) + self.storeReference?.update(assessmentEvent, with: result, state: .completed) { (_, _, error) in + if let error = error { + NSLog(error.localizedDescription) + } + } + } + } + alert.addAction(doneAction) + let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) + alert.addAction(cancelAction) + DispatchQueue.main.async { + self.present(alert, animated: true, completion: nil) + } + } +} + +//MARK: OCKConnectViewControllerDataSource +extension TabBarViewController: OCKConnectViewControllerDataSource { + func connectViewControllerCareTeamConnections(_ viewController: OCKConnectViewController) -> [OCKContact] { + guard let plistObject = plistObjectReference, let contactsObject = plistObject.contacts else { + fatalError("could not access the contacts object, maybe no Contacts information was provided in the plist?") + } + var contacts = [OCKContact]() + for object in contactsObject { + contacts.append(OCKContact(withObject: object as? [String : Any])) + } + return contacts + } + + func connectViewController(_ viewController: OCKConnectViewController, connectMessageItemAt index: Int, careTeamContact contact: OCKContact) -> OCKConnectMessageItem { + if index == 0 { + return OCKConnectMessageItem(messageType: .received, name: contact.name, message: "Hello", dateString: initialTimeStamp!) + } else { + guard let arrayOfMessages = messages[contact.name] else { + fatalError("could not extract message conversation for the contact - \(contact.name)") + } + return arrayOfMessages[index-1] + } + } + + func connectViewControllerNumber(ofConnectMessageItems viewController: OCKConnectViewController, careTeamContact contact: OCKContact) -> Int { + return ((((messages[contact.name])?.count) ?? 0) + 1) + } +} + + +//MARK: OCKConnectViewControllerDelegate +extension TabBarViewController: OCKConnectViewControllerDelegate { + + func connectViewController(_ connectViewController: OCKConnectViewController, didSelectShareButtonFor contact: OCKContact, presentationSourceView sourceView: UIView?) { + let document = OCKDocument(withObject: plistObjectReference?.insights) + document.pageHeader = "Intended recepient of this report - \(contact.name)" + let documentDisplayVC = self.storyboard?.instantiateViewController(withIdentifier: "documentsDisplay") as? DocumentsDisplayViewController + documentDisplayVC?.documentObject = document + self.present(documentDisplayVC!, animated: true, completion: nil) + } + + func connectViewController(_ connectViewController: OCKConnectViewController, titleForSharingCellFor contact: OCKContact) -> String? { + return "Display Generated Insights Report" + } + + func connectViewController(_ viewController: OCKConnectViewController, didSendConnectMessage message: String, careTeamContact contact: OCKContact) { + let dateTimeString = dateFormatter.string(from: Date()) + if messages[contact.name] == nil { + messages[contact.name] = [OCKConnectMessageItem(messageType: .sent, name: "Me", message: message, dateString: dateTimeString)] + } else { + var allMessages = messages[contact.name] ?? [] + allMessages.append(OCKConnectMessageItem(messageType: .sent, name: "Me", message: message, dateString: dateTimeString)) + messages[contact.name] = allMessages + } + } +} + +//MARK: OCKCarePlanStoreDelegate +extension TabBarViewController: OCKCarePlanStoreDelegate { + func carePlanStore(_ store: OCKCarePlanStore, didReceiveUpdateOf event: OCKCarePlanEvent) { + if event.activity.type == .assessment { + let triggeredThresholds = event.evaluateNumericThresholds() + for thresholdArray in triggeredThresholds { + for threshold in thresholdArray { + NSLog("Threshold triggered on event \(event.occurrenceIndexOfDay) of \(event.date) for activity \(event.activity.identifier) with title:\n\(threshold.title!)") + } + } + } + } +} diff --git a/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/UIColorHelper.swift b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/UIColorHelper.swift new file mode 100644 index 000000000..70f9e0837 --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/UIColorHelper.swift @@ -0,0 +1,60 @@ +/* + Copyright (c) 2017, Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder(s) nor the names of any contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. No license is granted to the trademarks of + the copyright holders even if such marks are included in this software. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +import UIKit + + +extension UIColor { + convenience init(red: Int, green: Int, blue: Int) { + assert(red >= 0 && red <= 255, "Invalid red component") + assert(green >= 0 && green <= 255, "Invalid green component") + assert(blue >= 0 && blue <= 255, "Invalid blue component") + self.init(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0, alpha: 1.0) + } + convenience init(netHex:Int) { + self.init(red:(netHex >> 16) & 0xff, green:(netHex >> 8) & 0xff, blue:netHex & 0xff) + } +} + +extension UIColor { + static func generateRandom() -> UIColor { + return UIColor(red: .generateRandom(), + green: .generateRandom(), + blue: .generateRandom(), + alpha: 1.0) + } +} + +extension CGFloat { + static func generateRandom() -> CGFloat { + return CGFloat(RandomNumberGeneratorHelper.shared.getRandomValue()) / CGFloat(RandomNumberGeneratorHelper.shared.maxValue) + } +} diff --git a/CareKitPrototypingTool/OCKPrototyper/OCKPrototyper.xcodeproj/project.pbxproj b/CareKitPrototypingTool/OCKPrototyper/OCKPrototyper.xcodeproj/project.pbxproj new file mode 100644 index 000000000..776d950fb --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/OCKPrototyper.xcodeproj/project.pbxproj @@ -0,0 +1,542 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 710F792A1EAE9A2B00C3466A /* DocumentsDisplayViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 710F79291EAE9A2B00C3466A /* DocumentsDisplayViewController.swift */; }; + 712952731E806556008E99A8 /* StringHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 712952721E806556008E99A8 /* StringHelper.swift */; }; + 712CC0181E82EE2E004411A9 /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 712CC0171E82EE2E004411A9 /* Media.xcassets */; }; + 714334751ED7AB390098C35F /* HeartIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 714334741ED7AB390098C35F /* HeartIcon.png */; }; + 717A25AF1E73401E00D2A2D3 /* Patient.plist in Resources */ = {isa = PBXBuildFile; fileRef = 717A25AE1E73401E00D2A2D3 /* Patient.plist */; }; + 717F70C41E77C23C00828B2E /* PlistParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 717F70C31E77C23C00828B2E /* PlistParser.swift */; }; + 7188E53A1EE47BE1006C4114 /* CareKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7188E5361EE47BAA006C4114 /* CareKit.framework */; }; + 7188E53B1EE47BE1006C4114 /* CareKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 7188E5361EE47BAA006C4114 /* CareKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 7188E53F1EE483F8006C4114 /* $Template$.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7188E53E1EE483F8006C4114 /* $Template$.plist */; }; + 7194C21C1E7BC7B600E02526 /* UIColorHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7194C21B1E7BC7B600E02526 /* UIColorHelper.swift */; }; + 71992A631EB3F82E0088DC70 /* RandomNumberGeneratorHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71992A621EB3F82E0088DC70 /* RandomNumberGeneratorHelper.swift */; }; + 71ABB45C1E7222B6000BAEFE /* TabBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71ABB45B1E7222B6000BAEFE /* TabBarViewController.swift */; }; + 71ABF1031E6FBBB900B1E410 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71ABF1021E6FBBB900B1E410 /* AppDelegate.swift */; }; + 71ABF10A1E6FBBB900B1E410 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71ABF1081E6FBBB900B1E410 /* Main.storyboard */; }; + 71ABF10F1E6FBBB900B1E410 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71ABF10D1E6FBBB900B1E410 /* LaunchScreen.storyboard */; }; + 71B063CC1E7B7F7E002BEF45 /* CarePlanStoreManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71B063CB1E7B7F7E002BEF45 /* CarePlanStoreManager.swift */; }; + 71D173211E93074900519E9A /* APIExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71D173201E93074900519E9A /* APIExtensions.swift */; }; + 71D173241E9334D700519E9A /* PlistConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71D173231E9334D700519E9A /* PlistConstants.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 7188E5351EE47BAA006C4114 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7188E52F1EE47BAA006C4114 /* CareKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8605A5BA1C4F04EC00DD65FF; + remoteInfo = CareKit; + }; + 7188E5371EE47BAA006C4114 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7188E52F1EE47BAA006C4114 /* CareKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8605A5C41C4F04EC00DD65FF; + remoteInfo = CareKitTests; + }; + 7188E53C1EE47BE1006C4114 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7188E52F1EE47BAA006C4114 /* CareKit.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 8605A5B91C4F04EC00DD65FF; + remoteInfo = CareKit; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 7134B57F1E847E05003A90C7 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 7188E53B1EE47BE1006C4114 /* CareKit.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 710F79291EAE9A2B00C3466A /* DocumentsDisplayViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocumentsDisplayViewController.swift; sourceTree = ""; }; + 712952721E806556008E99A8 /* StringHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringHelper.swift; sourceTree = ""; }; + 712CC0171E82EE2E004411A9 /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = ""; }; + 714334741ED7AB390098C35F /* HeartIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = HeartIcon.png; sourceTree = ""; }; + 717A25AE1E73401E00D2A2D3 /* Patient.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Patient.plist; sourceTree = ""; }; + 717F70C31E77C23C00828B2E /* PlistParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlistParser.swift; sourceTree = ""; }; + 7188E52F1EE47BAA006C4114 /* CareKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CareKit.xcodeproj; path = ../../../CareKit.xcodeproj; sourceTree = ""; }; + 7188E53E1EE483F8006C4114 /* $Template$.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = $Template$.plist; sourceTree = ""; }; + 7194C21B1E7BC7B600E02526 /* UIColorHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIColorHelper.swift; sourceTree = ""; }; + 71992A621EB3F82E0088DC70 /* RandomNumberGeneratorHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomNumberGeneratorHelper.swift; sourceTree = ""; }; + 71ABB45B1E7222B6000BAEFE /* TabBarViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabBarViewController.swift; sourceTree = ""; }; + 71ABF0FF1E6FBBB900B1E410 /* CareKitPatient.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CareKitPatient.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 71ABF1021E6FBBB900B1E410 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 71ABF1091E6FBBB900B1E410 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 71ABF10E1E6FBBB900B1E410 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 71ABF1101E6FBBB900B1E410 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 71B063CB1E7B7F7E002BEF45 /* CarePlanStoreManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CarePlanStoreManager.swift; sourceTree = ""; }; + 71D173201E93074900519E9A /* APIExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = APIExtensions.swift; sourceTree = ""; }; + 71D173231E9334D700519E9A /* PlistConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlistConstants.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 71ABF0FC1E6FBBB900B1E410 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 7188E53A1EE47BE1006C4114 /* CareKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 710842CF1E9AB8A400CA4CC9 /* filesForImageURL */ = { + isa = PBXGroup; + children = ( + 714334741ED7AB390098C35F /* HeartIcon.png */, + ); + name = filesForImageURL; + sourceTree = ""; + }; + 7149F2081ED3B4C800E00146 /* Other files */ = { + isa = PBXGroup; + children = ( + 71ABF1011E6FBBB900B1E410 /* CareKitPatient */, + 7194C2171E7BC56800E02526 /* Helper files */, + 71ABF12B1E6FBE0F00B1E410 /* Linked frameworks */, + 71ABF1001E6FBBB900B1E410 /* Products */, + ); + name = "Other files"; + sourceTree = ""; + }; + 7152CCB71E6FD87D00643F89 /* Other */ = { + isa = PBXGroup; + children = ( + 71ABF1021E6FBBB900B1E410 /* AppDelegate.swift */, + 710F79291EAE9A2B00C3466A /* DocumentsDisplayViewController.swift */, + 71ABF10D1E6FBBB900B1E410 /* LaunchScreen.storyboard */, + 71ABF1081E6FBBB900B1E410 /* Main.storyboard */, + 71ABF1101E6FBBB900B1E410 /* Info.plist */, + ); + name = Other; + sourceTree = ""; + }; + 7188E5301EE47BAA006C4114 /* Products */ = { + isa = PBXGroup; + children = ( + 7188E5361EE47BAA006C4114 /* CareKit.framework */, + 7188E5381EE47BAA006C4114 /* CareKitTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 7194C2171E7BC56800E02526 /* Helper files */ = { + isa = PBXGroup; + children = ( + 717F70C31E77C23C00828B2E /* PlistParser.swift */, + 71B063CB1E7B7F7E002BEF45 /* CarePlanStoreManager.swift */, + 71D173201E93074900519E9A /* APIExtensions.swift */, + 7194C21B1E7BC7B600E02526 /* UIColorHelper.swift */, + 712952721E806556008E99A8 /* StringHelper.swift */, + 71D173231E9334D700519E9A /* PlistConstants.swift */, + 71992A621EB3F82E0088DC70 /* RandomNumberGeneratorHelper.swift */, + ); + name = "Helper files"; + path = CareKitPatient; + sourceTree = ""; + }; + 71ABF0F61E6FBBB900B1E410 = { + isa = PBXGroup; + children = ( + 71C58B841EBD3F1D00768CA0 /* Plist files */, + 7149F2081ED3B4C800E00146 /* Other files */, + ); + sourceTree = ""; + }; + 71ABF1001E6FBBB900B1E410 /* Products */ = { + isa = PBXGroup; + children = ( + 71ABF0FF1E6FBBB900B1E410 /* CareKitPatient.app */, + ); + name = Products; + sourceTree = ""; + }; + 71ABF1011E6FBBB900B1E410 /* CareKitPatient */ = { + isa = PBXGroup; + children = ( + 71ABB45B1E7222B6000BAEFE /* TabBarViewController.swift */, + 71C58B811EBD3A1400768CA0 /* Media Files */, + 7152CCB71E6FD87D00643F89 /* Other */, + ); + path = CareKitPatient; + sourceTree = ""; + }; + 71ABF12B1E6FBE0F00B1E410 /* Linked frameworks */ = { + isa = PBXGroup; + children = ( + 7188E52F1EE47BAA006C4114 /* CareKit.xcodeproj */, + ); + name = "Linked frameworks"; + path = CareKitPatient; + sourceTree = ""; + }; + 71C58B811EBD3A1400768CA0 /* Media Files */ = { + isa = PBXGroup; + children = ( + 712CC0171E82EE2E004411A9 /* Media.xcassets */, + 710842CF1E9AB8A400CA4CC9 /* filesForImageURL */, + ); + name = "Media Files"; + sourceTree = ""; + }; + 71C58B841EBD3F1D00768CA0 /* Plist files */ = { + isa = PBXGroup; + children = ( + 717A25AE1E73401E00D2A2D3 /* Patient.plist */, + 7188E53E1EE483F8006C4114 /* $Template$.plist */, + ); + name = "Plist files"; + path = CareKitPatient; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 71ABF0FE1E6FBBB900B1E410 /* CareKitPatient */ = { + isa = PBXNativeTarget; + buildConfigurationList = 71ABF1131E6FBBB900B1E410 /* Build configuration list for PBXNativeTarget "CareKitPatient" */; + buildPhases = ( + 71ABF0FB1E6FBBB900B1E410 /* Sources */, + 71ABF0FC1E6FBBB900B1E410 /* Frameworks */, + 71ABF0FD1E6FBBB900B1E410 /* Resources */, + 7134B57F1E847E05003A90C7 /* Embed Frameworks */, + 715BF1701EA7F01C002908C7 /* Increment Build Number */, + ); + buildRules = ( + ); + dependencies = ( + 7188E53D1EE47BE1006C4114 /* PBXTargetDependency */, + ); + name = CareKitPatient; + productName = CareKitPatient; + productReference = 71ABF0FF1E6FBBB900B1E410 /* CareKitPatient.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 71ABF0F71E6FBBB900B1E410 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0830; + LastUpgradeCheck = 0900; + ORGANIZATIONNAME = "Srinath Tupil Muralidharan"; + TargetAttributes = { + 71ABF0FE1E6FBBB900B1E410 = { + CreatedOnToolsVersion = 8.3; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.ApplicationGroups.iOS = { + enabled = 0; + }; + }; + }; + }; + }; + buildConfigurationList = 71ABF0FA1E6FBBB900B1E410 /* Build configuration list for PBXProject "OCKPrototyper" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 71ABF0F61E6FBBB900B1E410; + productRefGroup = 71ABF1001E6FBBB900B1E410 /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 7188E5301EE47BAA006C4114 /* Products */; + ProjectRef = 7188E52F1EE47BAA006C4114 /* CareKit.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 71ABF0FE1E6FBBB900B1E410 /* CareKitPatient */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 7188E5361EE47BAA006C4114 /* CareKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = CareKit.framework; + remoteRef = 7188E5351EE47BAA006C4114 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 7188E5381EE47BAA006C4114 /* CareKitTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = CareKitTests.xctest; + remoteRef = 7188E5371EE47BAA006C4114 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 71ABF0FD1E6FBBB900B1E410 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 717A25AF1E73401E00D2A2D3 /* Patient.plist in Resources */, + 71ABF10F1E6FBBB900B1E410 /* LaunchScreen.storyboard in Resources */, + 7188E53F1EE483F8006C4114 /* $Template$.plist in Resources */, + 712CC0181E82EE2E004411A9 /* Media.xcassets in Resources */, + 714334751ED7AB390098C35F /* HeartIcon.png in Resources */, + 71ABF10A1E6FBBB900B1E410 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 715BF1701EA7F01C002908C7 /* Increment Build Number */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 12; + files = ( + ); + inputPaths = ( + ); + name = "Increment Build Number"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "build=$(/usr/libexec/PlistBuddy -c \"Print CFBundleVersion\" \"${PROJECT_DIR}/${INFOPLIST_FILE}\")\nbuild=$(($build + 1))\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $build\" \"${PROJECT_DIR}/${INFOPLIST_FILE}\""; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 71ABF0FB1E6FBBB900B1E410 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 71D173211E93074900519E9A /* APIExtensions.swift in Sources */, + 712952731E806556008E99A8 /* StringHelper.swift in Sources */, + 71ABF1031E6FBBB900B1E410 /* AppDelegate.swift in Sources */, + 71ABB45C1E7222B6000BAEFE /* TabBarViewController.swift in Sources */, + 7194C21C1E7BC7B600E02526 /* UIColorHelper.swift in Sources */, + 71992A631EB3F82E0088DC70 /* RandomNumberGeneratorHelper.swift in Sources */, + 717F70C41E77C23C00828B2E /* PlistParser.swift in Sources */, + 71B063CC1E7B7F7E002BEF45 /* CarePlanStoreManager.swift in Sources */, + 71D173241E9334D700519E9A /* PlistConstants.swift in Sources */, + 710F792A1EAE9A2B00C3466A /* DocumentsDisplayViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 7188E53D1EE47BE1006C4114 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = CareKit; + targetProxy = 7188E53C1EE47BE1006C4114 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 71ABF1081E6FBBB900B1E410 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 71ABF1091E6FBBB900B1E410 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 71ABF10D1E6FBBB900B1E410 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 71ABF10E1E6FBBB900B1E410 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 71ABF1111E6FBBB900B1E410 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 71ABF1121E6FBBB900B1E410 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 71ABF1141E6FBBB900B1E410 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = CareKitPatient/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = org.CareKit.CareKitPatient; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 71ABF1151E6FBBB900B1E410 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = CareKitPatient/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = org.CareKit.CareKitPatient; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 71ABF0FA1E6FBBB900B1E410 /* Build configuration list for PBXProject "OCKPrototyper" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 71ABF1111E6FBBB900B1E410 /* Debug */, + 71ABF1121E6FBBB900B1E410 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 71ABF1131E6FBBB900B1E410 /* Build configuration list for PBXNativeTarget "CareKitPatient" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 71ABF1141E6FBBB900B1E410 /* Debug */, + 71ABF1151E6FBBB900B1E410 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 71ABF0F71E6FBBB900B1E410 /* Project object */; +} diff --git a/CareKitPrototypingTool/OCKPrototyper/OCKPrototyper.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/CareKitPrototypingTool/OCKPrototyper/OCKPrototyper.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..0cdb99b42 --- /dev/null +++ b/CareKitPrototypingTool/OCKPrototyper/OCKPrototyper.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/CareKit.xcodeproj/xcshareddata/xcschemes/CareKit.xcscheme b/CareKitPrototypingTool/OCKPrototyper/OCKPrototyper.xcodeproj/xcshareddata/xcschemes/CareKitPatient.xcscheme similarity index 61% rename from CareKit.xcodeproj/xcshareddata/xcschemes/CareKit.xcscheme rename to CareKitPrototypingTool/OCKPrototyper/OCKPrototyper.xcodeproj/xcshareddata/xcschemes/CareKitPatient.xcscheme index 28541e3db..26333d8e7 100644 --- a/CareKit.xcodeproj/xcshareddata/xcschemes/CareKit.xcscheme +++ b/CareKitPrototypingTool/OCKPrototyper/OCKPrototyper.xcodeproj/xcshareddata/xcschemes/CareKitPatient.xcscheme @@ -1,6 +1,6 @@ + BlueprintIdentifier = "71ABF0FE1E6FBBB900B1E410" + BuildableName = "CareKitPatient.app" + BlueprintName = "CareKitPatient" + ReferencedContainer = "container:OCKPrototyper.xcodeproj"> @@ -28,24 +28,14 @@ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> - - - - + BlueprintIdentifier = "71ABF0FE1E6FBBB900B1E410" + BuildableName = "CareKitPatient.app" + BlueprintName = "CareKitPatient" + ReferencedContainer = "container:OCKPrototyper.xcodeproj"> @@ -61,15 +51,16 @@ debugDocumentVersioning = "YES" debugServiceExtension = "internal" allowLocationSimulation = "YES"> - + + BlueprintIdentifier = "71ABF0FE1E6FBBB900B1E410" + BuildableName = "CareKitPatient.app" + BlueprintName = "CareKitPatient" + ReferencedContainer = "container:OCKPrototyper.xcodeproj"> - + @@ -79,15 +70,16 @@ savedToolIdentifier = "" useCustomWorkingDirectory = "NO" debugDocumentVersioning = "YES"> - + + BlueprintIdentifier = "71ABF0FE1E6FBBB900B1E410" + BuildableName = "CareKitPatient.app" + BlueprintName = "CareKitPatient" + ReferencedContainer = "container:OCKPrototyper.xcodeproj"> - + diff --git a/CareKitTests/CareKitCarePlanTests.m b/CareKitTests/CareKitCarePlanTests.m index 3c5781aef..0ab0c5092 100644 --- a/CareKitTests/CareKitCarePlanTests.m +++ b/CareKitTests/CareKitCarePlanTests.m @@ -101,7 +101,8 @@ - (void)testActivitys { instructions:@"detailText1" imageURL:url1 schedule:schedule - userInfo:@{@"key":@"value1"}]; + userInfo:@{@"key":@"value1"} + optional:false]; OCKCareSchedule *weeklySchedule = [OCKCareSchedule weeklyScheduleWithStartDate:startDate occurrencesOnEachDay:@[@3, @3, @3, @3, @3, @3, @3]]; @@ -114,7 +115,8 @@ - (void)testActivitys { instructions:@"detailText2" imageURL:url2 schedule:weeklySchedule - userInfo:@{@"key":@"value2"}]; + userInfo:@{@"key":@"value2"} + optional:false]; OCKCarePlanActivity *item3 = [OCKCarePlanActivity assessmentWithIdentifier:@"id3" groupIdentifier:@"gid3" @@ -123,7 +125,8 @@ - (void)testActivitys { tintColor:[UIColor greenColor] resultResettable:YES schedule:schedule - userInfo:@{@"key":@"value3"}]; + userInfo:@{@"key":@"value3"} + optional:false]; OCKCarePlanActivity *item4 = [OCKCarePlanActivity interventionWithIdentifier:@"id4" groupIdentifier:@"gid4" @@ -133,7 +136,8 @@ - (void)testActivitys { instructions:@"detailText4" imageURL:url4 schedule:schedule - userInfo:@{@"key":@"value4"}]; + userInfo:@{@"key":@"value4"} + optional:false]; __block NSError *error; @@ -614,6 +618,26 @@ - (void)testActivitys { }]; XCTAssertEqual(count, 100); + +} + + +- (void)testThresholdFlagging { + NSCalendar *calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian]; + NSDate *now = [NSDate date]; + NSDateComponents *today = [[NSDateComponents alloc] initWithDate:now calendar:calendar]; + + OCKCareSchedule *dailySchedule = [OCKCareSchedule dailyScheduleWithStartDate:today occurrencesPerDay:1 daysToSkip:1 endDate:nil dailyThreshold:[OCKCarePlanThreshold adheranceThresholdWithValue:@(1) title:@"Did not do activity"]]; + + NSArray *> *thresholds = [NSArray arrayWithObject:[NSArray arrayWithObject:[OCKCarePlanThreshold numericThresholdWithValue:@(80) type:OCKCarePlanThresholdTypeNumericGreaterThan upperValue:nil title:@"Heart Rate Above 80"]]]; + + OCKCarePlanActivity *assessmentActivity = [OCKCarePlanActivity assessmentWithIdentifier:@"assessment1" groupIdentifier:nil title:@"First Assessment" text:nil tintColor:nil resultResettable:YES schedule:dailySchedule userInfo:nil thresholds:thresholds optional:false]; + + OCKCarePlanEventResult *result1 = [[OCKCarePlanEventResult alloc] initWithValueString:@"75" unitString:@"beats/minute" userInfo:nil values:@[@(75)]]; + OCKCarePlanEventResult *result2 = [[OCKCarePlanEventResult alloc] initWithValueString:@"85" unitString:@"beats/minute" userInfo:nil values:@[@(85)]]; + + XCTAssertFalse([assessmentActivity.thresholds[0][0] evaluateThresholdForValue:result1.values[0]]); + XCTAssertTrue([assessmentActivity.thresholds[0][0] evaluateThresholdForValue:result2.values[0]]); } @@ -648,6 +672,18 @@ - (void)testDailySchedules { XCTAssertEqual([dailySchedule numberOfEventsOnDate:[today dateCompByAddingDays:1]], 4); XCTAssertEqual([dailySchedule numberOfEventsOnDate:[today dateCompByAddingDays:6]], 0); + // Thresholds + dailySchedule = [OCKCareSchedule dailyScheduleWithStartDate:today + occurrencesPerDay:3 + daysToSkip:3 + endDate:nil + dailyThreshold:[OCKCarePlanThreshold adheranceThresholdWithValue:@(2) title:@"Did it at least twice today."]]; + + XCTAssertEqual([dailySchedule thresholdOnDate:today].value, @(2)); + XCTAssertNil([dailySchedule thresholdOnDate:[today dateCompByAddingDays:1]]); + XCTAssertEqual([dailySchedule thresholdOnDate:[today dateCompByAddingDays:4]].value, @(2)); + XCTAssertTrue([[dailySchedule thresholdOnDate:today] evaluateThresholdForValue:@(1)]); + XCTAssertFalse([[dailySchedule thresholdOnDate:today] evaluateThresholdForValue:@(3)]); } - (void)testWeeklySchedules { @@ -688,6 +724,26 @@ - (void)testWeeklySchedules { XCTAssertEqual([weeklySchedule numberOfEventsOnDate:[today dateCompByAddingDays:1*7]], occurrencesOnDay); XCTAssertEqual([weeklySchedule numberOfEventsOnDate:[today dateCompByAddingDays:6*7]], 0); + // Thresholds + OCKCarePlanThreshold *threshold0 = [OCKCarePlanThreshold adheranceThresholdWithValue:@(0) title:nil]; + OCKCarePlanThreshold *threshold1 = [OCKCarePlanThreshold adheranceThresholdWithValue:@(1) title:nil]; + OCKCarePlanThreshold *threshold2 = [OCKCarePlanThreshold adheranceThresholdWithValue:@(2) title:nil]; + NSDateComponents *sunday = [today dateCompByAddingDays:3*7 + 1 - weekday]; + + NSArray *thresholds = @[threshold1, threshold1, threshold1, threshold0, threshold2, threshold2, threshold2]; + weeklySchedule = [OCKCareSchedule weeklyScheduleWithStartDate:today + occurrencesOnEachDay:occurrences + weeksToSkip:2 + endDate:nil + thresholdsOnEachDay:thresholds]; + + XCTAssertEqual([weeklySchedule thresholdOnDate:sunday].value, @(1)); + XCTAssertEqual([weeklySchedule thresholdOnDate:[sunday dateCompByAddingDays:3]].value, @(0)); + XCTAssertEqual([weeklySchedule thresholdOnDate:[sunday dateCompByAddingDays:5]].value, @(2)); + XCTAssertNil([weeklySchedule thresholdOnDate:[sunday dateCompByAddingDays:9]]); + XCTAssertTrue([[weeklySchedule thresholdOnDate:sunday] evaluateThresholdForValue:@(0)]); + XCTAssertFalse([[weeklySchedule thresholdOnDate:[sunday dateCompByAddingDays:6]] evaluateThresholdForValue:@(4)]); + } - (void)testDateComponents { @@ -710,6 +766,502 @@ - (void)testDateComponents { } } +- (void)testThresholds { + + // Logic check on threshold evaulations + OCKCarePlanThreshold *threshold = [OCKCarePlanThreshold adheranceThresholdWithValue:@(1) title:@"Didn't Do It"]; + XCTAssertTrue([threshold evaluateThresholdForValue:@(0)]); + XCTAssertFalse([threshold evaluateThresholdForValue:@(1)]); + + threshold = [OCKCarePlanThreshold numericThresholdWithValue:@(4) type:OCKCarePlanThresholdTypeNumericGreaterThan upperValue:nil title:nil]; + XCTAssertTrue([threshold evaluateThresholdForValue:@(6)]); + XCTAssertFalse([threshold evaluateThresholdForValue:@(4)]); + XCTAssertFalse([threshold evaluateThresholdForValue:@(3)]); + + threshold = [OCKCarePlanThreshold numericThresholdWithValue:@(4) type:OCKCarePlanThresholdTypeNumericGreaterThanOrEqual upperValue:nil title:nil]; + XCTAssertTrue([threshold evaluateThresholdForValue:@(6)]); + XCTAssertTrue([threshold evaluateThresholdForValue:@(4)]); + XCTAssertFalse([threshold evaluateThresholdForValue:@(3)]); + + threshold = [OCKCarePlanThreshold numericThresholdWithValue:@(4) type:OCKCarePlanThresholdTypeNumericLessThan upperValue:nil title:nil]; + XCTAssertFalse([threshold evaluateThresholdForValue:@(6)]); + XCTAssertFalse([threshold evaluateThresholdForValue:@(4)]); + XCTAssertTrue([threshold evaluateThresholdForValue:@(3)]); + + threshold = [OCKCarePlanThreshold numericThresholdWithValue:@(4) type:OCKCarePlanThresholdTypeNumericLessThanOrEqual upperValue:nil title:nil]; + XCTAssertFalse([threshold evaluateThresholdForValue:@(6)]); + XCTAssertTrue([threshold evaluateThresholdForValue:@(4)]); + XCTAssertTrue([threshold evaluateThresholdForValue:@(3)]); + + threshold = [OCKCarePlanThreshold numericThresholdWithValue:@(4) type:OCKCarePlanThresholdTypeNumericEqual upperValue:nil title:nil]; + XCTAssertFalse([threshold evaluateThresholdForValue:@(6)]); + XCTAssertTrue([threshold evaluateThresholdForValue:@(4)]); + XCTAssertFalse([threshold evaluateThresholdForValue:@(3)]); + + threshold = [OCKCarePlanThreshold numericThresholdWithValue:@(4) type:OCKCarePlanThresholdTypeNumericRangeInclusive upperValue:@(7) title:nil]; + XCTAssertFalse([threshold evaluateThresholdForValue:@(9)]); + XCTAssertTrue([threshold evaluateThresholdForValue:@(7)]); + XCTAssertTrue([threshold evaluateThresholdForValue:@(5)]); + XCTAssertTrue([threshold evaluateThresholdForValue:@(4)]); + XCTAssertFalse([threshold evaluateThresholdForValue:@(3)]); + + threshold = [OCKCarePlanThreshold numericThresholdWithValue:@(4) type:OCKCarePlanThresholdTypeNumericRangeExclusive upperValue:@(7) title:nil]; + XCTAssertFalse([threshold evaluateThresholdForValue:@(9)]); + XCTAssertFalse([threshold evaluateThresholdForValue:@(7)]); + XCTAssertTrue([threshold evaluateThresholdForValue:@(5)]); + XCTAssertFalse([threshold evaluateThresholdForValue:@(4)]); + XCTAssertFalse([threshold evaluateThresholdForValue:@(3)]); + + /////////////////////// + // Core Data Storage // + /////////////////////// + + NSURL *directoryURL = [NSURL fileURLWithPath:[self cleanTestPath]]; + OCKCarePlanStore *store = [[OCKCarePlanStore alloc] initWithPersistenceDirectoryURL:directoryURL]; + store.delegate = self; + NSDateComponents *startDate = [[NSDateComponents alloc] initWithYear:2016 month:01 day:01]; + XCTestExpectation *expectation; + + + // Adherance Thresholds + + threshold = [OCKCarePlanThreshold adheranceThresholdWithValue:@(2) + title:@"Did not complete activity"]; + + OCKCareSchedule *schedule = [OCKCareSchedule dailyScheduleWithStartDate:startDate + occurrencesPerDay:2 + daysToSkip:1 + endDate:nil + dailyThreshold:threshold]; + + + OCKCarePlanActivity *activity = [OCKCarePlanActivity assessmentWithIdentifier:@"Activity1" + groupIdentifier:nil + title:@"The First Activity" + text:@"To be performed twice every other day." + tintColor:nil + resultResettable:YES + schedule:schedule + userInfo:nil + thresholds:nil + optional:false]; + + __block NSMutableArray *eventsArray; + + expectation = [self expectationWithDescription:@"Add activity 1"]; + [store addActivity:activity completion:^(BOOL success, NSError * _Nullable error) { + XCTAssertTrue(success); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError * _Nullable error) { + XCTAssertNil(error); + XCTAssertTrue([self isListChangeDelegateCalled]); + }]; + + + [store activityForIdentifier:activity.identifier completion:^(BOOL success, OCKCarePlanActivity * _Nullable cdActivity, NSError * _Nullable error) { + XCTAssertTrue(success); + XCTAssertEqualObjects(cdActivity, activity); + OCKCarePlanThreshold *cdAdheranceThreshold = [cdActivity.schedule thresholdOnDate:startDate]; + XCTAssertTrue([cdAdheranceThreshold evaluateThresholdForValue:@(1)]); + XCTAssertFalse([cdAdheranceThreshold evaluateThresholdForValue:@(2)]); + + cdAdheranceThreshold = [cdActivity.schedule thresholdOnDate:[startDate dateCompByAddingDays:1]]; + XCTAssertNil(cdAdheranceThreshold); + }]; + + expectation = [self expectationWithDescription:@"Get events 1"]; + [store eventsForActivity:activity date:startDate completion:^(NSArray * _Nonnull events, NSError * _Nullable error) { + XCTAssertNotNil(events); + XCTAssertEqual(events.count, 2); + XCTAssertEqual(events[0].state, OCKCarePlanEventStateInitial); + XCTAssertEqual(events[1].state, OCKCarePlanEventStateInitial); + + eventsArray = [NSMutableArray arrayWithArray:events]; + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError * _Nullable error) { + XCTAssertNil(error); + }]; + + [store evaluateAdheranceThresholdForActivity:activity date:startDate completion:^(BOOL success, OCKCarePlanThreshold * _Nullable cdThreshold, NSError * _Nullable error) { + XCTAssertNil(error); + XCTAssertEqualObjects(threshold, cdThreshold); + }]; + + [store evaluateAdheranceThresholdForActivity:activity date:[startDate dateCompByAddingDays:1] completion:^(BOOL success, OCKCarePlanThreshold * _Nullable cdThreshold, NSError * _Nullable error) { + XCTAssertNil(error); + XCTAssertNil(cdThreshold); + }]; + + expectation = [self expectationWithDescription:@"Update event 1.1"]; + [store updateEvent:eventsArray[0] withResult:nil state:OCKCarePlanEventStateCompleted completion:^(BOOL success, OCKCarePlanEvent * _Nullable event, NSError * _Nullable error) { + XCTAssertTrue(success); + XCTAssertEqual(event.state, OCKCarePlanEventStateCompleted); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError * _Nullable error) { + XCTAssertNil(error); + XCTAssertTrue([self isEventChangeDelegateCalled]); + }]; + + [store evaluateAdheranceThresholdForActivity:activity date:startDate completion:^(BOOL success, OCKCarePlanThreshold * _Nullable cdThreshold, NSError * _Nullable error) { + XCTAssertNil(error); + XCTAssertEqualObjects(threshold, cdThreshold); + }]; + + [store evaluateAdheranceThresholdForActivity:activity date:[startDate dateCompByAddingDays:1] completion:^(BOOL success, OCKCarePlanThreshold * _Nullable cdThreshold, NSError * _Nullable error) { + XCTAssertNil(error); + XCTAssertNil(cdThreshold); + }]; + + expectation = [self expectationWithDescription:@"Update event 1.2"]; + [store updateEvent:eventsArray[1] withResult:nil state:OCKCarePlanEventStateCompleted completion:^(BOOL success, OCKCarePlanEvent * _Nullable event, NSError * _Nullable error) { + XCTAssertTrue(success); + XCTAssertEqual(event.state, OCKCarePlanEventStateCompleted); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError * _Nullable error) { + XCTAssertNil(error); + XCTAssertTrue([self isEventChangeDelegateCalled]); + }]; + + [store evaluateAdheranceThresholdForActivity:activity date:startDate completion:^(BOOL success, OCKCarePlanThreshold * _Nullable cdThreshold, NSError * _Nullable error) { + XCTAssertNil(error); + XCTAssertNil(cdThreshold); + }]; + + [store evaluateAdheranceThresholdForActivity:activity date:[startDate dateCompByAddingDays:1] completion:^(BOOL success, OCKCarePlanThreshold * _Nullable cdThreshold, NSError * _Nullable error) { + XCTAssertNil(error); + XCTAssertNil(cdThreshold); + }]; + + [store evaluateAdheranceThresholdForActivity:activity date:[startDate dateCompByAddingDays:2] completion:^(BOOL success, OCKCarePlanThreshold * _Nullable cdThreshold, NSError * _Nullable error) { + XCTAssertNil(error); + XCTAssertEqualObjects(threshold, cdThreshold); + }]; + + + // Numeric Thresholds + + OCKCarePlanThreshold *lowThreshold = [OCKCarePlanThreshold numericThresholdWithValue:@(3) type:OCKCarePlanThresholdTypeNumericLessThanOrEqual upperValue:nil title:@"A score <= 3!"]; + OCKCarePlanThreshold *highThreshold = [OCKCarePlanThreshold numericThresholdWithValue:@(7) type:OCKCarePlanThresholdTypeNumericGreaterThan upperValue:nil title:@"A score > 7!"]; + + schedule = [OCKCareSchedule dailyScheduleWithStartDate:startDate occurrencesPerDay:1 daysToSkip:1 endDate:nil]; + + OCKCarePlanActivity *activity2 = [OCKCarePlanActivity assessmentWithIdentifier:@"mood" + groupIdentifier:nil + title:@"Mood Score" + text:@"How would you rate your mood right now?" + tintColor:nil + resultResettable:YES + schedule:schedule + userInfo:nil + thresholds:@[@[lowThreshold, highThreshold]] + optional:false]; + + expectation = [self expectationWithDescription:@"Add activity 2"]; + [store addActivity:activity2 completion:^(BOOL success, NSError * _Nullable error) { + XCTAssertTrue(success); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError * _Nullable error) { + XCTAssertNil(error); + XCTAssertTrue([self isListChangeDelegateCalled]); + }]; + + expectation = [self expectationWithDescription:@"Get events 2.1"]; + [eventsArray removeAllObjects]; + [store enumerateEventsOfActivity:activity2 startDate:startDate endDate:[startDate dateCompByAddingDays:6] handler:^(OCKCarePlanEvent * _Nullable event, BOOL * _Nonnull stop) { + if (event) { + [eventsArray addObject:event]; + } + } completion:^(BOOL completed, NSError * _Nullable error) { + XCTAssertTrue(completed); + XCTAssertNil(error); + XCTAssertEqual(eventsArray.count, 4); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError * _Nullable error) { + XCTAssertNil(error); + }]; + + OCKCarePlanEventResult *middleResult = [[OCKCarePlanEventResult alloc] initWithValueString:@"5" unitString:@"" userInfo:nil values:@[@(5)]]; + OCKCarePlanEventResult *lowResult = [[OCKCarePlanEventResult alloc] initWithValueString:@"2" unitString:@"" userInfo:nil values:@[@(2)]]; + OCKCarePlanEventResult *highResult = [[OCKCarePlanEventResult alloc] initWithValueString:@"9" unitString:@"" userInfo:nil values:@[@(9)]]; + + expectation = [self expectationWithDescription:@"Update event results 2.1"]; + [store updateEvent:eventsArray[0] withResult:middleResult state:OCKCarePlanEventStateCompleted completion:^(BOOL success, OCKCarePlanEvent * _Nullable event, NSError * _Nullable error) { + XCTAssertTrue(success); + XCTAssertNil(error); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError * _Nullable error) { + XCTAssertNil(error); + XCTAssertTrue([self isEventChangeDelegateCalled]); + }]; + + expectation = [self expectationWithDescription:@"Update event results 2.2"]; + [store updateEvent:eventsArray[1] withResult:lowResult state:OCKCarePlanEventStateCompleted completion:^(BOOL success, OCKCarePlanEvent * _Nullable event, NSError * _Nullable error) { + XCTAssertTrue(success); + XCTAssertNil(error); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError * _Nullable error) { + XCTAssertNil(error); + XCTAssertTrue([self isEventChangeDelegateCalled]); + }]; + + expectation = [self expectationWithDescription:@"Update event results 2.3"]; + [store updateEvent:eventsArray[2] withResult:highResult state:OCKCarePlanEventStateCompleted completion:^(BOOL success, OCKCarePlanEvent * _Nullable event, NSError * _Nullable error) { + XCTAssertTrue(success); + XCTAssertNil(error); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError * _Nullable error) { + XCTAssertNil(error); + XCTAssertTrue([self isEventChangeDelegateCalled]); + }]; + + expectation = [self expectationWithDescription:@"Get events 2.2"]; + [eventsArray removeAllObjects]; + [store enumerateEventsOfActivity:activity2 startDate:startDate endDate:[startDate dateCompByAddingDays:6] handler:^(OCKCarePlanEvent * _Nullable event, BOOL * _Nonnull stop) { + if (event) { + [eventsArray addObject:event]; + } + } completion:^(BOOL completed, NSError * _Nullable error) { + XCTAssertTrue(completed); + XCTAssertNil(error); + XCTAssertEqual(eventsArray.count, 4); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError * _Nullable error) { + XCTAssertNil(error); + }]; + + NSArray *> *triggeredThresholds = [eventsArray[0] evaluateNumericThresholds]; + XCTAssertEqual(triggeredThresholds.count, 1); + XCTAssertEqual(triggeredThresholds[0].count, 0); + + triggeredThresholds = [eventsArray[1] evaluateNumericThresholds]; + XCTAssertEqual(triggeredThresholds.count, 1); + XCTAssertEqual(triggeredThresholds[0].count, 1); + XCTAssertEqualObjects(triggeredThresholds[0][0], lowThreshold); + + triggeredThresholds = [eventsArray[2] evaluateNumericThresholds]; + XCTAssertEqual(triggeredThresholds.count, 1); + XCTAssertEqual(triggeredThresholds[0].count, 1); + XCTAssertEqualObjects(triggeredThresholds[0][0], highThreshold); + + triggeredThresholds = [eventsArray[3] evaluateNumericThresholds]; + XCTAssertEqual(triggeredThresholds.count, 0); + + // 2-valued numeric thresholds + + OCKCarePlanThreshold *lowSystolicThreshold = [OCKCarePlanThreshold numericThresholdWithValue:@(100) type:OCKCarePlanThresholdTypeNumericLessThanOrEqual upperValue:nil title:@"A low systolic blood pressure."]; + OCKCarePlanThreshold *highSystolicThreshold = [OCKCarePlanThreshold numericThresholdWithValue:@(140) type:OCKCarePlanThresholdTypeNumericGreaterThanOrEqual upperValue:nil title:@"A high systolic blood pressure."]; + OCKCarePlanThreshold *veryHighSystolicThreshold = [OCKCarePlanThreshold numericThresholdWithValue:@(180) type:OCKCarePlanThresholdTypeNumericGreaterThanOrEqual upperValue:nil title:@"A crazy high systolic blood pressure!"]; + OCKCarePlanThreshold *middleDiastolicThreshold = [OCKCarePlanThreshold numericThresholdWithValue:@(65) type:OCKCarePlanThresholdTypeNumericRangeInclusive upperValue:@(85) title:@"A good diastolic blood pressure."]; + + OCKCarePlanActivity *activity3 = [OCKCarePlanActivity assessmentWithIdentifier:@"bloodPressure" + groupIdentifier:nil + title:@"Blood Pressure" + text:@"Systolic-Diastolic blood pressure." + tintColor:nil + resultResettable:YES + schedule:schedule + userInfo:nil + thresholds:@[@[lowSystolicThreshold, highSystolicThreshold, veryHighSystolicThreshold], @[middleDiastolicThreshold]] + optional:false]; + + expectation = [self expectationWithDescription:@"Add activity 3"]; + [store addActivity:activity3 completion:^(BOOL success, NSError * _Nullable error) { + XCTAssertTrue(success); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError * _Nullable error) { + XCTAssertNil(error); + XCTAssertTrue([self isListChangeDelegateCalled]); + }]; + + expectation = [self expectationWithDescription:@"Get events 3.1"]; + [eventsArray removeAllObjects]; + [store enumerateEventsOfActivity:activity3 startDate:startDate endDate:[startDate dateCompByAddingDays:14] handler:^(OCKCarePlanEvent * _Nullable event, BOOL * _Nonnull stop) { + if (event) { + [eventsArray addObject:event]; + } + } completion:^(BOOL completed, NSError * _Nullable error) { + XCTAssertTrue(completed); + XCTAssertNil(error); + XCTAssertEqual(eventsArray.count, 8); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError * _Nullable error) { + XCTAssertNil(error); + }]; + + NSArray *results = @[[[OCKCarePlanEventResult alloc] initWithValueString:@"120-80" unitString:@"" userInfo:nil values:@[@(120), @(80)]], + [[OCKCarePlanEventResult alloc] initWithValueString:@"160-80" unitString:@"" userInfo:nil values:@[@(160), @(80)]], + [[OCKCarePlanEventResult alloc] initWithValueString:@"90-80" unitString:@"" userInfo:nil values:@[@(90), @(80)]], + [[OCKCarePlanEventResult alloc] initWithValueString:@"120-90" unitString:@"" userInfo:nil values:@[@(120), @(90)]], + [[OCKCarePlanEventResult alloc] initWithValueString:@"160-90" unitString:@"" userInfo:nil values:@[@(160), @(90)]], + [[OCKCarePlanEventResult alloc] initWithValueString:@"90-90" unitString:@"" userInfo:nil values:@[@(90), @(90)]], + [[OCKCarePlanEventResult alloc] initWithValueString:@"200-80" unitString:@"" userInfo:nil values:@[@(200), @(80)]] + ]; + + for (int index = 0; index < results.count; index++) { + expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Update event results 3.%d", index + 1]]; + [store updateEvent:eventsArray[index] withResult:results[index] state:OCKCarePlanEventStateCompleted completion:^(BOOL success, OCKCarePlanEvent * _Nullable event, NSError * _Nullable error) { + XCTAssertTrue(success); + XCTAssertNil(error); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError * _Nullable error) { + XCTAssertNil(error); + XCTAssertTrue([self isEventChangeDelegateCalled]); + }]; + } + + expectation = [self expectationWithDescription:@"Get events 3.2"]; + [eventsArray removeAllObjects]; + [store enumerateEventsOfActivity:activity3 startDate:startDate endDate:[startDate dateCompByAddingDays:14] handler:^(OCKCarePlanEvent * _Nullable event, BOOL * _Nonnull stop) { + if (event) { + [eventsArray addObject:event]; + } + } completion:^(BOOL completed, NSError * _Nullable error) { + XCTAssertTrue(completed); + XCTAssertNil(error); + XCTAssertEqual(eventsArray.count, 8); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError * _Nullable error) { + XCTAssertNil(error); + }]; + + triggeredThresholds = [eventsArray[0] evaluateNumericThresholds]; + XCTAssertEqual(triggeredThresholds.count, 2); + XCTAssertEqual(triggeredThresholds[0].count, 0); + XCTAssertEqual(triggeredThresholds[1].count, 1); + XCTAssertEqualObjects(triggeredThresholds[1][0], middleDiastolicThreshold); + + triggeredThresholds = [eventsArray[1] evaluateNumericThresholds]; + XCTAssertEqual(triggeredThresholds.count, 2); + XCTAssertEqual(triggeredThresholds[0].count, 1); + XCTAssertEqualObjects(triggeredThresholds[0][0], highSystolicThreshold); + XCTAssertEqual(triggeredThresholds[1].count, 1); + XCTAssertEqualObjects(triggeredThresholds[1][0], middleDiastolicThreshold); + + triggeredThresholds = [eventsArray[2] evaluateNumericThresholds]; + XCTAssertEqual(triggeredThresholds.count, 2); + XCTAssertEqual(triggeredThresholds[0].count, 1); + XCTAssertEqualObjects(triggeredThresholds[0][0], lowSystolicThreshold); + XCTAssertEqual(triggeredThresholds[1].count, 1); + XCTAssertEqualObjects(triggeredThresholds[1][0], middleDiastolicThreshold); + + triggeredThresholds = [eventsArray[3] evaluateNumericThresholds]; + XCTAssertEqual(triggeredThresholds.count, 2); + XCTAssertEqual(triggeredThresholds[0].count, 0); + XCTAssertEqual(triggeredThresholds[1].count, 0); + + triggeredThresholds = [eventsArray[4] evaluateNumericThresholds]; + XCTAssertEqual(triggeredThresholds.count, 2); + XCTAssertEqual(triggeredThresholds[0].count, 1); + XCTAssertEqualObjects(triggeredThresholds[0][0], highSystolicThreshold); + XCTAssertEqual(triggeredThresholds[1].count, 0); + + triggeredThresholds = [eventsArray[5] evaluateNumericThresholds]; + XCTAssertEqual(triggeredThresholds.count, 2); + XCTAssertEqual(triggeredThresholds[0].count, 1); + XCTAssertEqualObjects(triggeredThresholds[0][0], lowSystolicThreshold); + XCTAssertEqual(triggeredThresholds[1].count, 0); + + triggeredThresholds = [eventsArray[6] evaluateNumericThresholds]; + XCTAssertEqual(triggeredThresholds.count, 2); + XCTAssertEqual(triggeredThresholds[0].count, 2); + XCTAssertTrue([triggeredThresholds[0] containsObject:highSystolicThreshold]); + XCTAssertTrue([triggeredThresholds[0] containsObject:veryHighSystolicThreshold]); + XCTAssertEqual(triggeredThresholds[1].count, 1); + XCTAssertEqualObjects(triggeredThresholds[1][0], middleDiastolicThreshold); + + triggeredThresholds = [eventsArray[7] evaluateNumericThresholds]; + XCTAssertEqual(triggeredThresholds.count, 0); +} + +- (void)testResults { + // Custom created results (no HKSample). + OCKCarePlanEventResult *customResult = [[OCKCarePlanEventResult alloc] initWithValueString:@"10" + unitString:@"mg" + userInfo:nil]; + XCTAssert([customResult.valueString isEqualToString:@"10"]); + XCTAssertNil(customResult.values); + + customResult = [customResult initWithValueString:@"10" + unitString:@"mg" + userInfo:nil + values:nil]; + XCTAssert([customResult.valueString isEqualToString:@"10"]); + XCTAssertNil(customResult.values); + + customResult = [customResult initWithValueString:@"10" + unitString:@"mg" + userInfo:nil + values:@[@(10)]]; + XCTAssert([customResult.valueString isEqualToString:@"10"]); + XCTAssertNotNil(customResult.values); + XCTAssertEqual(customResult.values.count, 1); + XCTAssertEqual(customResult.values[0], @(10)); + + // Single HKSample result. + HKQuantitySample *sample = [HKQuantitySample quantitySampleWithType:[HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeight] + quantity:[HKQuantity quantityWithUnit:[HKUnit meterUnit] doubleValue:1.85] + startDate:[NSDate date] + endDate:[NSDate date]]; + + OCKCarePlanEventResult *sampleResult = [[OCKCarePlanEventResult alloc] initWithQuantitySample:sample + quantityStringFormatter:nil + displayUnit:[HKUnit meterUnit] + displayUnitStringKey:@"m" + userInfo:nil]; + + XCTAssert([sampleResult.valueString isEqualToString:@"1.85"]); + XCTAssertNotNil(sampleResult.values); + XCTAssertEqual(sampleResult.values.count, 1); + XCTAssertEqual(sampleResult.values[0].doubleValue, 1.85); + + // Double HKSample result (blood pressure). + HKQuantitySample *diastolicSample = [HKQuantitySample quantitySampleWithType:[HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodPressureDiastolic] + quantity:[HKQuantity quantityWithUnit:[HKUnit millimeterOfMercuryUnit] doubleValue:120] + startDate:[NSDate date] + endDate:[NSDate date]]; + HKQuantitySample *systolicSample = [HKQuantitySample quantitySampleWithType:[HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodPressureSystolic] + quantity:[HKQuantity quantityWithUnit:[HKUnit millimeterOfMercuryUnit] doubleValue:80] + startDate:[NSDate date] + endDate:[NSDate date]]; + HKCorrelation *bloodPressure = [HKCorrelation correlationWithType:[HKCorrelationType correlationTypeForIdentifier:HKCorrelationTypeIdentifierBloodPressure] startDate:[NSDate date] endDate:[NSDate date] objects:[NSSet setWithObjects:diastolicSample, systolicSample, nil]]; + + OCKCarePlanEventResult *correlationResult = [[OCKCarePlanEventResult alloc] initWithCorrelation:bloodPressure + quantityStringFormatter:nil + displayUnit:[HKUnit millimeterOfMercuryUnit] + unitStringKeys:@{[HKUnit millimeterOfMercuryUnit] : @"mmHg"} + userInfo:nil]; + + XCTAssertNotNil(correlationResult.values); + XCTAssertEqual(correlationResult.values.count, 2); + XCTAssertEqual(correlationResult.values[0].doubleValue, 120); + XCTAssertEqual(correlationResult.values[1].doubleValue, 80); + +} + - (void)carePlanStoreActivityListDidChange:(OCKCarePlanStore *)store { XCTAssertTrue([NSThread isMainThread]); XCTAssertFalse(_listChanged); diff --git a/README.md b/README.md index 005051d83..bc793a609 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ The source in the CareKit repository is made available under the following license unless another license is explicitly identified: ``` -Copyright (c) 2016, Apple Inc. All rights reserved. +Copyright (c) 2016 - 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 2a0254438..f117e19be 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,5 +1,40 @@ # CareKit Release Notes +## CareKit 1.2 Release Notes + +*CareKit 1.2* supports *iOS* and requires *Xcode 8.0* or later. The minimum supported *Base SDK* is *9.0*. +*CareKit 1.2* includes the following new features and enhancements *by [Apple Inc.](https://github.com/carekit-apple)* + +- **Care Contents Card** + + The *Sample App* (OCKSample project in CareKit's workspace) now includes a Care Contents view controller, which allows for activities and interventions to be seen in the same place. You may still choose to use the *Care Card *and *Symptom Tracker* view controllers independently to separate out care “to-do’s” and measurement tracking if desired. + +- **Optional Activities** + + Intervention and assessment activity types can now be tagged as “optional”. Completion of optional activities do not contribute to a user’s daily completion goals, and are well suited for “take as needed” care activities – such as pain medications or optional physical activities. + +- **Read Only Activity Type** + + You can now create a new class of activity called “read only” by utilizing the ReadOnly initializer. Read only activities can be used to display information which do not require any action from the user. Examples can include day-of-surgery dietary instructions, or tricks and tips that might be interesting to share throughout a user’s care journey. + +- **Updated Header View and 28 Glyph Icons** + + The header view across *Care Card, Symptom Tracker and the New Care Contents* has been updated to display a daily ring view with customizable glyphs inside to represent completion of care activities. The Apple team has designed 28 icons that can be used within the ring view, and are compatible as Apple Watch complications. Once a user reaches 100%, the ring will fill in and a star badge will appear to easily identify days of full compliance. + +- **Updated Insights Tab with Thresholds** + + The *Insights* view controller has been updated to include the ability to display thresholds. You can now set thresholds in your assessment or intervention activities, and display alert UI and tint colors on the *Insights* view controller if thresholds are broken. + +- **Inbox Feature in Connect** + + We’ve updated the *Inbox* view controller to include UI for messaging between consumers and care teams, friends, and family members. Developers can choose to include this functionality in use cases where asynchronous messaging might play a crucial role in a consumer’s care journey. + +- **Cloud Bridge API** + + We’re making data sharing between CareKit apps even easier with the new addition of our Cloud Bridge API. The bridge API is an Abstract cloud API that conforms to the CareKit schema and enables data syncing without any additional configuration. It’s designed to allow CareKit based apps to seamlessly integrate with backend cloud solutions, and is based upon the current CareKit data model and architecture +The bridge API provides all of the necessary hooks through delegate functions for cloud bridge’s to seamlessly hook into the CareKit framework, allowing developers or current cloud providers to conform to the CareKit schema with reduced effort and create backend solutions that fit directly into the current framework architecture. + + ## CareKit 1.1 Release Notes *CareKit 1.1* supports *iOS* and requires *Xcode 8.0* or later. The minimum supported *Base SDK* is *9.0*. diff --git a/Sample/OCKSample.xcodeproj/project.pbxproj b/Sample/OCKSample.xcodeproj/project.pbxproj index 394f0a5a8..3f32035fd 100644 --- a/Sample/OCKSample.xcodeproj/project.pbxproj +++ b/Sample/OCKSample.xcodeproj/project.pbxproj @@ -10,7 +10,6 @@ 1801613F1D4E778F0036E247 /* WatchConnectivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1801613E1D4E778F0036E247 /* WatchConnectivityManager.swift */; }; 18214FC81D3D9C8000AE765B /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 18214FC61D3D9C8000AE765B /* Interface.storyboard */; }; 18214FD11D3D9C8000AE765B /* OCKSampleWatch Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 18214FD01D3D9C8000AE765B /* OCKSampleWatch Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 18214FD61D3D9C8000AE765B /* InterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18214FD51D3D9C8000AE765B /* InterfaceController.swift */; }; 18214FD81D3D9C8000AE765B /* ExtensionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18214FD71D3D9C8000AE765B /* ExtensionDelegate.swift */; }; 18214FDA1D3D9C8000AE765B /* ComplicationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18214FD91D3D9C8000AE765B /* ComplicationController.swift */; }; 18214FE01D3D9C8000AE765B /* OCKSampleWatch.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 18214FC41D3D9C8000AE765B /* OCKSampleWatch.app */; }; @@ -21,8 +20,8 @@ 18FB87491D3ED97A00D2A06D /* WCKEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FB87481D3ED97A00D2A06D /* WCKEvent.swift */; }; 18FB874F1D3F106200D2A06D /* ActivityRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FB874E1D3F106200D2A06D /* ActivityRow.swift */; }; 24026C671D6D244A007582F3 /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 24026C661D6D244A007582F3 /* HealthKit.framework */; }; - 2436E4341CD18BFD00ABE381 /* ResearchKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2436E4311CD18BED00ABE381 /* ResearchKit.framework */; }; - 2436E4351CD18BFD00ABE381 /* ResearchKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 2436E4311CD18BED00ABE381 /* ResearchKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 247E67881E53DCD30045D28B /* ResearchKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 247E67871E53DCD30045D28B /* ResearchKit.framework */; }; + 247E67891E53DCD30045D28B /* ResearchKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 247E67871E53DCD30045D28B /* ResearchKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 24A69BBC1D779DE600BEAD84 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B5C100991CB12E0C00FAD7E8 /* Assets.xcassets */; }; B55565771CC82FCA00513644 /* CareKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B55565711CC82F8D00513644 /* CareKit.framework */; }; B55565781CC82FCA00513644 /* CareKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B55565711CC82F8D00513644 /* CareKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -43,12 +42,14 @@ B5C100981CB12E0C00FAD7E8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B5C100961CB12E0C00FAD7E8 /* Main.storyboard */; }; B5C1009A1CB12E0C00FAD7E8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B5C100991CB12E0C00FAD7E8 /* Assets.xcassets */; }; B5C1009D1CB12E0C00FAD7E8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B5C1009B1CB12E0C00FAD7E8 /* LaunchScreen.storyboard */; }; - B5C100D51CB1475900FAD7E8 /* RootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C100D41CB1475900FAD7E8 /* RootViewController.swift */; }; B5C100DC1CB1547600FAD7E8 /* SampleData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C100DB1CB1547600FAD7E8 /* SampleData.swift */; }; B5C100DE1CB1556F00FAD7E8 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C100DD1CB1556F00FAD7E8 /* Colors.swift */; }; B5C100E01CB158D900FAD7E8 /* CarePlanStoreManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C100DF1CB158D900FAD7E8 /* CarePlanStoreManager.swift */; }; B5C100E41CB27B5F00FAD7E8 /* Activity.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C100E31CB27B5F00FAD7E8 /* Activity.swift */; }; B5C100EB1CB2C9FE00FAD7E8 /* QueryActivityEventsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C100EA1CB2C9FE00FAD7E8 /* QueryActivityEventsOperation.swift */; }; + BA965B281DC6D3200066EAC6 /* Glyph.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA965B271DC6D3200066EAC6 /* Glyph.swift */; }; + BADD6DC81DC5428C008E7893 /* InterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18214FD51D3D9C8000AE765B /* InterfaceController.swift */; }; + BADD6DCA1DC542CF008E7893 /* RootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C100D41CB1475900FAD7E8 /* RootViewController.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -80,13 +81,6 @@ remoteGlobalIDString = 86CC8E9A1AC09332001CCD89; remoteInfo = ResearchKitTests; }; - 2436E4361CD18BFD00ABE381 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 2436E42A1CD18BEC00ABE381 /* ResearchKit.xcodeproj */; - proxyType = 1; - remoteGlobalIDString = B183A4731A8535D100C76870; - remoteInfo = ResearchKit; - }; B55565701CC82F8D00513644 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = B5C100AE1CB12F0C00FAD7E8 /* CareKit.xcodeproj */; @@ -139,8 +133,8 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 2436E4351CD18BFD00ABE381 /* ResearchKit.framework in Embed Frameworks */, B55565781CC82FCA00513644 /* CareKit.framework in Embed Frameworks */, + 247E67891E53DCD30045D28B /* ResearchKit.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -165,6 +159,7 @@ 18FB874E1D3F106200D2A06D /* ActivityRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityRow.swift; sourceTree = ""; }; 24026C661D6D244A007582F3 /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = System/Library/Frameworks/HealthKit.framework; sourceTree = SDKROOT; }; 2436E42A1CD18BEC00ABE381 /* ResearchKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ResearchKit.xcodeproj; path = ../dependency/ResearchKit.xcodeproj; sourceTree = ""; }; + 247E67871E53DCD30045D28B /* ResearchKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ResearchKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; B555759B1CB3DB8C0028DC37 /* NSCalendar+Dates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSCalendar+Dates.swift"; sourceTree = ""; }; B555759D1CB3DBBE0028DC37 /* OCKInsightItem+EmptyMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OCKInsightItem+EmptyMessage.swift"; sourceTree = ""; }; B56848B51CB5085E00D190EE /* InsightsBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InsightsBuilder.swift; sourceTree = ""; }; @@ -192,6 +187,7 @@ B5C100DF1CB158D900FAD7E8 /* CarePlanStoreManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CarePlanStoreManager.swift; sourceTree = ""; }; B5C100E31CB27B5F00FAD7E8 /* Activity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Activity.swift; sourceTree = ""; }; B5C100EA1CB2C9FE00FAD7E8 /* QueryActivityEventsOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryActivityEventsOperation.swift; sourceTree = ""; }; + BA965B271DC6D3200066EAC6 /* Glyph.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Glyph.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -207,7 +203,7 @@ buildActionMask = 2147483647; files = ( 24026C671D6D244A007582F3 /* HealthKit.framework in Frameworks */, - 2436E4341CD18BFD00ABE381 /* ResearchKit.framework in Frameworks */, + 247E67881E53DCD30045D28B /* ResearchKit.framework in Frameworks */, B55565771CC82FCA00513644 /* CareKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -330,6 +326,7 @@ B5C100841CB12E0C00FAD7E8 = { isa = PBXGroup; children = ( + 247E67871E53DCD30045D28B /* ResearchKit.framework */, B5C1008F1CB12E0C00FAD7E8 /* OCKSample */, 24E285101D74AE0A00F840E1 /* Watch */, B555657F1CC82FEF00513644 /* Linked frameworks */, @@ -355,6 +352,7 @@ B5C100D41CB1475900FAD7E8 /* RootViewController.swift */, B5C100DF1CB158D900FAD7E8 /* CarePlanStoreManager.swift */, B5C100DB1CB1547600FAD7E8 /* SampleData.swift */, + BA965B201DC6D1210066EAC6 /* Glyph */, B58A3FAD1CC81E5D009CF1B9 /* Activities */, B58A3FB71CC81F19009CF1B9 /* Assessments */, B5C100E71CB2C99600FAD7E8 /* Insights */, @@ -379,6 +377,14 @@ name = Insights; sourceTree = ""; }; + BA965B201DC6D1210066EAC6 /* Glyph */ = { + isa = PBXGroup; + children = ( + BA965B271DC6D3200066EAC6 /* Glyph.swift */, + ); + name = Glyph; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -430,7 +436,6 @@ ); dependencies = ( B555657A1CC82FCA00513644 /* PBXTargetDependency */, - 2436E4371CD18BFD00ABE381 /* PBXTargetDependency */, 18214FDF1D3D9C8000AE765B /* PBXTargetDependency */, ); name = OCKSample; @@ -566,8 +571,8 @@ files = ( 18214FD81D3D9C8000AE765B /* ExtensionDelegate.swift in Sources */, 18FB874F1D3F106200D2A06D /* ActivityRow.swift in Sources */, - 18214FD61D3D9C8000AE765B /* InterfaceController.swift in Sources */, 18FB87471D3ED97200D2A06D /* WCKActivity.swift in Sources */, + BADD6DC81DC5428C008E7893 /* InterfaceController.swift in Sources */, 18214FDA1D3D9C8000AE765B /* ComplicationController.swift in Sources */, 18B2F9021D4973B7005FFBC6 /* EventRow.swift in Sources */, 18FB87491D3ED97A00D2A06D /* WCKEvent.swift in Sources */, @@ -595,8 +600,9 @@ B5C100DE1CB1556F00FAD7E8 /* Colors.swift in Sources */, B56848B61CB5085E00D190EE /* InsightsBuilder.swift in Sources */, B5C100DC1CB1547600FAD7E8 /* SampleData.swift in Sources */, + BADD6DCA1DC542CF008E7893 /* RootViewController.swift in Sources */, + BA965B281DC6D3200066EAC6 /* Glyph.swift in Sources */, B58A3FBD1CC81FF9009CF1B9 /* Mood.swift in Sources */, - B5C100D51CB1475900FAD7E8 /* RootViewController.swift in Sources */, 1801613F1D4E778F0036E247 /* WatchConnectivityManager.swift in Sources */, B5C01D521CB3BC2D000728D9 /* BuildInsightsOperation.swift in Sources */, ); @@ -615,11 +621,6 @@ target = 18214FC31D3D9C8000AE765B /* OCKSampleWatch */; targetProxy = 18214FDE1D3D9C8000AE765B /* PBXContainerItemProxy */; }; - 2436E4371CD18BFD00ABE381 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = ResearchKit; - targetProxy = 2436E4361CD18BFD00ABE381 /* PBXContainerItemProxy */; - }; B555657A1CC82FCA00513644 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = CareKit; @@ -670,6 +671,7 @@ ASSETCATALOG_NOTICES = NO; ASSETCATALOG_WARNINGS = NO; CODE_SIGN_IDENTITY = "iPhone Developer"; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "OCKSampleWatch Extension/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.CareKit.watchkitapp.watchkitextension"; @@ -678,7 +680,7 @@ SKIP_INSTALL = YES; SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = 4; - WATCHOS_DEPLOYMENT_TARGET = 2.2; + WATCHOS_DEPLOYMENT_TARGET = 3.0; }; name = Debug; }; @@ -689,6 +691,7 @@ ASSETCATALOG_NOTICES = NO; ASSETCATALOG_WARNINGS = NO; CODE_SIGN_IDENTITY = "iPhone Developer"; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "OCKSampleWatch Extension/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.CareKit.watchkitapp.watchkitextension"; @@ -698,7 +701,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = 4; - WATCHOS_DEPLOYMENT_TARGET = 2.2; + WATCHOS_DEPLOYMENT_TARGET = 3.0; }; name = Release; }; @@ -709,6 +712,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=watchos*]" = "iPhone Developer"; + DEVELOPMENT_TEAM = ""; IBSC_MODULE = OCKSampleWatch_Extension; INFOPLIST_FILE = OCKSampleWatch/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.CareKit.watchkitapp"; @@ -718,7 +722,7 @@ SKIP_INSTALL = YES; SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = 4; - WATCHOS_DEPLOYMENT_TARGET = 2.2; + WATCHOS_DEPLOYMENT_TARGET = 3.0; }; name = Debug; }; @@ -729,6 +733,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=watchos*]" = "iPhone Developer"; + DEVELOPMENT_TEAM = ""; IBSC_MODULE = OCKSampleWatch_Extension; INFOPLIST_FILE = OCKSampleWatch/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.CareKit.watchkitapp"; @@ -738,7 +743,7 @@ SKIP_INSTALL = YES; SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = 4; - WATCHOS_DEPLOYMENT_TARGET = 2.2; + WATCHOS_DEPLOYMENT_TARGET = 3.0; }; name = Release; }; @@ -833,10 +838,10 @@ B5C100A21CB12E0C00FAD7E8 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = OCKSample/OCKSample.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = OCKSample/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.CareKit"; @@ -849,10 +854,10 @@ B5C100A31CB12E0C00FAD7E8 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = OCKSample/OCKSample.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = OCKSample/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.CareKit"; diff --git a/Sample/OCKSample/AppDelegate.swift b/Sample/OCKSample/AppDelegate.swift index 8d5e55e73..fc503c029 100644 --- a/Sample/OCKSample/AppDelegate.swift +++ b/Sample/OCKSample/AppDelegate.swift @@ -29,6 +29,7 @@ */ import UIKit +import CareKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { @@ -36,7 +37,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool { - window?.tintColor = Colors.red.color + window?.tintColor = OCKColor.red return true } } diff --git a/Sample/OCKSample/Assessment.swift b/Sample/OCKSample/Assessment.swift index ab8a1b29a..7b6c2bbd3 100644 --- a/Sample/OCKSample/Assessment.swift +++ b/Sample/OCKSample/Assessment.swift @@ -52,10 +52,10 @@ extension Assessment { // Determine what type of result should be saved. if let scaleResult = stepResult as? ORKScaleQuestionResult, let answer = scaleResult.scaleAnswer { - return OCKCarePlanEventResult(valueString: answer.stringValue, unitString: "out of 10", userInfo: nil) + return OCKCarePlanEventResult(valueString: answer.stringValue, unitString: "of 10", userInfo: nil, values: [answer]) } else if let numericResult = stepResult as? ORKNumericQuestionResult, let answer = numericResult.numericAnswer { - return OCKCarePlanEventResult(valueString: answer.stringValue, unitString: numericResult.unit, userInfo: nil) + return OCKCarePlanEventResult(valueString: answer.stringValue, unitString: numericResult.unit!, userInfo: nil, values: [answer]) } fatalError("Unexpected task result type") diff --git a/Sample/OCKSample/Assets.xcassets/connect-filled.imageset/Connect-ON@1x.png b/Sample/OCKSample/Assets.xcassets/connect-filled.imageset/Connect-ON@1x.png deleted file mode 100644 index 67c34c957..000000000 Binary files a/Sample/OCKSample/Assets.xcassets/connect-filled.imageset/Connect-ON@1x.png and /dev/null differ diff --git a/Sample/OCKSample/Assets.xcassets/connect-filled.imageset/Connect-ON@2x.png b/Sample/OCKSample/Assets.xcassets/connect-filled.imageset/Connect-ON@2x.png index fa8c1d296..1222b4191 100644 Binary files a/Sample/OCKSample/Assets.xcassets/connect-filled.imageset/Connect-ON@2x.png and b/Sample/OCKSample/Assets.xcassets/connect-filled.imageset/Connect-ON@2x.png differ diff --git a/Sample/OCKSample/Assets.xcassets/connect-filled.imageset/Connect-ON@3x.png b/Sample/OCKSample/Assets.xcassets/connect-filled.imageset/Connect-ON@3x.png index a1ce8bb3c..553fc1ae7 100644 Binary files a/Sample/OCKSample/Assets.xcassets/connect-filled.imageset/Connect-ON@3x.png and b/Sample/OCKSample/Assets.xcassets/connect-filled.imageset/Connect-ON@3x.png differ diff --git a/Sample/OCKSample/Assets.xcassets/connect-filled.imageset/Contents.json b/Sample/OCKSample/Assets.xcassets/connect-filled.imageset/Contents.json index 59e422e1f..31616a14b 100644 --- a/Sample/OCKSample/Assets.xcassets/connect-filled.imageset/Contents.json +++ b/Sample/OCKSample/Assets.xcassets/connect-filled.imageset/Contents.json @@ -2,7 +2,6 @@ "images" : [ { "idiom" : "universal", - "filename" : "Connect-ON@1x.png", "scale" : "1x" }, { diff --git a/Sample/OCKSample/Assets.xcassets/connect.imageset/Connect-OFF@1x.png b/Sample/OCKSample/Assets.xcassets/connect.imageset/Connect-OFF@1x.png deleted file mode 100644 index 058d051e3..000000000 Binary files a/Sample/OCKSample/Assets.xcassets/connect.imageset/Connect-OFF@1x.png and /dev/null differ diff --git a/Sample/OCKSample/Assets.xcassets/connect.imageset/Connect-OFF@2x.png b/Sample/OCKSample/Assets.xcassets/connect.imageset/Connect-OFF@2x.png index 7a3940bb8..c31719b9e 100644 Binary files a/Sample/OCKSample/Assets.xcassets/connect.imageset/Connect-OFF@2x.png and b/Sample/OCKSample/Assets.xcassets/connect.imageset/Connect-OFF@2x.png differ diff --git a/Sample/OCKSample/Assets.xcassets/connect.imageset/Connect-OFF@3x.png b/Sample/OCKSample/Assets.xcassets/connect.imageset/Connect-OFF@3x.png index e5afa0181..95a8b8d3c 100644 Binary files a/Sample/OCKSample/Assets.xcassets/connect.imageset/Connect-OFF@3x.png and b/Sample/OCKSample/Assets.xcassets/connect.imageset/Connect-OFF@3x.png differ diff --git a/Sample/OCKSample/Assets.xcassets/connect.imageset/Contents.json b/Sample/OCKSample/Assets.xcassets/connect.imageset/Contents.json index cd749be48..aabd5a676 100644 --- a/Sample/OCKSample/Assets.xcassets/connect.imageset/Contents.json +++ b/Sample/OCKSample/Assets.xcassets/connect.imageset/Contents.json @@ -2,7 +2,6 @@ "images" : [ { "idiom" : "universal", - "filename" : "Connect-OFF@1x.png", "scale" : "1x" }, { diff --git a/Sample/OCKSample/BackPain.swift b/Sample/OCKSample/BackPain.swift index 0944622d2..3c7d6b542 100644 --- a/Sample/OCKSample/BackPain.swift +++ b/Sample/OCKSample/BackPain.swift @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -44,6 +44,7 @@ struct BackPain: Assessment { // Create a weekly schedule. let startDate = DateComponents(year: 2016, month: 01, day: 01) let schedule = OCKCareSchedule.weeklySchedule(withStartDate: startDate as DateComponents, occurrencesOnEachDay: [1, 1, 1, 1, 1, 1, 1]) + let thresholds : [OCKCarePlanThreshold] = Array([OCKCarePlanThreshold.numericThreshold(withValue: NSNumber.init(value: 7), type: .numericGreaterThan, upperValue: nil, title: "High pain level. Please call your doctor."), OCKCarePlanThreshold.numericThreshold(withValue: NSNumber.init(value: 2), type: .numericLessThanOrEqual, upperValue: nil, title: "Very little pain.")]) // Get the localized strings to use for the assessment. let title = NSLocalizedString("Pain", comment: "") @@ -57,7 +58,9 @@ struct BackPain: Assessment { tintColor: Colors.blue.color, resultResettable: true, schedule: schedule, - userInfo: nil + userInfo: nil, + thresholds: [thresholds], + optional: false ) return activity diff --git a/Sample/OCKSample/Base.lproj/LaunchScreen.storyboard b/Sample/OCKSample/Base.lproj/LaunchScreen.storyboard index da7ced23b..e293c22c8 100644 --- a/Sample/OCKSample/Base.lproj/LaunchScreen.storyboard +++ b/Sample/OCKSample/Base.lproj/LaunchScreen.storyboard @@ -1,7 +1,12 @@ - - + + + + + - + + + @@ -29,16 +34,16 @@ - + - - + + - + diff --git a/Sample/OCKSample/Base.lproj/Main.storyboard b/Sample/OCKSample/Base.lproj/Main.storyboard index 4a30064ba..719f7f330 100644 --- a/Sample/OCKSample/Base.lproj/Main.storyboard +++ b/Sample/OCKSample/Base.lproj/Main.storyboard @@ -1,7 +1,12 @@ - + + + + - + + + @@ -12,7 +17,7 @@ - + diff --git a/Sample/OCKSample/BloodGlucose.swift b/Sample/OCKSample/BloodGlucose.swift index 5369f0bfb..3c0d99def 100644 --- a/Sample/OCKSample/BloodGlucose.swift +++ b/Sample/OCKSample/BloodGlucose.swift @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -44,6 +44,7 @@ struct BloodGlucose: Assessment { // Create a weekly schedule. let startDate = DateComponents(year: 2016, month: 01, day: 01) let schedule = OCKCareSchedule.weeklySchedule(withStartDate: startDate as DateComponents, occurrencesOnEachDay: [1, 1, 1, 1, 1, 1, 1]) + let thresholds = [OCKCarePlanThreshold.numericThreshold(withValue: NSNumber.init(value: 70), type: .numericRangeInclusive, upperValue: NSNumber.init(value: 100), title: "Healthy blood glucose."), OCKCarePlanThreshold.numericThreshold(withValue: NSNumber.init(value: 180), type: .numericGreaterThanOrEqual, upperValue: nil, title: "High blood glucose.")] as Array; // Get the localized strings to use for the assessment. let title = NSLocalizedString("Blood Glucose", comment: "") @@ -57,7 +58,9 @@ struct BloodGlucose: Assessment { tintColor: Colors.purple.color, resultResettable: false, schedule: schedule, - userInfo: nil + userInfo: nil, + thresholds: [thresholds], + optional: false ) return activity diff --git a/Sample/OCKSample/BuildInsightsOperation.swift b/Sample/OCKSample/BuildInsightsOperation.swift index 779fcbd21..848344bfc 100644 --- a/Sample/OCKSample/BuildInsightsOperation.swift +++ b/Sample/OCKSample/BuildInsightsOperation.swift @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/Sample/OCKSample/CarePlanStoreManager.swift b/Sample/OCKSample/CarePlanStoreManager.swift index deac36668..9f5a9e7dd 100644 --- a/Sample/OCKSample/CarePlanStoreManager.swift +++ b/Sample/OCKSample/CarePlanStoreManager.swift @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -88,7 +88,6 @@ class CarePlanStoreManager: NSObject { } - extension CarePlanStoreManager: OCKCarePlanStoreDelegate { func carePlanStoreActivityListDidChange(_ store: OCKCarePlanStore) { updateInsights() @@ -96,11 +95,20 @@ extension CarePlanStoreManager: OCKCarePlanStoreDelegate { func carePlanStore(_ store: OCKCarePlanStore, didReceiveUpdateOf event: OCKCarePlanEvent) { updateInsights() + + let triggeredThresholds = event.evaluateNumericThresholds() + if triggeredThresholds.count != 0 { + for thresholdArray in triggeredThresholds { + for threshold in thresholdArray { + NSLog("Threshold triggered on event \(event.occurrenceIndexOfDay) of \(event.date) for activity \(event.activity.identifier) with title:\n\(threshold.title!)") + } + } + } + } } - protocol CarePlanStoreManagerDelegate: class { func carePlanStoreManager(_ manager: CarePlanStoreManager, didUpdateInsights insights: [OCKInsightItem]) diff --git a/Sample/OCKSample/Colors.swift b/Sample/OCKSample/Colors.swift index b1e05f74d..42796ee49 100644 --- a/Sample/OCKSample/Colors.swift +++ b/Sample/OCKSample/Colors.swift @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/Sample/OCKSample/Glyph.swift b/Sample/OCKSample/Glyph.swift new file mode 100644 index 000000000..fd75175ed --- /dev/null +++ b/Sample/OCKSample/Glyph.swift @@ -0,0 +1,206 @@ +// +// Glyph.swift +// OCKSample +// +// Created by Akshay on 10/30/16. +// Copyright © 2017 Apple. All rights reserved. +// + +import UIKit + +class Glyph: NSObject { + + + enum glyphType: Int { + case GlyphTypeHeart = 0 + + case GlyphTypeAccessibility + + case GlyphTypeActiveLife + + case GlyphTypeAdultLearning + + case GlyphTypeAwareness + + case GlyphTypeBlood + + case GlyphTypeBloodPressure + + case GlyphTypeCardio + + case GlyphTypeChildLearning + + case GlyphTypeDentalHealth + + case GlyphTypeFemaleHealth + + case GlyphTypeHearing + + case GlyphTypeHomeCare + + case GlyphTypeInfantCare + + case GlyphTypeLaboratory + + case GlyphTypeMaleHealth + + case GlyphTypeMaternalHealth + + case GlyphTypeMedication + + case GlyphTypeMentalHealth + + case GlyphTypeNeuro + + case GlyphTypeNutrition + + case GlyphTypeOptometry + + case GlyphTypePediatrics + + case GlyphTypePhysicalTherapy + + case GlyphTypePodiatry + + case GlyphTypeRespiratoryHealth + + case GlyphTypeScale + + case GlyphTypeStethoscope + + case GlyphTypeSyringe + + case GlyphTypeCustom + + } + + class func imageNameForGlyphType(glyphType: glyphType) -> String { + + var imageName = "" + + switch glyphType { + + case .GlyphTypeHeart : + imageName = "Heart" + break + + case .GlyphTypeAccessibility : + imageName = "Accessibility" + break + + case .GlyphTypeActiveLife : + imageName = "ActiveLife" + break + + case .GlyphTypeAdultLearning : + imageName = "AdultLearning" + break + + case .GlyphTypeAwareness : + imageName = "Awareness" + break + + case .GlyphTypeBlood : + imageName = "Blood" + break + + case .GlyphTypeBloodPressure : + imageName = "BloodPressure" + break + + case .GlyphTypeCardio : + imageName = "Cardio" + break + + case .GlyphTypeChildLearning : + imageName = "ChildLearning" + break + + case .GlyphTypeDentalHealth : + imageName = "DentalHealth" + break + + case .GlyphTypeFemaleHealth : + imageName = "FemaleHealth" + break + + case .GlyphTypeHearing : + imageName = "Hearing" + break + + case .GlyphTypeHomeCare : + imageName = "HomeCare" + break + + case .GlyphTypeInfantCare : + imageName = "InfantCare" + break + + case .GlyphTypeLaboratory : + imageName = "Laboratory" + break + + case .GlyphTypeMaleHealth : + imageName = "MaleHealth" + break + + case .GlyphTypeMaternalHealth : + imageName = "MaternalHealth" + break + + case .GlyphTypeMedication : + imageName = "Medication" + break + + case .GlyphTypeMentalHealth : + imageName = "MentalHealth" + break + + case .GlyphTypeNeuro : + imageName = "Neuro" + break + + case .GlyphTypeNutrition : + imageName = "Nutrition" + break + + case .GlyphTypeOptometry : + imageName = "Optometry" + break + + case .GlyphTypePediatrics : + imageName = "Pediatrics" + break + + case .GlyphTypePhysicalTherapy : + imageName = "PhysicalTherapy" + break + + case .GlyphTypePodiatry : + imageName = "Podiatry" + break + + case .GlyphTypeRespiratoryHealth : + imageName = "RespiratoryHealth" + break + + case .GlyphTypeScale : + imageName = "Scale" + break + + case .GlyphTypeStethoscope : + imageName = "Stethoscope" + break + + case .GlyphTypeSyringe : + imageName = "Syringe" + break + + case .GlyphTypeCustom : + imageName = "Custom" + break + } + + return imageName + } +} diff --git a/Sample/OCKSample/HamstringStretch.swift b/Sample/OCKSample/HamstringStretch.swift index ce7654d02..41015c42b 100644 --- a/Sample/OCKSample/HamstringStretch.swift +++ b/Sample/OCKSample/HamstringStretch.swift @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -52,14 +52,15 @@ struct HamstringStretch: Activity { // Create the intervention activity. let activity = OCKCarePlanActivity.intervention( withIdentifier: activityType.rawValue, - groupIdentifier: nil, + groupIdentifier: "Physical Activity", title: title, text: summary, tintColor: Colors.blue.color, instructions: instructions, imageURL: nil, schedule: schedule, - userInfo: nil + userInfo: nil, + optional: false ) return activity diff --git a/Sample/OCKSample/Info.plist b/Sample/OCKSample/Info.plist index e55a58fdf..373081dc1 100644 --- a/Sample/OCKSample/Info.plist +++ b/Sample/OCKSample/Info.plist @@ -4,6 +4,8 @@ CFBundleDevelopmentRegion en + CFBundleDisplayName + Care CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -15,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0 + 1.2 CFBundleSignature ???? CFBundleVersion @@ -50,6 +52,7 @@ UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortraitUpsideDown diff --git a/Sample/OCKSample/Mood.swift b/Sample/OCKSample/Mood.swift index c78e936ca..8005d862e 100644 --- a/Sample/OCKSample/Mood.swift +++ b/Sample/OCKSample/Mood.swift @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -44,6 +44,7 @@ struct Mood: Assessment { // Create a weekly schedule. let startDate = DateComponents(year: 2016, month: 01, day: 01) let schedule = OCKCareSchedule.weeklySchedule(withStartDate: startDate as DateComponents, occurrencesOnEachDay: [1, 1, 1, 1, 1, 1, 1]) + let thresholds = [OCKCarePlanThreshold.numericThreshold(withValue: NSNumber.init(value: 7), type: .numericGreaterThan, upperValue: nil, title: "Good mood."), OCKCarePlanThreshold.numericThreshold(withValue: NSNumber.init(value: 3), type: .numericLessThanOrEqual, upperValue: nil, title: "Bad mood.")] as Array // Get the localized strings to use for the assessment. let title = NSLocalizedString("Mood", comment: "") @@ -56,7 +57,9 @@ struct Mood: Assessment { tintColor: Colors.green.color, resultResettable: false, schedule: schedule, - userInfo: nil + userInfo: nil, + thresholds: [thresholds], + optional: false ) return activity diff --git a/Sample/OCKSample/OutdoorWalk.swift b/Sample/OCKSample/OutdoorWalk.swift index 285a43144..251cb41aa 100644 --- a/Sample/OCKSample/OutdoorWalk.swift +++ b/Sample/OCKSample/OutdoorWalk.swift @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -52,14 +52,15 @@ struct OutdoorWalk: Activity { // Create the intervention activity. let activity = OCKCarePlanActivity.intervention( withIdentifier: activityType.rawValue, - groupIdentifier: nil, + groupIdentifier: "Physical Activity", title: title, text: summary, tintColor: Colors.purple.color, instructions: instructions, imageURL: nil, schedule: schedule, - userInfo: nil + userInfo: nil, + optional: false ) return activity diff --git a/Sample/OCKSample/QueryActivityEventsOperation.swift b/Sample/OCKSample/QueryActivityEventsOperation.swift index b8a9c6e1f..20a341af9 100644 --- a/Sample/OCKSample/QueryActivityEventsOperation.swift +++ b/Sample/OCKSample/QueryActivityEventsOperation.swift @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -70,14 +70,16 @@ class QueryActivityEventsOperation: Operation { // Query for events for the activity between the requested dates. self.dailyEvents = DailyEvents() - store.enumerateEvents(of: activity, startDate: startDate as DateComponents, endDate: endDate as DateComponents, handler: { event, _ in - if let event = event { - self.dailyEvents?[event.date].append(event) - } - }, completion: { _, _ in - // Use the semaphore to signal that the query is complete. - semaphore.signal() - }) + DispatchQueue.main.async { + self.store.enumerateEvents(of: activity, startDate: self.startDate as DateComponents, endDate: self.endDate as DateComponents, handler: { event, _ in + if let event = event { + self.dailyEvents?[event.date].append(event) + } + }, completion: { _, _ in + // Use the semaphore to signal that the query is complete. + semaphore.signal() + }) + } // Wait for the semaphore to be signalled. _ = semaphore.wait(timeout: DispatchTime.distantFuture) diff --git a/Sample/OCKSample/RootViewController.swift b/Sample/OCKSample/RootViewController.swift index e979cf3f7..44161a7be 100644 --- a/Sample/OCKSample/RootViewController.swift +++ b/Sample/OCKSample/RootViewController.swift @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -40,77 +40,95 @@ class RootViewController: UITabBarController { fileprivate let storeManager = CarePlanStoreManager.sharedCarePlanStoreManager - fileprivate var careCardViewController: OCKCareCardViewController! - - fileprivate var symptomTrackerViewController: OCKSymptomTrackerViewController! + fileprivate var careContentsViewController: OCKCareContentsViewController! fileprivate var insightsViewController: OCKInsightsViewController! fileprivate var connectViewController: OCKConnectViewController! fileprivate var watchManager: WatchConnectivityManager? + + // MARK: Initialization required init?(coder aDecoder: NSCoder) { sampleData = SampleData(carePlanStore: storeManager.store) super.init(coder: aDecoder) - - careCardViewController = createCareCardViewController() - symptomTrackerViewController = createSymptomTrackerViewController() + careContentsViewController = createCareContentsViewController() insightsViewController = createInsightsViewController() connectViewController = createConnectViewController() self.viewControllers = [ - UINavigationController(rootViewController: careCardViewController), - UINavigationController(rootViewController: symptomTrackerViewController), + UINavigationController(rootViewController: careContentsViewController), UINavigationController(rootViewController: insightsViewController), UINavigationController(rootViewController: connectViewController) ] storeManager.delegate = self watchManager = WatchConnectivityManager(withStore: storeManager.store) + let glyphType = Glyph.glyphType(rawValue: careContentsViewController.glyphType.rawValue) + + // Default the default glyph tint color + + var glyphTintColor = OCKGlyph.defaultColor(for: careContentsViewController.glyphType) + if (careContentsViewController.glyphTintColor != nil) { + glyphTintColor = careContentsViewController.glyphTintColor + } + + // Create color component array + let glyphTintColorComponents = glyphTintColor.cgColor.components + let glyphTintColorArray = [glyphTintColorComponents![0], glyphTintColorComponents![1], glyphTintColorComponents![2], glyphTintColorComponents![3]] + watchManager?.glyphType = Glyph.imageNameForGlyphType(glyphType: glyphType!) + watchManager?.glyphTintColor = glyphTintColorArray + + // Set the custom image name if the glyphType is custom + if (careContentsViewController.glyphType == .custom) { + let glyphImageName = careContentsViewController.customGlyphImageName + if (glyphImageName != "") { + watchManager?.glyphImageName = glyphImageName + } + + watchManager?.sendGlyphType(glyphType: Glyph.imageNameForGlyphType(glyphType: glyphType!), + glyphTintColor: glyphTintColorArray, + glyphImageName: glyphImageName) + } else { + watchManager?.sendGlyphType(glyphType: Glyph.imageNameForGlyphType(glyphType: glyphType!), glyphTintColor: glyphTintColorArray) + } } - + // MARK: Convenience fileprivate func createInsightsViewController() -> OCKInsightsViewController { // Create an `OCKInsightsViewController` with sample data. - let headerTitle = NSLocalizedString("Weekly Charts", comment: "") - let viewController = OCKInsightsViewController(insightItems: storeManager.insights, headerTitle: headerTitle, headerSubtitle: "") + let activityType1: ActivityType = .backPain + let activityType2: ActivityType = .bloodGlucose + let activityType3: ActivityType = .weight + let widget1 = OCKPatientWidget.defaultWidget(withActivityIdentifier: activityType1.rawValue, tintColor: OCKColor.red) + let widget2 = OCKPatientWidget.defaultWidget(withActivityIdentifier: activityType2.rawValue, tintColor: OCKColor.red) + let widget3 = OCKPatientWidget.defaultWidget(withActivityIdentifier: activityType3.rawValue, tintColor: OCKColor.red) + + let viewController = OCKInsightsViewController(insightItems: storeManager.insights, patientWidgets: [widget1, widget2, widget3], thresholds: [activityType1.rawValue], store:storeManager.store) // Setup the controller's title and tab bar item viewController.title = NSLocalizedString("Insights", comment: "") viewController.tabBarItem = UITabBarItem(title: viewController.title, image: UIImage(named:"insights"), selectedImage: UIImage(named: "insights-filled")) - return viewController - } + return viewController } - fileprivate func createCareCardViewController() -> OCKCareCardViewController { - let viewController = OCKCareCardViewController(carePlanStore: storeManager.store) - - // Setup the controller's title and tab bar item - viewController.title = NSLocalizedString("Care Card", comment: "") + fileprivate func createCareContentsViewController() -> OCKCareContentsViewController { + let viewController = OCKCareContentsViewController(carePlanStore: storeManager.store) + viewController.title = NSLocalizedString("Care Contents", comment: "") viewController.tabBarItem = UITabBarItem(title: viewController.title, image: UIImage(named:"carecard"), selectedImage: UIImage(named: "carecard-filled")) - - return viewController - } - - fileprivate func createSymptomTrackerViewController() -> OCKSymptomTrackerViewController { - let viewController = OCKSymptomTrackerViewController(carePlanStore: storeManager.store) - viewController.delegate = self - - // Setup the controller's title and tab bar item - viewController.title = NSLocalizedString("Symptom Tracker", comment: "") - viewController.tabBarItem = UITabBarItem(title: viewController.title, image: UIImage(named:"symptoms"), selectedImage: UIImage(named: "symptoms-filled")) - + viewController.delegate = self; return viewController + } - + fileprivate func createConnectViewController() -> OCKConnectViewController { - let viewController = OCKConnectViewController(contacts: sampleData.contacts) + let viewController = OCKConnectViewController.init(contacts: sampleData.contacts, patient: sampleData.patient) viewController.delegate = self - + viewController.dataSource = self // Setup the controller's title and tab bar item viewController.title = NSLocalizedString("Connect", comment: "") viewController.tabBarItem = UITabBarItem(title: viewController.title, image: UIImage(named:"connect"), selectedImage: UIImage(named: "connect-filled")) @@ -120,33 +138,29 @@ class RootViewController: UITabBarController { } - -extension RootViewController: OCKSymptomTrackerViewControllerDelegate { +extension RootViewController: OCKCareContentsViewControllerDelegate { - /// Called when the user taps an assessment on the `OCKSymptomTrackerViewController`. - func symptomTrackerViewController(_ viewController: OCKSymptomTrackerViewController, didSelectRowWithAssessmentEvent assessmentEvent: OCKCarePlanEvent) { - // Lookup the assessment the row represents. - guard let activityType = ActivityType(rawValue: assessmentEvent.activity.identifier) else { return } - guard let sampleAssessment = sampleData.activityWithType(activityType) as? Assessment else { return } - - /* - Check if we should show a task for the selected assessment event - based on its state. - */ - guard assessmentEvent.state == .initial || - assessmentEvent.state == .notCompleted || - (assessmentEvent.state == .completed && assessmentEvent.activity.resultResettable) else { return } - - // Show an `ORKTaskViewController` for the assessment's task. - let taskViewController = ORKTaskViewController(task: sampleAssessment.task(), taskRun: nil) - taskViewController.delegate = self - - present(taskViewController, animated: true, completion: nil) + func careContentsViewController(_ viewController: OCKCareContentsViewController, didSelectRowWithAssessmentEvent assessmentEvent: OCKCarePlanEvent) { + // Lookup the assessment the row represents. + guard let activityType = ActivityType(rawValue: assessmentEvent.activity.identifier) else { return } + guard let sampleAssessment = sampleData.activityWithType(activityType) as? Assessment else { return } + + /* + Check if we should show a task for the selected assessment event + based on its state. + */ + guard assessmentEvent.state == .initial || + assessmentEvent.state == .notCompleted || + (assessmentEvent.state == .completed && assessmentEvent.activity.resultResettable) else { return } + + // Show an `ORKTaskViewController` for the assessment's task. + let taskViewController = ORKTaskViewController(task: sampleAssessment.task(), taskRun: nil) + taskViewController.delegate = self + + present(taskViewController, animated: true, completion: nil) } } - - extension RootViewController: ORKTaskViewControllerDelegate { /// Called with then user completes a presented `ORKTaskViewController`. @@ -159,7 +173,7 @@ extension RootViewController: ORKTaskViewControllerDelegate { guard reason == .completed else { return } // Determine the event that was completed and the `SampleAssessment` it represents. - guard let event = symptomTrackerViewController.lastSelectedAssessmentEvent, + guard let event = careContentsViewController.lastSelectedEvent, let activityType = ActivityType(rawValue: event.activity.identifier), let sampleAssessment = sampleData.activityWithType(activityType) as? Assessment else { return } @@ -225,12 +239,29 @@ extension RootViewController: ORKTaskViewControllerDelegate { fileprivate func completeEvent(_ event: OCKCarePlanEvent, inStore store: OCKCarePlanStore, withResult result: OCKCarePlanEventResult) { store.update(event, with: result, state: .completed) { success, _, error in if !success { - print(error?.localizedDescription as Any) + print(error!.localizedDescription) } } } } +// MARK: OCKConnectViewControllerDataSource + +extension RootViewController: OCKConnectViewControllerDataSource { + + func connectViewControllerNumber(ofConnectMessageItems viewController: OCKConnectViewController, careTeamContact contact: OCKContact) -> Int { + return sampleData.connectMessageItems.count + } + + func connectViewControllerCareTeamConnections(_ viewController: OCKConnectViewController) -> [OCKContact] { + return sampleData.contactsWithMessageItems + } + + func connectViewController(_ viewController: OCKConnectViewController, connectMessageItemAt index: Int, careTeamContact contact: OCKContact) -> OCKConnectMessageItem { + return sampleData.connectMessageItems[index] + } +} + // MARK: OCKConnectViewControllerDelegate extension RootViewController: OCKConnectViewControllerDelegate { @@ -238,9 +269,12 @@ extension RootViewController: OCKConnectViewControllerDelegate { /// Called when the user taps a contact in the `OCKConnectViewController`. func connectViewController(_ connectViewController: OCKConnectViewController, didSelectShareButtonFor contact: OCKContact, presentationSourceView sourceView: UIView?) { let document = sampleData.generateSampleDocument() - let activityViewController = UIActivityViewController(activityItems: [document], applicationActivities: nil) - - present(activityViewController, animated: true, completion: nil) + document.createPDFData {(data, error) in + let activityViewController = UIActivityViewController(activityItems: [data], applicationActivities: nil) + DispatchQueue.main.async { + self.present(activityViewController, animated: true, completion: nil) + } + } } } diff --git a/Sample/OCKSample/SampleData.swift b/Sample/OCKSample/SampleData.swift index dd69fde77..2a42e9d79 100644 --- a/Sample/OCKSample/SampleData.swift +++ b/Sample/OCKSample/SampleData.swift @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -45,6 +45,11 @@ class SampleData: NSObject { BloodGlucose(), Weight() ] + /** + An `OCKPatient` object to assign contacts to. + */ + + var patient: OCKPatient /** An array of `OCKContact`s to display on the Connect view. @@ -53,7 +58,7 @@ class SampleData: NSObject { OCKContact(contactType: .careTeam, name: "Dr. Maria Ruiz", relation: "Physician", - contactInfoItems:[.phone("888-555-5512"), .sms("888-555-5512"), .email("mruiz2@mac.com")], + contactInfoItems: [OCKContactInfo.phone("888-555-5512"), OCKContactInfo.sms("888-555-5512"), OCKContactInfo.email("mruiz2@mac.com")], tintColor: Colors.blue.color, monogram: "MR", image: nil), @@ -61,23 +66,42 @@ class SampleData: NSObject { OCKContact(contactType: .careTeam, name: "Bill James", relation: "Nurse", - contactInfoItems:[.phone("888-555-5512"), .sms("888-555-5512"), .email("billjames2@mac.com")], + contactInfoItems: [OCKContactInfo.phone("888-555-5512"), OCKContactInfo.sms("888-555-5512"), OCKContactInfo.email("billjames2@mac.com")], tintColor: Colors.green.color, - monogram: nil, + monogram: "BJ", image: nil), OCKContact(contactType: .personal, name: "Tom Clark", relation: "Father", - contactInfoItems:[.phone("888-555-5512"), .sms("888-555-5512"), .facetimeVideo("8885555512", display: "888-555-5512")], + contactInfoItems: [OCKContactInfo.phone("888-555-5512"), OCKContactInfo.sms("888-555-5512")], tintColor: Colors.yellow.color, - monogram: nil, + monogram: "TC", image: nil) ] + /** + Connect message items + */ + + let dateString = DateFormatter.localizedString(from: Date(), dateStyle: .medium, timeStyle: .short) + var connectMessageItems = [OCKConnectMessageItem]() + var contactsWithMessageItems = [OCKContact]() + + // MARK: Initialization required init(carePlanStore: OCKCarePlanStore) { + self.patient = OCKPatient(identifier: "patient", carePlanStore: carePlanStore, name: "John Doe", detailInfo: nil, careTeamContacts: contacts, tintColor: Colors.lightBlue.color, monogram: "JD", image: nil, categories: nil, userInfo: ["Age": "21", "Gender": "M", "Phone":"888-555-5512"]) + + for contact in contacts { + if contact.type == .careTeam { + contactsWithMessageItems.append(contact) + self.connectMessageItems = [OCKConnectMessageItem(messageType: OCKConnectMessageType.sent, name: contact.name, message: "I am feeling good after taking the medication! Thank you.", dateString:dateString)] + break + } + } + super.init() // Populate the store with the sample activities. @@ -86,7 +110,7 @@ class SampleData: NSObject { carePlanStore.add(carePlanActivity) { success, error in if !success { - print(error?.localizedDescription as Any) + print(error!.localizedDescription) } } } diff --git a/Sample/OCKSample/TakeMedication.swift b/Sample/OCKSample/TakeMedication.swift index b4945e187..0293ac514 100644 --- a/Sample/OCKSample/TakeMedication.swift +++ b/Sample/OCKSample/TakeMedication.swift @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -51,14 +51,15 @@ struct TakeMedication: Activity { let activity = OCKCarePlanActivity.intervention( withIdentifier: activityType.rawValue, - groupIdentifier: nil, + groupIdentifier: "Medications", title: title, text: summary, tintColor: Colors.green.color, instructions: instructions, imageURL: nil, schedule: schedule, - userInfo: nil + userInfo: nil, + optional: true ) return activity diff --git a/Sample/OCKSample/WatchConnectivityManager.swift b/Sample/OCKSample/WatchConnectivityManager.swift index 8475120ff..7805daf7e 100644 --- a/Sample/OCKSample/WatchConnectivityManager.swift +++ b/Sample/OCKSample/WatchConnectivityManager.swift @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -36,10 +36,13 @@ class WatchConnectivityManager : NSObject { // MARK: Properties var store : OCKCarePlanStore var session : WCSession - + var glyphType: String = "Image Unavailable" + var customGlyphImage: UIImage = UIImage() + var glyphTintColor: [CGFloat] = [239.0, 68.0, 91.0, 0.0] + var glyphImageName: String = "Heart" + var eventUpdatesFromWatch = [String]() - // MARK: Initialization init(withStore store : OCKCarePlanStore) { @@ -61,7 +64,6 @@ class WatchConnectivityManager : NSObject { extension WatchConnectivityManager : OCKCarePlanStoreDelegate { func carePlanStore(_ store: OCKCarePlanStore, didReceiveUpdateOf event: OCKCarePlanEvent) { - if event.activity.type != .intervention { return } @@ -74,9 +76,15 @@ extension WatchConnectivityManager : OCKCarePlanStoreDelegate { let eventUpdateString = hashEventUpdate(event.activity.identifier, eventIndex: event.occurrenceIndexOfDay, state: event.state) if let hashIndex = eventUpdatesFromWatch.index(of: eventUpdateString) { eventUpdatesFromWatch.remove(at: hashIndex) + return } + self.session.transferCurrentComplicationUserInfo(["glyphType" : self.glyphType, + "glyphTintColor" : self.glyphTintColor, + "glyphImageName" : self.glyphImageName]) + try? self.session.updateApplicationContext(["glyphType" : self.glyphType]) + if eventIsToday { self.store.events(onDate: today, type: .intervention, completion: { (allEventsArray, errorOrNil) in let eventsCompleted = allEventsArray.map({$0.filter({$0.state == OCKCarePlanEventState.completed}).count}).reduce(0, +) @@ -90,11 +98,14 @@ extension WatchConnectivityManager : OCKCarePlanStoreDelegate { encoder.encode("updateEvent", forKey: "type") encoder.encode(self.parseEventToDictionary(event), forKey: "event") encoder.encode(Int64(completionPercentage), forKey: "currentCompletionPercentage") - + encoder.finishEncoding() self.session.sendMessageData(data as Data, replyHandler: nil, errorHandler: nil) } else { - try? self.session.updateApplicationContext(["currentCompletionPercentage" : completionPercentage, "eventsRemaining" : totalEvents - eventsCompleted]) + self.session.transferCurrentComplicationUserInfo(["glyphType" : self.glyphType, + "glyphTintColor" : self.glyphTintColor, + "glyphImageName" : self.glyphImageName]) + try? self.session.updateApplicationContext(["glyphType" : self.glyphType,"currentCompletionPercentage" : completionPercentage, "eventsRemaining" : totalEvents - eventsCompleted]) } }) } @@ -114,10 +125,50 @@ extension WatchConnectivityManager : OCKCarePlanStoreDelegate { let totalEvents = allEventsArray.map({$0.count}).reduce(0, +) let completionPercentage = round(Float(eventsCompleted) * 100.0 / Float(totalEvents)) - try? self.session.updateApplicationContext(["currentCompletionPercentage" : completionPercentage, "eventsRemaining" : totalEvents - eventsCompleted]) + try? self.session.updateApplicationContext(["glyphType" : self.glyphType, "currentCompletionPercentage" : completionPercentage, "eventsRemaining" : totalEvents - eventsCompleted]) + self.session.transferCurrentComplicationUserInfo(["glyphType" : self.glyphType, + "glyphTintColor" : self.glyphTintColor, + "glyphImageName" : self.glyphImageName]) + }) } } + + // transfer glyph type and tint color + func sendGlyphType(glyphType: String, glyphTintColor: [CGFloat]) { + + if self.session.isReachable { + let data = NSMutableData() + let encoder = NSKeyedArchiver(forWritingWith: data) + + encoder.encode(glyphType, forKey: "glyphType") + encoder.finishEncoding() + + self.session.sendMessageData(data as Data, replyHandler: nil, errorHandler: nil) + } else { + self.session.transferCurrentComplicationUserInfo(["glyphType" : glyphType, "glyphTintColor" : glyphTintColor]) + try? self.session.updateApplicationContext(["glyphType" : glyphType]) + } + } + + // transfer glyph type, tint color and image it is a custom glyph image type + func sendGlyphType(glyphType: String, glyphTintColor: [CGFloat], glyphImageName: String) { + + if self.session.isReachable { + let data = NSMutableData() + let encoder = NSKeyedArchiver(forWritingWith: data) + + encoder.encode(glyphType, forKey: "glyphType") + encoder.finishEncoding() + + self.session.sendMessageData(data as Data, replyHandler: nil, errorHandler: nil) + } else { + self.session.transferCurrentComplicationUserInfo(["glyphType" : glyphType, + "glyphTintColor" : glyphTintColor, + "glyphImageName" : glyphImageName]) + try? self.session.updateApplicationContext(["glyphType" : glyphType]) + } + } } // MARK: Incoming messages @@ -203,8 +254,7 @@ extension WatchConnectivityManager : WCSessionDelegate { replyHandler(Data()) } } - - + // MARK: Data parsing func parseEntireStore(_ initialCompletion: @escaping (Data) -> Void) { diff --git a/Sample/OCKSample/Weight.swift b/Sample/OCKSample/Weight.swift index e47a05df5..982b369a7 100644 --- a/Sample/OCKSample/Weight.swift +++ b/Sample/OCKSample/Weight.swift @@ -64,7 +64,8 @@ struct Weight: Assessment, HealthSampleBuilder { tintColor: Colors.yellow.color, resultResettable: false, schedule: schedule, - userInfo: nil + userInfo: nil, + optional: false ) return activity diff --git a/Sample/OCKSampleWatch Extension/ActivityRow.swift b/Sample/OCKSampleWatch Extension/ActivityRow.swift index 3f88e149c..f65717bbf 100644 --- a/Sample/OCKSampleWatch Extension/ActivityRow.swift +++ b/Sample/OCKSampleWatch Extension/ActivityRow.swift @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Accessibility.imageset/Accessibility@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Accessibility.imageset/Accessibility@2x.png new file mode 100644 index 000000000..00651815f Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/Accessibility.imageset/Accessibility@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Accessibility.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/Accessibility.imageset/Contents.json new file mode 100644 index 000000000..0ca25ca1a --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/Accessibility.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Accessibility@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/AdultLearning.imageset/Adult-Learning@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/AdultLearning.imageset/Adult-Learning@2x.png new file mode 100644 index 000000000..b29c82d48 Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/AdultLearning.imageset/Adult-Learning@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/AdultLearning.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/AdultLearning.imageset/Contents.json new file mode 100644 index 000000000..9c2c16199 --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/AdultLearning.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Adult-Learning@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Awareness.imageset/Awareness@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Awareness.imageset/Awareness@2x.png new file mode 100644 index 000000000..d67d6240c Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/Awareness.imageset/Awareness@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Awareness.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/Awareness.imageset/Contents.json new file mode 100644 index 000000000..f6f34e884 --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/Awareness.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Awareness@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Blood.imageset/Blood@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Blood.imageset/Blood@2x.png new file mode 100644 index 000000000..bf7548134 Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/Blood.imageset/Blood@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Blood.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/Blood.imageset/Contents.json new file mode 100644 index 000000000..fbb5c8366 --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/Blood.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Blood@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/BloodPressure.imageset/BloodPressure@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/BloodPressure.imageset/BloodPressure@2x.png new file mode 100644 index 000000000..6247efc8a Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/BloodPressure.imageset/BloodPressure@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/BloodPressure.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/BloodPressure.imageset/Contents.json new file mode 100644 index 000000000..16477c179 --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/BloodPressure.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "BloodPressure@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Cardio.imageset/Cardio@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Cardio.imageset/Cardio@2x.png new file mode 100644 index 000000000..ef6407cf9 Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/Cardio.imageset/Cardio@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Cardio.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/Cardio.imageset/Contents.json new file mode 100644 index 000000000..84be88b04 --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/Cardio.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Cardio@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/ChildLearning.imageset/ChildLearning@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/ChildLearning.imageset/ChildLearning@2x.png new file mode 100644 index 000000000..305fe5299 Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/ChildLearning.imageset/ChildLearning@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/ChildLearning.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/ChildLearning.imageset/Contents.json new file mode 100644 index 000000000..2d1e276f4 --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/ChildLearning.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ChildLearning@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json deleted file mode 100644 index bb7ff9728..000000000 --- a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x" - }, - { - "idiom" : "watch", - "filename" : "circular-stack-38mm@2x.png", - "screen-width" : "<=145", - "scale" : "2x" - }, - { - "idiom" : "watch", - "filename" : "circular-stack-42mm@2x.png", - "screen-width" : ">145", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "template" - } -} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/circular-stack-38mm@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/circular-stack-38mm@2x.png deleted file mode 100644 index a16044de1..000000000 Binary files a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/circular-stack-38mm@2x.png and /dev/null differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/circular-stack-42mm@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/circular-stack-42mm@2x.png deleted file mode 100644 index 440ed97fc..000000000 Binary files a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/circular-stack-42mm@2x.png and /dev/null differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Contents.json index 53b5400e1..c8ebf0675 100644 --- a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Contents.json +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Contents.json @@ -5,6 +5,11 @@ "filename" : "Circular.imageset", "role" : "circular" }, + { + "idiom" : "watch", + "filename" : "Extra Large.imageset", + "role" : "extra-large" + }, { "idiom" : "watch", "filename" : "Modular.imageset", @@ -14,9 +19,6 @@ "idiom" : "watch", "filename" : "Utilitarian.imageset", "role" : "utilitarian" - }, - { - "filename" : "X-Large.imageset" } ], "info" : { diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json deleted file mode 100644 index 204d24f2d..000000000 --- a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x" - }, - { - "idiom" : "watch", - "filename" : "mod-small-stack-38mm@2x.png", - "screen-width" : "<=145", - "scale" : "2x" - }, - { - "idiom" : "watch", - "filename" : "mod-small-stack-42mm@2x.png", - "screen-width" : ">145", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "template" - } -} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/mod-small-stack-38mm@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/mod-small-stack-38mm@2x.png deleted file mode 100644 index 93ec1a622..000000000 Binary files a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/mod-small-stack-38mm@2x.png and /dev/null differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/mod-small-stack-42mm@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/mod-small-stack-42mm@2x.png deleted file mode 100644 index 940c1da1d..000000000 Binary files a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/mod-small-stack-42mm@2x.png and /dev/null differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Star.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Star.imageset/Contents.json new file mode 100644 index 000000000..17ec3431f --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Star.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "SmallWhiteStar@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Star.imageset/SmallWhiteStar@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Star.imageset/SmallWhiteStar@2x.png new file mode 100644 index 000000000..6b2d27d8e Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Star.imageset/SmallWhiteStar@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json deleted file mode 100644 index 91b6449a6..000000000 --- a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x" - }, - { - "idiom" : "watch", - "filename" : "utilitarian-38mm@2x.png", - "screen-width" : "<=145", - "scale" : "2x" - }, - { - "idiom" : "watch", - "filename" : "utilitarian-42mm@2x.png", - "screen-width" : ">145", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "template" - } -} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/utilitarian-38mm@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/utilitarian-38mm@2x.png deleted file mode 100644 index b9ca4e726..000000000 Binary files a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/utilitarian-38mm@2x.png and /dev/null differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/utilitarian-42mm@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/utilitarian-42mm@2x.png deleted file mode 100644 index 66e01363c..000000000 Binary files a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/utilitarian-42mm@2x.png and /dev/null differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/X-Large.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/X-Large.imageset/Contents.json deleted file mode 100644 index 39576cce9..000000000 --- a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/X-Large.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "filename" : "xl-stack-38mm@2x.png", - "screen-width" : "<=145", - "scale" : "2x" - }, - { - "idiom" : "watch", - "filename" : "xl-stack-42mm@2x.png", - "screen-width" : ">145", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "template" - } -} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/X-Large.imageset/xl-stack-38mm@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/X-Large.imageset/xl-stack-38mm@2x.png deleted file mode 100644 index 2f912fb9a..000000000 Binary files a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/X-Large.imageset/xl-stack-38mm@2x.png and /dev/null differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/X-Large.imageset/xl-stack-42mm@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/X-Large.imageset/xl-stack-42mm@2x.png deleted file mode 100644 index e90ff4904..000000000 Binary files a/Sample/OCKSampleWatch Extension/Assets.xcassets/Complication.complicationset/X-Large.imageset/xl-stack-42mm@2x.png and /dev/null differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/DentalHealth.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/DentalHealth.imageset/Contents.json new file mode 100644 index 000000000..e630b5eeb --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/DentalHealth.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "DentalHealth@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/DentalHealth.imageset/DentalHealth@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/DentalHealth.imageset/DentalHealth@2x.png new file mode 100644 index 000000000..96c412b36 Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/DentalHealth.imageset/DentalHealth@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/FemaleHealth.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/FemaleHealth.imageset/Contents.json new file mode 100644 index 000000000..e125a0d14 --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/FemaleHealth.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "FemaleHealth@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/FemaleHealth.imageset/FemaleHealth@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/FemaleHealth.imageset/FemaleHealth@2x.png new file mode 100644 index 000000000..13c03d397 Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/FemaleHealth.imageset/FemaleHealth@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Hearing.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/Hearing.imageset/Contents.json new file mode 100644 index 000000000..39ed85d36 --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/Hearing.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Hearing@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Hearing.imageset/Hearing@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Hearing.imageset/Hearing@2x.png new file mode 100644 index 000000000..202cdb96a Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/Hearing.imageset/Hearing@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Heart.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/Heart.imageset/Contents.json new file mode 100644 index 000000000..001610c99 --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/Heart.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "heart_inactive.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Heart.imageset/heart_inactive.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Heart.imageset/heart_inactive.png new file mode 100644 index 000000000..ef6407cf9 Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/Heart.imageset/heart_inactive.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/HomeCare.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/HomeCare.imageset/Contents.json new file mode 100644 index 000000000..7f9f217fb --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/HomeCare.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "HomeCare@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/HomeCare.imageset/HomeCare@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/HomeCare.imageset/HomeCare@2x.png new file mode 100644 index 000000000..ff68298cb Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/HomeCare.imageset/HomeCare@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/InfantCare.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/InfantCare.imageset/Contents.json new file mode 100644 index 000000000..6eff5e436 --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/InfantCare.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "InfantCare@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/InfantCare.imageset/InfantCare@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/InfantCare.imageset/InfantCare@2x.png new file mode 100644 index 000000000..c5b0ce50e Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/InfantCare.imageset/InfantCare@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Laboratory.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/Laboratory.imageset/Contents.json new file mode 100644 index 000000000..2e57c98bb --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/Laboratory.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Laboratory@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Laboratory.imageset/Laboratory@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Laboratory.imageset/Laboratory@2x.png new file mode 100644 index 000000000..b7cdb9999 Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/Laboratory.imageset/Laboratory@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/MaternalHealth.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/MaternalHealth.imageset/Contents.json new file mode 100644 index 000000000..e66ffb44d --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/MaternalHealth.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "MaternalHealth@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/MaternalHealth.imageset/MaternalHealth@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/MaternalHealth.imageset/MaternalHealth@2x.png new file mode 100644 index 000000000..da7cabb86 Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/MaternalHealth.imageset/MaternalHealth@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Medication.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/Medication.imageset/Contents.json new file mode 100644 index 000000000..bffd83ba6 --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/Medication.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Medication@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Medication.imageset/Medication@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Medication.imageset/Medication@2x.png new file mode 100644 index 000000000..e6ede13bb Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/Medication.imageset/Medication@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/MentalHealth.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/MentalHealth.imageset/Contents.json new file mode 100644 index 000000000..c7d77e41a --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/MentalHealth.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "MentalHealth@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/MentalHealth.imageset/MentalHealth@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/MentalHealth.imageset/MentalHealth@2x.png new file mode 100644 index 000000000..fc6abc7f5 Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/MentalHealth.imageset/MentalHealth@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Neuro.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/Neuro.imageset/Contents.json new file mode 100644 index 000000000..fc097163f --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/Neuro.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Neuro@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Neuro.imageset/Neuro@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Neuro.imageset/Neuro@2x.png new file mode 100644 index 000000000..0944f634c Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/Neuro.imageset/Neuro@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Nutrition.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/Nutrition.imageset/Contents.json new file mode 100644 index 000000000..bf86e87b9 --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/Nutrition.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Nutrition@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Nutrition.imageset/Nutrition@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Nutrition.imageset/Nutrition@2x.png new file mode 100644 index 000000000..cb1578eed Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/Nutrition.imageset/Nutrition@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Optometry.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/Optometry.imageset/Contents.json new file mode 100644 index 000000000..1a34dfb41 --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/Optometry.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Optometry@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Optometry.imageset/Optometry@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Optometry.imageset/Optometry@2x.png new file mode 100644 index 000000000..b01c7ca3f Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/Optometry.imageset/Optometry@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Pediatrics.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/Pediatrics.imageset/Contents.json new file mode 100644 index 000000000..51e95c51a --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/Pediatrics.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Pediatrics@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Pediatrics.imageset/Pediatrics@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Pediatrics.imageset/Pediatrics@2x.png new file mode 100644 index 000000000..ca1138ba1 Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/Pediatrics.imageset/Pediatrics@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/PhysicalTherapy.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/PhysicalTherapy.imageset/Contents.json new file mode 100644 index 000000000..33ef4b766 --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/PhysicalTherapy.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "PhysicalTherapy@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/PhysicalTherapy.imageset/PhysicalTherapy@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/PhysicalTherapy.imageset/PhysicalTherapy@2x.png new file mode 100644 index 000000000..7169097f1 Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/PhysicalTherapy.imageset/PhysicalTherapy@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Podiatry.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/Podiatry.imageset/Contents.json new file mode 100644 index 000000000..43a29ec3a --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/Podiatry.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Podiatry@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Podiatry.imageset/Podiatry@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Podiatry.imageset/Podiatry@2x.png new file mode 100644 index 000000000..e1def8504 Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/Podiatry.imageset/Podiatry@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/RespiratoryHealth.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/RespiratoryHealth.imageset/Contents.json new file mode 100644 index 000000000..bd0928a99 --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/RespiratoryHealth.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "RespiratoryHealth@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/RespiratoryHealth.imageset/RespiratoryHealth@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/RespiratoryHealth.imageset/RespiratoryHealth@2x.png new file mode 100644 index 000000000..922312509 Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/RespiratoryHealth.imageset/RespiratoryHealth@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Scale.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/Scale.imageset/Contents.json new file mode 100644 index 000000000..d7379e486 --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/Scale.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Scale@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Scale.imageset/Scale@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Scale.imageset/Scale@2x.png new file mode 100644 index 000000000..46ce69ad5 Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/Scale.imageset/Scale@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Stethoscope.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/Stethoscope.imageset/Contents.json new file mode 100644 index 000000000..714d003f2 --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/Stethoscope.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Stethoscope@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Stethoscope.imageset/Stethoscope@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Stethoscope.imageset/Stethoscope@2x.png new file mode 100644 index 000000000..105d2c051 Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/Stethoscope.imageset/Stethoscope@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Syringe.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/Syringe.imageset/Contents.json new file mode 100644 index 000000000..810b3495d --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/Syringe.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Syringe@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/Syringe.imageset/Syringe@2x.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/Syringe.imageset/Syringe@2x.png new file mode 100644 index 000000000..98db9f6c5 Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/Syringe.imageset/Syringe@2x.png differ diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/custom.imageset/Contents.json b/Sample/OCKSampleWatch Extension/Assets.xcassets/custom.imageset/Contents.json new file mode 100644 index 000000000..1fc7f8593 --- /dev/null +++ b/Sample/OCKSampleWatch Extension/Assets.xcassets/custom.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "custom.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/OCKSampleWatch Extension/Assets.xcassets/custom.imageset/custom.png b/Sample/OCKSampleWatch Extension/Assets.xcassets/custom.imageset/custom.png new file mode 100644 index 000000000..a7bfd78bf Binary files /dev/null and b/Sample/OCKSampleWatch Extension/Assets.xcassets/custom.imageset/custom.png differ diff --git a/Sample/OCKSampleWatch Extension/ComplicationController.swift b/Sample/OCKSampleWatch Extension/ComplicationController.swift index 24f220d3d..348bbf64b 100644 --- a/Sample/OCKSampleWatch Extension/ComplicationController.swift +++ b/Sample/OCKSampleWatch Extension/ComplicationController.swift @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -30,9 +30,10 @@ import ClockKit - class ComplicationController: NSObject, CLKComplicationDataSource { + var complicationImage: UIImage = UIImage() + // MARK: - Timeline Configuration func getSupportedTimeTravelDirections(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimeTravelDirections) -> Void) { @@ -47,6 +48,7 @@ class ComplicationController: NSObject, CLKComplicationDataSource { func getCurrentTimelineEntry(for complication: CLKComplication, withHandler handler: @escaping((CLKComplicationTimelineEntry?) -> Void)) { // Call the handler with the current timeline entry + let template = getTemplate(forCompletionPercentage: getCurrentCompletionPercentage(), complication: complication) if template != nil { handler(CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template!)) @@ -69,27 +71,35 @@ class ComplicationController: NSObject, CLKComplicationDataSource { func getPlaceholderTemplate(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTemplate?) -> Void) { // This method will be called once per supported complication, and the results will be cached + handler(getTemplate(forCompletionPercentage: nil, complication: complication)) } - // MARK: Rendering Templates func getTemplate(forCompletionPercentage completionPercentage : Int?, complication : CLKComplication) -> CLKComplicationTemplate? { var textToDisplay : String + + if (getGlyphType() != "Image Unavailable") { + complicationImage = UIImage(named: getGlyphType())! + } + + let defaults = UserDefaults.standard + let tintColor = defaults.array(forKey: "glyphTintColor") as? [CGFloat] ?? [0.0, 0.0, 0.0, 0.0] + let glyphTintColor = UIColor(red: tintColor[0], green: tintColor[1], blue: tintColor[2], alpha: tintColor[3]) + if completionPercentage == nil || completionPercentage == -1 { - // completionPercentage of -1 indicates request for nil to be displayed by InterfaceController + textToDisplay = "--%" } else { textToDisplay = "\(completionPercentage!)%" } - - + switch complication.family { case .modularLarge: let template = CLKComplicationTemplateModularLargeStandardBody() - template.headerTextProvider = CLKSimpleTextProvider(text: "Care Completion") - template.tintColor = InterfaceController.watchTintColor + template.headerTextProvider = CLKSimpleTextProvider(text: "Care Overview") + template.tintColor = glyphTintColor template.body1TextProvider = CLKSimpleTextProvider(text: textToDisplay) if completionPercentage != nil && completionPercentage != -1 { @@ -107,38 +117,72 @@ class ComplicationController: NSObject, CLKComplicationDataSource { return template case .modularSmall: - let template = CLKComplicationTemplateModularSmallStackImage() - template.line1ImageProvider = CLKImageProvider(onePieceImage: UIImage(named: "Complication/Modular")!) - template.line2TextProvider = CLKSimpleTextProvider(text: textToDisplay) - template.tintColor = InterfaceController.watchTintColor + let template = CLKComplicationTemplateModularSmallRingImage() + if (completionPercentage != nil) { + template.fillFraction = Float(completionPercentage!)/100 + if (completionPercentage == 100) { + if let image = UIImage(named: "Complication/Star") { + template.imageProvider = CLKImageProvider(onePieceImage: image) + } + } + else { + template.imageProvider = CLKImageProvider(onePieceImage: complicationImage) + } + } + else { + template.imageProvider = CLKImageProvider(onePieceImage: complicationImage) + } + template.tintColor = glyphTintColor return template - + case .utilitarianSmall: - let template = CLKComplicationTemplateUtilitarianSmallFlat() - template.imageProvider = CLKImageProvider(onePieceImage: UIImage(named: "Complication/Utilitarian")!) - template.textProvider = CLKSimpleTextProvider(text: textToDisplay) - template.tintColor = InterfaceController.watchTintColor + let template = CLKComplicationTemplateUtilitarianSmallRingImage() + if (completionPercentage != nil) { + template.fillFraction = Float(completionPercentage!)/100 + if (completionPercentage == 100) { + if let image = UIImage(named: "Complication/Star") { + template.imageProvider = CLKImageProvider(onePieceImage: image) + } + } + else { + template.imageProvider = CLKImageProvider(onePieceImage: complicationImage) + } + } + else { + template.imageProvider = CLKImageProvider(onePieceImage: complicationImage) + } + + template.tintColor = glyphTintColor return template case .utilitarianLarge: let template = CLKComplicationTemplateUtilitarianLargeFlat() if completionPercentage == nil && completionPercentage != -1 { template.textProvider = CLKSimpleTextProvider(text: "Care Plan") + template.imageProvider = CLKImageProvider(onePieceImage: complicationImage) } else { switch completionPercentage! { case 100: template.textProvider = CLKSimpleTextProvider(text: "Care Complete") + if let image = UIImage(named: "Complication/Star") { + template.imageProvider = CLKImageProvider(onePieceImage: image) + } default: template.textProvider = CLKSimpleTextProvider(text: "Care Plan: " + textToDisplay) + template.imageProvider = CLKImageProvider(onePieceImage: complicationImage) } } - template.imageProvider = CLKImageProvider(onePieceImage: UIImage(named: "Complication/Utilitarian")!) - template.tintColor = InterfaceController.watchTintColor + template.tintColor = glyphTintColor return template - case .circularSmall: let template = CLKComplicationTemplateCircularSmallStackImage() - template.line1ImageProvider = CLKImageProvider(onePieceImage: UIImage(named: "Complication/Circular")!) + if let image = UIImage(named: "Complication/Circular") { + template.line1ImageProvider = CLKImageProvider(onePieceImage: image) + } else { + if let image = UIImage(named: "Complication/Star") { + template.line1ImageProvider = CLKImageProvider(onePieceImage: image) + } + } template.line2TextProvider = CLKSimpleTextProvider(text: textToDisplay) template.tintColor = InterfaceController.watchTintColor return template @@ -146,7 +190,15 @@ class ComplicationController: NSObject, CLKComplicationDataSource { case .extraLarge: if #available(watchOSApplicationExtension 3.0, *) { let template = CLKComplicationTemplateExtraLargeStackImage() - template.line1ImageProvider = CLKImageProvider(onePieceImage: UIImage(named: "Complication/X-Large")!) + let image = UIImage(named: "Complication/X-Large") + if (image != nil) { + template.line1ImageProvider = CLKImageProvider(onePieceImage: image!) + } else { + let image = UIImage(named: "Complication/Star") + if (image != nil) { + template.line1ImageProvider = CLKImageProvider(onePieceImage: image!) + } + } template.line2TextProvider = CLKSimpleTextProvider(text: textToDisplay) template.tintColor = InterfaceController.watchTintColor template.highlightLine2 = false @@ -167,9 +219,28 @@ class ComplicationController: NSObject, CLKComplicationDataSource { return defaults.integer(forKey: "currentCompletionPercentage") } + + func getEventsRemaining() -> Int { let defaults = UserDefaults.standard return defaults.integer(forKey: "eventsRemaining") } + func getGlyphType() -> String { + let defaults = UserDefaults.standard + let glyphType = defaults.string(forKey: "glyphType") + + if (glyphType == nil){ + return "Image Unavailable" + } else if (glyphType == "Custom") { + let glyphImageName = defaults.string(forKey: "glyphImageName")! + if (glyphImageName != "") { + return glyphImageName + } + + return "Image Unavailable" + } else { + return defaults.string(forKey: "glyphType")! + } + } } diff --git a/Sample/OCKSampleWatch Extension/Info.plist b/Sample/OCKSampleWatch Extension/Info.plist index f02b1daf9..812b6522a 100644 --- a/Sample/OCKSampleWatch Extension/Info.plist +++ b/Sample/OCKSampleWatch Extension/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 1.0 + 1.1 CFBundleSignature ???? CFBundleVersion diff --git a/Sample/OCKSampleWatch Extension/InterfaceController.swift b/Sample/OCKSampleWatch Extension/InterfaceController.swift index bc779dc46..e46c6319c 100644 --- a/Sample/OCKSampleWatch Extension/InterfaceController.swift +++ b/Sample/OCKSampleWatch Extension/InterfaceController.swift @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -99,6 +99,7 @@ class InterfaceController: WKInterfaceController { tableView.removeRows(at: IndexSet.init(integersIn: 0.. Int { let eventsComplete = self.activities.values.map({$0.getNumberOfCompletedEvents()}).reduce(0, +) let eventsTotal = self.activities.values.map({$0.eventsForToday.count}).reduce(0, +) @@ -342,9 +353,41 @@ extension InterfaceController: WCSessionDelegate { return } + guard let glyphType = applicationContext["glyphType"] as? String else { + return + } + let defaults = UserDefaults.standard defaults.set(completionPercentage, forKey: "currentCompletionPercentage") defaults.set(eventsRemaining, forKey: "eventsRemaining") + defaults.set(glyphType, forKey: "glyphType") + + let server = CLKComplicationServer.sharedInstance() + for complication in server.activeComplications! { + server.reloadTimeline(for: complication) + } + } + + func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) { + guard let glyphType = userInfo["glyphType"] as? String else{ + return + } + + guard let glyphTintColor = userInfo["glyphTintColor"] as? [CGFloat] else { + return + } + + guard let glyphImageName = userInfo["glyphImageName"] as? String else { + return + } + + let defaults = UserDefaults.standard + defaults.set(glyphType, forKey: "glyphType") + defaults.set(glyphTintColor, forKey: "glyphTintColor") + + if (glyphType == "Custom") { + defaults.set(glyphImageName, forKey: "glyphImageName") + } let server = CLKComplicationServer.sharedInstance() for complication in server.activeComplications! { diff --git a/Sample/OCKSampleWatch/Info.plist b/Sample/OCKSampleWatch/Info.plist index 28c07672d..676b3d277 100644 --- a/Sample/OCKSampleWatch/Info.plist +++ b/Sample/OCKSampleWatch/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion en CFBundleDisplayName - OCKSample + Care CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0 + 1.1 CFBundleSignature ???? CFBundleVersion diff --git a/testing/OCKTest/OCKTest.xcodeproj/project.pbxproj b/testing/OCKTest/OCKTest.xcodeproj/project.pbxproj index e0d6feb02..d9e03039a 100644 --- a/testing/OCKTest/OCKTest.xcodeproj/project.pbxproj +++ b/testing/OCKTest/OCKTest.xcodeproj/project.pbxproj @@ -247,6 +247,7 @@ ORGANIZATIONNAME = carekit.org; TargetAttributes = { 2D365AED1CDC15A300B147FC = { + LastSwiftMigration = 0820; SystemCapabilities = { com.apple.HealthKit = { enabled = 0; @@ -403,6 +404,7 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "$(SRCROOT)/OCKTest/Info-iPad.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "org.carekit.OCKTest-iPad"; @@ -416,6 +418,7 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "$(SRCROOT)/OCKTest/Info-iPad.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "org.carekit.OCKTest-iPad"; @@ -520,6 +523,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = OCKTest/OCKTest.entitlements; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "$(SRCROOT)/OCKTest/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.carekit.OCKTest; @@ -534,6 +538,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = OCKTest/OCKTest.entitlements; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "$(SRCROOT)/OCKTest/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.carekit.OCKTest; diff --git a/testing/OCKTest/OCKTest/Base.lproj/Main.storyboard b/testing/OCKTest/OCKTest/Base.lproj/Main.storyboard index c8b802c93..79ebd4fd6 100644 --- a/testing/OCKTest/OCKTest/Base.lproj/Main.storyboard +++ b/testing/OCKTest/OCKTest/Base.lproj/Main.storyboard @@ -1,7 +1,8 @@ - - + + - + + @@ -9,24 +10,24 @@ - + - + - + - + @@ -36,17 +37,17 @@ - + - + @@ -56,17 +57,17 @@ - + - + @@ -76,17 +77,17 @@ - + - + @@ -96,17 +97,17 @@ - + - + @@ -116,17 +117,17 @@ - + - + @@ -172,15 +173,15 @@ - + - + - + - + @@ -200,15 +201,15 @@ - + - + - + - + @@ -232,11 +233,11 @@ - + - - + + @@ -247,7 +248,7 @@ - + @@ -268,15 +269,15 @@ - + - + - + - + @@ -296,15 +297,15 @@ - + - + - + - + @@ -324,15 +325,15 @@ - + - + - + - + diff --git a/testing/OCKTest/OCKTest/CareCardTableViewController.swift b/testing/OCKTest/OCKTest/CareCardTableViewController.swift index d658902b6..cbea060da 100644 --- a/testing/OCKTest/OCKTest/CareCardTableViewController.swift +++ b/testing/OCKTest/OCKTest/CareCardTableViewController.swift @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Apple Inc. All rights reserved. + Copyright (c) 2017, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -39,7 +39,7 @@ class CareCardTableViewController: UITableViewController, OCKCarePlanStoreDelega } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return 8 + return 10 } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { @@ -62,6 +62,10 @@ class CareCardTableViewController: UITableViewController, OCKCarePlanStoreDelega cell.textLabel?.text = "Save an Image" case 7: cell.textLabel?.text = "Delete all Activites" + case 8: + cell.textLabel?.text = "Trying different glyphType" + case 9: + cell.textLabel?.text = "Trying different Tint Color" default: cell.textLabel?.text = nil } @@ -105,9 +109,9 @@ class CareCardTableViewController: UITableViewController, OCKCarePlanStoreDelega let activity6 = OCKCarePlanActivity.init(identifier: "Intervention Activity #6", groupIdentifier: firstGroupId, type: OCKCarePlanActivityType.intervention, title: "Activity Ended Yesterday", text: LoremIpsum, tintColor: UIColor.gray, instructions: LoremIpsum, imageURL: nil, schedule: dailySchedule, resultResettable: true, userInfo: nil) carePlanActivities.append(activity6) - carePlanActivities.append(OCKCarePlanActivity.intervention(withIdentifier: "Intervention Activity #7", groupIdentifier: nil, title: "No Group, No Text Activity", text: nil, tintColor: nil, instructions: nil, imageURL: nil, schedule: dailySchedule, userInfo: nil)) + carePlanActivities.append(OCKCarePlanActivity.intervention(withIdentifier: "Intervention Activity #7", groupIdentifier: nil, title: "No Group, No Text Activity", text: nil, tintColor: nil, instructions: nil, imageURL: nil, schedule: dailySchedule, userInfo: nil, optional: false)) - carePlanActivities.append(OCKCarePlanActivity.intervention(withIdentifier: "Intervention Activity #8", groupIdentifier: nil, title: "", text: "Missing Title", tintColor: UIColor.purple, instructions: "Some Instructions", imageURL: nil, schedule: dailySchedule, userInfo: ["":""])) + carePlanActivities.append(OCKCarePlanActivity.intervention(withIdentifier: "Intervention Activity #8", groupIdentifier: nil, title: "", text: "Missing Title", tintColor: UIColor.purple, instructions: "Some Instructions", imageURL: nil, schedule: dailySchedule, userInfo: ["":""], optional: false)) let carePlanStore = OCKCarePlanStore.init(persistenceDirectoryURL: URL.init(string: documentsDirectory[0])!) @@ -123,7 +127,6 @@ class CareCardTableViewController: UITableViewController, OCKCarePlanStoreDelega }) let careCardController = OCKCareCardViewController.init(carePlanStore: carePlanStore) - careCardController.showEdgeIndicators = true self.navigationController?.pushViewController(careCardController, animated: true) } else if (indexPath as NSIndexPath).row == 1 { @@ -141,7 +144,7 @@ class CareCardTableViewController: UITableViewController, OCKCarePlanStoreDelega let carePlanActivity2 = OCKCarePlanActivity.init(identifier: "Intervention Activity 2", groupIdentifier: secondGroupId, type: OCKCarePlanActivityType.intervention, title: "2. Another Intervention Activity", text: "Complete this activity ASAP. No Instructions!", tintColor: nil, instructions: nil, imageURL: imageFileURL, schedule: schedule, resultResettable: true, userInfo: nil) - let carePlanActivity3 = OCKCarePlanActivity.intervention(withIdentifier: "Intervention Activity 3", groupIdentifier: secondGroupId, title: "3. Activity #3 is the last one", text: "Some Text", tintColor: UIColor.purple, instructions: "Some Instructions", imageURL: imageFileURL, schedule: schedule, userInfo: ["Key":"Val"]) + let carePlanActivity3 = OCKCarePlanActivity.intervention(withIdentifier: "Intervention Activity 3", groupIdentifier: secondGroupId, title: "3. Activity #3 is the last one", text: "Some Text", tintColor: UIColor.purple, instructions: "Some Instructions", imageURL: imageFileURL, schedule: schedule, userInfo: ["Key":"Val"], optional:false) let dataPath = documentsDirectory[0] + "/CarePlan2" if !FileManager.default.fileExists(atPath: dataPath) { @@ -166,10 +169,8 @@ class CareCardTableViewController: UITableViewController, OCKCarePlanStoreDelega }) let careCardController = OCKCareCardViewController.init(carePlanStore: carePlanStore) - careCardController.maskImage = UIImage.init(named: "Stars") - careCardController.smallMaskImage = UIImage.init(named: "Triangles.jpg") - careCardController.maskImageTintColor = UIColor.cyan - careCardController.showEdgeIndicators = true + careCardController.glyphType = .accessibility + careCardController.glyphTintColor = UIColor.cyan self.navigationController?.pushViewController(careCardController, animated: true) } else if (indexPath as NSIndexPath).row == 2 { @@ -187,7 +188,7 @@ class CareCardTableViewController: UITableViewController, OCKCarePlanStoreDelega let carePlanStore = OCKCarePlanStore.init(persistenceDirectoryURL: URL.init(string: dataPath)!) let careCardController = OCKCareCardViewController.init(carePlanStore: carePlanStore) - careCardController.maskImageTintColor = UIColor.orange + careCardController.glyphTintColor = UIColor.orange self.navigationController?.pushViewController(careCardController, animated: true) } else if (indexPath as NSIndexPath).row == 3 { @@ -231,7 +232,7 @@ class CareCardTableViewController: UITableViewController, OCKCarePlanStoreDelega let careCardController = OCKCareCardViewController.init(carePlanStore: carePlanStore) - careCardController.maskImageTintColor = UIColor.init(red: 0.2, green: 0.4, blue: 0.9, alpha: 0.4) + careCardController.glyphTintColor = UIColor.init(red: 0.2, green: 0.4, blue: 0.9, alpha: 0.4) self.navigationController?.pushViewController(careCardController, animated: true) } else if (indexPath as NSIndexPath).row == 4 { @@ -265,8 +266,6 @@ class CareCardTableViewController: UITableViewController, OCKCarePlanStoreDelega let careCardController = OCKCareCardViewController.init(carePlanStore: carePlanStore) careCardController.delegate = self - careCardController.smallMaskImage = UIImage.init(named: "Stars") - careCardController.showEdgeIndicators = true self.navigationController?.pushViewController(careCardController, animated: true) } else if (indexPath as NSIndexPath).row == 5 { @@ -280,7 +279,6 @@ class CareCardTableViewController: UITableViewController, OCKCarePlanStoreDelega let carePlanStore = OCKCarePlanStore.init(persistenceDirectoryURL: URL.init(string: dataPath)!) let careCardController = OCKCareCardViewController.init(carePlanStore: carePlanStore) careCardController.delegate = self - careCardController.showEdgeIndicators = true self.navigationController?.pushViewController(careCardController, animated: true) } else if (indexPath as NSIndexPath).row == 6 { @@ -298,78 +296,54 @@ class CareCardTableViewController: UITableViewController, OCKCarePlanStoreDelega tableView.cellForRow(at: indexPath)?.isSelected = false } else if (indexPath as NSIndexPath).row == 7 { + // No Activities - // Delete all Activites + let dataPath = documentsDirectory[0] + "/EmptyCarePlan" + if !FileManager.default.fileExists(atPath: dataPath) { + do { + try FileManager.default.createDirectory(atPath: dataPath, withIntermediateDirectories: false, attributes: nil) + } catch(_) { + assertionFailure("Unable to Create Directory for EmptyCarePlan") + } + } - tableView.cellForRow(at: indexPath)?.isSelected = false + let carePlanStore = OCKCarePlanStore.init(persistenceDirectoryURL: URL.init(string: dataPath)!) + let careCardController = OCKCareCardViewController.init(carePlanStore: carePlanStore) + careCardController.glyphTintColor = UIColor.orange + self.navigationController?.pushViewController(careCardController, animated: true) + } else if (indexPath as NSIndexPath).row == 8 { + // No Activities - let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true) - let store = OCKCarePlanStore.init(persistenceDirectoryURL: URL.init(string: paths[0])!) - store.activities(with: OCKCarePlanActivityType.intervention, completion: { (boolVal, activities, error) in - for activity:OCKCarePlanActivity in activities { - store.remove(activity, completion: { (boolVal, error) in - if boolVal == true { - tableView.cellForRow(at: indexPath)?.textLabel?.textColor = UIColor.green - } else { - tableView.cellForRow(at: indexPath)?.textLabel?.textColor = UIColor.red - } - assert(boolVal, (error?.localizedDescription)!) - }) + let dataPath = documentsDirectory[0] + "/EmptyCarePlan" + if !FileManager.default.fileExists(atPath: dataPath) { + do { + try FileManager.default.createDirectory(atPath: dataPath, withIntermediateDirectories: false, attributes: nil) + } catch(_) { + assertionFailure("Unable to Create Directory for EmptyCarePlan") } - }) - - - if FileManager.default.fileExists(atPath: paths[0] + "/CarePlan2") { - let dataPath = URL.init(string:paths[0] + "/CarePlan2") - let store2 = OCKCarePlanStore.init(persistenceDirectoryURL: dataPath!) - store2.activities(withGroupIdentifier: "Group I2", completion: { (boolVal, activities, error) in - for activity:OCKCarePlanActivity in activities { - store2.remove(activity, completion: { (bool, error) in - if boolVal == true { - tableView.cellForRow(at: indexPath)?.textLabel?.textColor = UIColor.green - } else { - tableView.cellForRow(at: indexPath)?.textLabel?.textColor = UIColor.red - } - assert(boolVal, (error?.localizedDescription)!) - }) - } - }) } - if FileManager.default.fileExists(atPath: paths[0] + "/CarePlanAuto") { - let dataPath = URL.init(string:paths[0] + "/CarePlanAuto") - let store3 = OCKCarePlanStore.init(persistenceDirectoryURL: dataPath!) - store3.activities(completion: { (boolVal, activities, error) in - for activity in activities - { - store3.remove(activity, completion: { (boolVal, error) in - if boolVal == true { - tableView.cellForRow(at: indexPath)?.textLabel?.textColor = UIColor.green - } else { - tableView.cellForRow(at: indexPath)?.textLabel?.textColor = UIColor.red - } - assert(boolVal, (error?.localizedDescription)!) - }) - } - }) - } + let carePlanStore = OCKCarePlanStore.init(persistenceDirectoryURL: URL.init(string: dataPath)!) + let careCardController = OCKCareCardViewController.init(carePlanStore: carePlanStore) + careCardController.glyphType = .accessibility + careCardController.glyphTintColor = UIColor.orange + self.navigationController?.pushViewController(careCardController, animated: true) + } else if (indexPath as NSIndexPath).row == 9 { + // No Activities - if FileManager.default.fileExists(atPath: paths[0] + "/CarePlanIncomplete") { - let dataPath = URL.init(string:paths[0] + "/CarePlanIncomplete") - let store4 = OCKCarePlanStore.init(persistenceDirectoryURL: dataPath!) - store4.activities(completion: { (boolVal, activities, error) in - for activity in activities { - store4.remove(activity, completion: { (boolVal, error) in - if boolVal == true { - tableView.cellForRow(at: indexPath)?.textLabel?.textColor = UIColor.green - } else { - tableView.cellForRow(at: indexPath)?.textLabel?.textColor = UIColor.red - } - assert(boolVal, (error?.localizedDescription)!) - }) - } - }) + let dataPath = documentsDirectory[0] + "/EmptyCarePlan" + if !FileManager.default.fileExists(atPath: dataPath) { + do { + try FileManager.default.createDirectory(atPath: dataPath, withIntermediateDirectories: false, attributes: nil) + } catch(_) { + assertionFailure("Unable to Create Directory for EmptyCarePlan") + } } + + let carePlanStore = OCKCarePlanStore.init(persistenceDirectoryURL: URL.init(string: dataPath)!) + let careCardController = OCKCareCardViewController.init(carePlanStore: carePlanStore) + careCardController.glyphTintColor = UIColor.magenta + self.navigationController?.pushViewController(careCardController, animated: true) } } @@ -382,7 +356,7 @@ class CareCardTableViewController: UITableViewController, OCKCarePlanStoreDelega func careCardViewController(_ viewController: OCKCareCardViewController, didSelectButtonWithInterventionEvent interventionEvent: OCKCarePlanEvent) { if interventionEvent.activity.groupIdentifier == "Group I4" { - viewController.maskImageTintColor = interventionEvent.activity.tintColor + viewController.glyphTintColor = interventionEvent.activity.tintColor } } diff --git a/testing/OCKTest/OCKTest/ConnectTableViewController.swift b/testing/OCKTest/OCKTest/ConnectTableViewController.swift index 863081acc..ca9ebcb3a 100644 --- a/testing/OCKTest/OCKTest/ConnectTableViewController.swift +++ b/testing/OCKTest/OCKTest/ConnectTableViewController.swift @@ -107,7 +107,6 @@ class ConnectTableViewController: UITableViewController, OCKConnectViewControlle let connectViewController = OCKConnectViewController(contacts: [contact1, contact2, contact3, contact4, contact5, contact6, contact7, contact8, contact9]) connectViewController.delegate = self - connectViewController.showEdgeIndicators = true self.navigationController?.pushViewController(connectViewController, animated: true) } else if (indexPath as NSIndexPath).row == 2 { diff --git a/testing/OCKTest/OCKTest/InsightsTableViewController.swift b/testing/OCKTest/OCKTest/InsightsTableViewController.swift index b9dc41f6b..f4bdbb24f 100644 --- a/testing/OCKTest/OCKTest/InsightsTableViewController.swift +++ b/testing/OCKTest/OCKTest/InsightsTableViewController.swift @@ -39,7 +39,7 @@ class InsightsTableViewController: UITableViewController { } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return 7 + return 8 } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { @@ -60,6 +60,8 @@ class InsightsTableViewController: UITableViewController { cell.textLabel?.text = "Custom Charts" case 6: cell.textLabel?.text = "Custom Scales" + case 7: + cell.textLabel?.text = "Many Ring Items" default: cell.textLabel?.text = nil } @@ -104,7 +106,7 @@ class InsightsTableViewController: UITableViewController { let chart5Series2 = OCKBarSeries.init(title: "Zero", values: [0], valueLabels: ["0"], tintColor: UIColor.green) let chart5 = OCKBarChart.init(title: "Chart #5", text: "Chart #5 Description\n spans over two lines", tintColor: UIColor.green, axisTitles: ["A","B"], axisSubtitles: ["abcdefgh","pqrstuvwxyz"], dataSeries: [chart5Series1, chart5Series2]) - let insightsDashboardViewController = OCKInsightsViewController.init(insightItems: [chart1, chart2, chart3, chart4, chart5], headerTitle: "Insights Dashboard\nThis line should be hidden", headerSubtitle: "Many Bar Charts Here\nSubtitle can have two lines\nBut not three") + let insightsDashboardViewController = OCKInsightsViewController.init(insightItems: [chart1, chart2, chart3, chart4, chart5]) self.navigationController?.pushViewController(insightsDashboardViewController, animated: true) } else if (indexPath as NSIndexPath).row == 1 { @@ -136,7 +138,6 @@ class InsightsTableViewController: UITableViewController { let barSeries4 = OCKBarSeries.init(title: "Canada Average", values: [80000,90000,100000,120000,130000,135000,143000,145000,120000,120000,130000,150000,80000,90000,100000,120000,130000,135000,145000,120000,120000,130000,150000], valueLabels: ["80K","90K","100K","120K","130K","135K","143K","145K","120K","120K","130K","150K","80K","90K","100K","120K","130K","135K","145K","120K","120K","130K","150K"], tintColor: UIColor.yellow) let chart1 = OCKBarChart.init(title: "Walking", text: "Step Count", tintColor: UIColor.gray, axisTitles: ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec","Jan'17","Feb'17","Mar'17","Apr'17","May'17","Jun'17","Jul'17","Aug'17","Sep'17","Oct'17","Nov'17","Dec'17"], axisSubtitles: nil, dataSeries: [barSeries1, barSeries2,barSeries3,barSeries4]) let insightsDashboardViewController = OCKInsightsViewController.init(insightItems: [chart1]) - insightsDashboardViewController.headerSubtitle = "This is a subtitle without a title" self.navigationController?.pushViewController(insightsDashboardViewController, animated: true) } else if (indexPath as NSIndexPath).row == 3 { @@ -165,9 +166,10 @@ class InsightsTableViewController: UITableViewController { let chart1 = OCKBarChart.init(title: "Chart #1", text: "Chart #1 Description", tintColor: UIColor.gray, axisTitles: ["ABC","DEF","GHI","JKL","MNO"], axisSubtitles: ["123","456","789","012"], dataSeries: [series1, series2]) let messageItem3 = OCKMessageItem.init(title: "Another Alert", text: nil, tintColor: UIColor.orange, messageType: OCKMessageItemType.alert) let messageItem4 = OCKMessageItem.init(title: "Another Tip", text: nil, tintColor: UIColor.green, messageType: OCKMessageItemType.tip) - let insightsDashboardViewController = OCKInsightsViewController.init(insightItems: [messageItem1, messageItem2,chart1, messageItem3, messageItem4]) - insightsDashboardViewController.headerTitle = "Dashboard with Messages and Bar Charts" - insightsDashboardViewController.showEdgeIndicators = true + + let ringItem1 = OCKRingItem.init(title: "Medication Adherence", text: "Ibuprofen", tintColor: UIColor.green, value: 0.5, glyphType: .heart, glyphFilename: nil) + + let insightsDashboardViewController = OCKInsightsViewController.init(insightItems: [messageItem1, messageItem2,chart1, messageItem3, messageItem4, ringItem1]) self.navigationController?.pushViewController(insightsDashboardViewController, animated: true) } else if (indexPath as NSIndexPath).row == 5 { @@ -187,8 +189,6 @@ class InsightsTableViewController: UITableViewController { chart3.title = "3. Custom Chart" let insightsDashboardViewController = OCKInsightsViewController.init(insightItems: [chart1, chart2, chart3]) - insightsDashboardViewController.headerTitle = "Dashboard with Custom Chart" - insightsDashboardViewController.showEdgeIndicators = true self.navigationController?.pushViewController(insightsDashboardViewController, animated: true) } else if (indexPath as NSIndexPath).row == 6 { @@ -217,11 +217,23 @@ class InsightsTableViewController: UITableViewController { let chart6 = OCKBarChart.init(title: "Min & Max Int values", text: "Min = Int64.min, Max = Int64.max", tintColor: UIColor.green, axisTitles: nil, axisSubtitles: ["Min","Max"], dataSeries: [chart6Series1], minimumScaleRangeValue: NSNumber(value:Int64.min), maximumScaleRangeValue: NSNumber(value:Int64.max)) let insightsDashboardViewController = OCKInsightsViewController.init(insightItems: [chart1, chart2, chart3, chart4, chart5, chart6]) - insightsDashboardViewController.headerTitle = "Minimum/Maximum Scales" - insightsDashboardViewController.headerSubtitle = "With Edge Indicators" - insightsDashboardViewController.showEdgeIndicators = true self.navigationController?.pushViewController(insightsDashboardViewController, animated: true) + } else if(indexPath as NSIndexPath).row == 7 { + + let ringItem1 = OCKRingItem.init(title: "Medication Adherence", text: "Ibuprofen", tintColor: UIColor.red, value: 0.9, glyphType: .stethoscope, glyphFilename: nil) + + let ringItem2 = OCKRingItem.init(title: "Long Title for a Ring Item with no text", text: nil, tintColor: UIColor.purple, value: 0.5, glyphType: .stethoscope, glyphFilename: nil) + + let ringItem3 = OCKRingItem.init(title: "Title", text: "Long text for a Ring Item and small Title", tintColor: UIColor.green, value: 0.2, glyphType: .stethoscope, glyphFilename: nil) + + let ringItem4 = OCKRingItem.init(title: "Medication\nAdherence", text: "Multiline Title\nand text", tintColor: UIColor.cyan, value: 0.1, glyphType: .custom, glyphFilename: "club") + + let ringItem5 = OCKRingItem.init(title: "Medication Adherence", text: "Ibuprofen", tintColor: UIColor.yellow, value: 0.4, glyphType: .stethoscope, glyphFilename: nil) + + let insightsDashboardViewController = OCKInsightsViewController.init(insightItems: [ringItem1, ringItem2, ringItem3, ringItem4, ringItem5]) + self.navigationController?.pushViewController(insightsDashboardViewController, animated: true) + } } diff --git a/testing/OCKTest/OCKTest/SymptomTrackerTableViewController.swift b/testing/OCKTest/OCKTest/SymptomTrackerTableViewController.swift index c5fd1c188..817654185 100644 --- a/testing/OCKTest/OCKTest/SymptomTrackerTableViewController.swift +++ b/testing/OCKTest/OCKTest/SymptomTrackerTableViewController.swift @@ -42,7 +42,7 @@ class SymptomTrackerTableViewController: UITableViewController, OCKSymptomTracke } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return 5 + return 6 } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { @@ -63,6 +63,8 @@ class SymptomTrackerTableViewController: UITableViewController, OCKSymptomTracke } case 4: cell.textLabel?.text = "Delete all Activites" + case 5: + cell.textLabel?.text = "Custom Image" default: cell.textLabel?.text = nil } @@ -117,7 +119,6 @@ class SymptomTrackerTableViewController: UITableViewController, OCKSymptomTracke let carePlanStore = OCKCarePlanStore.init(persistenceDirectoryURL: URL.init(string: dataPath)!) let symptomTracker = OCKSymptomTrackerViewController.init(carePlanStore: carePlanStore) - symptomTracker.showEdgeIndicators = true self.navigationController?.pushViewController(symptomTracker, animated: true) } else if (indexPath as NSIndexPath).row == 2 { @@ -137,7 +138,7 @@ class SymptomTrackerTableViewController: UITableViewController, OCKSymptomTracke let secondGroupId = "Group A2" let carePlanActivity1 = OCKCarePlanActivity.init(identifier: "Assessment Activity #1", groupIdentifier: secondGroupId, type: OCKCarePlanActivityType.assessment, title: "Activity that ended yesterday ", text: "Read the instructions about this task", tintColor: nil, instructions: "Perform the described task and report the results. Talk to your doctor if you need help", imageURL: nil, schedule: schedule1, resultResettable: false, userInfo: nil) - let carePlanActivity2 = OCKCarePlanActivity.assessment(withIdentifier: "Assessment Activity #2", groupIdentifier: secondGroupId, title: "A Daily Activity is one that repeats every day", text: "This is an assessment. Be careful. You are being evaluated every single day.", tintColor: UIColor.purple, resultResettable: false, schedule: schedule2, userInfo: nil) + let carePlanActivity2 = OCKCarePlanActivity.assessment(withIdentifier: "Assessment Activity #2", groupIdentifier: secondGroupId, title: "A Daily Activity is one that repeats every day", text: "This is an assessment. Be careful. You are being evaluated every single day.", tintColor: UIColor.purple, resultResettable: false, schedule: schedule2, userInfo: nil, thresholds:nil, optional:false) let dataPath = documentsDirectory[0] + "/CarePlan2" if !FileManager.default.fileExists(atPath: dataPath) { @@ -157,9 +158,8 @@ class SymptomTrackerTableViewController: UITableViewController, OCKSymptomTracke }) let symptomTracker = OCKSymptomTrackerViewController.init(carePlanStore: carePlanStore) - symptomTracker.progressRingTintColor = UIColor.magenta + symptomTracker.glyphTintColor = UIColor.magenta symptomTracker.delegate = self - symptomTracker.showEdgeIndicators = true self.navigationController?.pushViewController(symptomTracker, animated: true) } else if (indexPath as NSIndexPath).row == 3 { @@ -186,32 +186,31 @@ class SymptomTrackerTableViewController: UITableViewController, OCKSymptomTracke let carePlanStore = OCKCarePlanStore.init(persistenceDirectoryURL: URL.init(string: dataPath)!) let thirdGroupId = "Group A3" - let carePlanActivity1 = OCKCarePlanActivity.assessment(withIdentifier: "Step Count", groupIdentifier: thirdGroupId, title: "Step Count", text: "Get steps from Health app", tintColor: UIColor.init(red: 0.3, green: 0.2, blue: 0.9, alpha: 0.4), resultResettable: true, schedule: schedule, userInfo: ["Type":"Steps"]) + let carePlanActivity1 = OCKCarePlanActivity.assessment(withIdentifier: "Step Count", groupIdentifier: thirdGroupId, title: "Step Count", text: "Get steps from Health app", tintColor: UIColor.init(red: 0.3, green: 0.2, blue: 0.9, alpha: 0.4), resultResettable: true, schedule: schedule, userInfo: ["Type":"Steps"], thresholds:nil, optional:false) carePlanStore.add(carePlanActivity1, completion: { (boolVal, error) in assert(boolVal, (error?.localizedDescription)!) }) - let carePlanActivity2 = OCKCarePlanActivity.assessment(withIdentifier: "Body Fat", groupIdentifier: thirdGroupId, title: "Body Fat", text: "Get Body Fat from Health app", tintColor: UIColor.init(red: 0.3, green: 0.2, blue: 0.9, alpha: 0.4), resultResettable: true, schedule: schedule, userInfo: ["Type":"BodyFat"]) + let carePlanActivity2 = OCKCarePlanActivity.assessment(withIdentifier: "Body Fat", groupIdentifier: thirdGroupId, title: "Body Fat", text: "Get Body Fat from Health app", tintColor: UIColor.init(red: 0.3, green: 0.2, blue: 0.9, alpha: 0.4), resultResettable: true, schedule: schedule, userInfo: ["Type":"BodyFat"], thresholds:nil, optional:false) carePlanStore.add(carePlanActivity2, completion: { (boolVal, error) in assert(boolVal, (error?.localizedDescription)!) }) - let carePlanActivity3 = OCKCarePlanActivity.assessment(withIdentifier: "Sleep Analysis", groupIdentifier: thirdGroupId, title: "Sleep Analysis", text: "Get Sleep Data from Health app", tintColor: UIColor.init(red: 0.3, green: 0.2, blue: 0.9, alpha: 0.4), resultResettable: true, schedule: schedule, userInfo: ["Type":"Sleep"]) + let carePlanActivity3 = OCKCarePlanActivity.assessment(withIdentifier: "Sleep Analysis", groupIdentifier: thirdGroupId, title: "Sleep Analysis", text: "Get Sleep Data from Health app", tintColor: UIColor.init(red: 0.3, green: 0.2, blue: 0.9, alpha: 0.4), resultResettable: true, schedule: schedule, userInfo: ["Type":"Sleep"], thresholds:nil, optional:false) carePlanStore.add(carePlanActivity3, completion: { (boolVal, error) in assert(boolVal, (error?.localizedDescription)!) }) - let carePlanActivity4 = OCKCarePlanActivity.assessment(withIdentifier: "Ovulation", groupIdentifier: thirdGroupId, title: "Ovulation", text: "Get Ovulation Data from Health app", tintColor: UIColor.init(red: 0.3, green: 0.2, blue: 0.9, alpha: 0.4), resultResettable: true, schedule: schedule, userInfo: ["Type":"Ovulation"]) + let carePlanActivity4 = OCKCarePlanActivity.assessment(withIdentifier: "Ovulation", groupIdentifier: thirdGroupId, title: "Ovulation", text: "Get Ovulation Data from Health app", tintColor: UIColor.init(red: 0.3, green: 0.2, blue: 0.9, alpha: 0.4), resultResettable: true, schedule: schedule, userInfo: ["Type":"Ovulation"], thresholds:nil, optional:false) carePlanStore.add(carePlanActivity4, completion: { (boolVal, error) in assert(boolVal, (error?.localizedDescription)!) }) - let carePlanActivity5 = OCKCarePlanActivity.assessment(withIdentifier: "Blood Pressure", groupIdentifier: thirdGroupId, title: "Blood Pressure", text: "Get Blood Pressure from Health app", tintColor: UIColor.init(red: 0.3, green: 0.2, blue: 0.9, alpha: 0.4), resultResettable: true, schedule: schedule, userInfo: ["Type":"Blood Pressure"]) + let carePlanActivity5 = OCKCarePlanActivity.assessment(withIdentifier: "Blood Pressure", groupIdentifier: thirdGroupId, title: "Blood Pressure", text: "Get Blood Pressure from Health app", tintColor: UIColor.init(red: 0.3, green: 0.2, blue: 0.9, alpha: 0.4), resultResettable: true, schedule: schedule, userInfo: ["Type":"Blood Pressure"], thresholds:nil, optional:false) carePlanStore.add(carePlanActivity5, completion: { (boolVal, error) in assert(boolVal, (error?.localizedDescription)!) }) let symptomTracker = OCKSymptomTrackerViewController.init(carePlanStore: carePlanStore) symptomTracker.delegate = self - symptomTracker.showEdgeIndicators = true - symptomTracker.progressRingTintColor = UIColor.init(red: 0.3, green: 0.2, blue: 0.9, alpha: 0.4) + symptomTracker.glyphTintColor = UIColor.init(red: 0.3, green: 0.2, blue: 0.9, alpha: 0.4) self.navigationController?.pushViewController(symptomTracker, animated: true) } else if (indexPath as NSIndexPath).row == 4 { @@ -270,6 +269,13 @@ class SymptomTrackerTableViewController: UITableViewController, OCKSymptomTracke } }) } + } else if (indexPath as NSIndexPath).row == 5 { + let carePlanStore = OCKCarePlanStore.init(persistenceDirectoryURL: URL.init(string: documentsDirectory[0])!) + + let symptomTracker = OCKSymptomTrackerViewController.init(carePlanStore: carePlanStore) + symptomTracker.glyphType = .infantCare + symptomTracker.glyphTintColor = UIColor.yellow + self.navigationController?.pushViewController(symptomTracker, animated: true) } } @@ -324,7 +330,7 @@ class SymptomTrackerTableViewController: UITableViewController, OCKSymptomTracke healthKitStore?.save([stepsSample, bodyFatSample, sleepSample, ovulationSample, bpSample], withCompletion: { (success, error) in if success == false { - print("Error saving Health Samples: \(error?.localizedDescription)") + print("Error saving Health Samples: \(error?.localizedDescription ?? "")") } else { print("Health data saved successfully!") } @@ -333,7 +339,7 @@ class SymptomTrackerTableViewController: UITableViewController, OCKSymptomTracke func symptomTrackerViewController(_ viewController: OCKSymptomTrackerViewController, didSelectRowWithAssessmentEvent assessmentEvent: OCKCarePlanEvent) { - if viewController.progressRingTintColor == UIColor.init(red: 0.3, green: 0.2, blue: 0.9, alpha: 0.4) + if viewController.glyphTintColor == UIColor.init(red: 0.3, green: 0.2, blue: 0.9, alpha: 0.4) { if String(describing: assessmentEvent.activity.userInfo!["Type"]!) == "Steps" { if (healthKitStore?.authorizationStatus(for: HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)!)) == HKAuthorizationStatus.sharingAuthorized {