@@ -14,86 +14,140 @@ class ContactImageUpdater {
1414 private let contactStore = CNContactStore ( )
1515 private let queue = DispatchQueue ( label: " ContactImageUpdaterQueue " )
1616
17- func updateContactImage( bgValue: String , extra: String , stale: Bool ) {
17+ private var savedBackgroundUIColor : UIColor {
18+ let rawValue = Storage . shared. contactBackgroundColor. value
19+ return ContactColorOption ( rawValue: rawValue) ? . uiColor ?? . black
20+ }
21+
22+ private var savedTextUIColor : UIColor {
23+ let rawValue = Storage . shared. contactTextColor. value
24+ return ContactColorOption ( rawValue: rawValue) ? . uiColor ?? . white
25+ }
26+
27+ func updateContactImage( bgValue: String , trend: String , delta: String , stale: Bool ) {
1828 queue. async {
1929 guard CNContactStore . authorizationStatus ( for: . contacts) == . authorized else {
2030 LogManager . shared. log ( category: . contact, message: " Access to contacts is not authorized. " )
2131 return
2232 }
2333
24- guard let imageData = self . generateContactImage ( bgValue: bgValue, extra: extra, stale: stale) ? . pngData ( ) else {
25- LogManager . shared. log ( category: . contact, message: " Failed to generate contact image. " )
26- return
27- }
28-
2934 let bundleDisplayName = Bundle . main. object ( forInfoDictionaryKey: " CFBundleDisplayName " ) as? String ?? " LoopFollow "
30- let contactName = " \( bundleDisplayName) - BG "
31- let predicate = CNContact . predicateForContacts ( matchingName: contactName)
32- let keysToFetch = [ CNContactGivenNameKey, CNContactFamilyNameKey, CNContactImageDataKey] as [ CNKeyDescriptor ]
33-
34- do {
35- let contacts = try self . contactStore. unifiedContacts ( matching: predicate, keysToFetch: keysToFetch)
36-
37- if let contact = contacts. first, let mutableContact = contact. mutableCopy ( ) as? CNMutableContact {
38- mutableContact. imageData = imageData
39- let saveRequest = CNSaveRequest ( )
40- saveRequest. update ( mutableContact)
41- try self . contactStore. execute ( saveRequest)
42- LogManager . shared. log ( category: . contact, message: " Contact image updated " , isDebug: true )
43- } else {
44- let newContact = CNMutableContact ( )
45- newContact. givenName = contactName
46- newContact. imageData = imageData
47- let saveRequest = CNSaveRequest ( )
48- saveRequest. add ( newContact, toContainerWithIdentifier: nil )
49- try self . contactStore. execute ( saveRequest)
50- LogManager . shared. log ( category: . contact, message: " New contact created " )
35+
36+ for contactType in ContactType . allCases {
37+ if contactType == . Delta && Storage . shared. contactDelta. value != . separate {
38+ continue
39+ }
40+
41+ if contactType == . Trend && Storage . shared. contactTrend. value != . separate {
42+ continue
43+ }
44+
45+ let contactName = " \( bundleDisplayName) - \( contactType. rawValue) "
46+
47+ guard let imageData = self . generateContactImage ( bgValue: bgValue, trend: trend, delta: delta, stale: stale, contactType: contactType) ? . pngData ( ) else {
48+ LogManager . shared. log ( category: . contact, message: " Failed to generate contact image for \( contactName) . " )
49+ continue
50+ }
51+
52+ let predicate = CNContact . predicateForContacts ( matchingName: contactName)
53+ let keysToFetch = [ CNContactGivenNameKey, CNContactFamilyNameKey, CNContactImageDataKey] as [ CNKeyDescriptor ]
54+
55+ do {
56+ let contacts = try self . contactStore. unifiedContacts ( matching: predicate, keysToFetch: keysToFetch)
57+
58+ if let contact = contacts. first, let mutableContact = contact. mutableCopy ( ) as? CNMutableContact {
59+ mutableContact. imageData = imageData
60+ let saveRequest = CNSaveRequest ( )
61+ saveRequest. update ( mutableContact)
62+ try self . contactStore. execute ( saveRequest)
63+ print ( " Contact image updated successfully for \( contactName) . " )
64+ } else {
65+ let newContact = CNMutableContact ( )
66+ newContact. givenName = contactName
67+ newContact. imageData = imageData
68+ let saveRequest = CNSaveRequest ( )
69+ saveRequest. add ( newContact, toContainerWithIdentifier: nil )
70+ try self . contactStore. execute ( saveRequest)
71+ print ( " New contact created with updated image for \( contactName) . " )
72+ }
73+ } catch {
74+ LogManager . shared. log ( category: . contact, message: " Failed to update or create contact for \( contactName) : \( error) " )
5175 }
52- } catch {
53- LogManager . shared. log ( category: . contact, message: " Failed to update or create contact: \( error) " )
5476 }
5577 }
5678 }
5779
58- private func generateContactImage( bgValue: String , extra : String , stale: Bool ) -> UIImage ? {
80+ private func generateContactImage( bgValue: String , trend : String , delta : String , stale: Bool , contactType : ContactType ) -> UIImage ? {
5981 let size = CGSize ( width: 300 , height: 300 )
6082 UIGraphicsBeginImageContextWithOptions ( size, false , 0 )
6183 guard let context = UIGraphicsGetCurrentContext ( ) else { return nil }
6284
63- UIColor . black . setFill ( )
85+ savedBackgroundUIColor . setFill ( )
6486 context. fill ( CGRect ( origin: . zero, size: size) )
6587
6688 let paragraphStyle = NSMutableParagraphStyle ( )
6789 paragraphStyle. alignment = . center
6890
69- let maxFontSize : CGFloat = extra. isEmpty ? 200 : 160
70- let fontSize = maxFontSize - CGFloat( bgValue. count * 15 )
71-
72- var bgAttributes : [ NSAttributedString . Key : Any ] = [
73- . font: UIFont . boldSystemFont ( ofSize: fontSize) ,
74- . foregroundColor: stale ? UIColor . gray : UIColor . white,
75- . paragraphStyle: paragraphStyle
76- ]
77-
78- if stale {
79- bgAttributes [ . strikethroughStyle] = NSUnderlineStyle . single. rawValue
80- }
91+ // Format extraDelta based on the user's unit preference
92+ let unitPreference = UserDefaultsRepository . units. value
93+ let yOffset : CGFloat = 48
94+ if contactType == . Trend && Storage . shared. contactTrend. value == . separate {
95+ let trendRect = CGRect ( x: 0 , y: 46 , width: size. width, height: size. height - 80 )
96+ let trendFontSize = max ( 40 , 200 - CGFloat( trend. count * 15 ) )
97+
98+ let trendAttributes : [ NSAttributedString . Key : Any ] = [
99+ . font: UIFont . boldSystemFont ( ofSize: trendFontSize) ,
100+ . foregroundColor: stale ? UIColor . gray : savedTextUIColor,
101+ . paragraphStyle: paragraphStyle
102+ ]
103+
104+ trend. draw ( in: trendRect, withAttributes: trendAttributes)
105+ } else if contactType == . Delta && Storage . shared. contactDelta. value == . separate {
106+ let deltaRect = CGRect ( x: 0 , y: yOffset, width: size. width, height: size. height - 80 )
107+ let deltaFontSize = max ( 40 , 200 - CGFloat( delta. count * 15 ) )
108+
109+ let deltaAttributes : [ NSAttributedString . Key : Any ] = [
110+ . font: UIFont . boldSystemFont ( ofSize: deltaFontSize) ,
111+ . foregroundColor: stale ? UIColor . gray : savedTextUIColor,
112+ . paragraphStyle: paragraphStyle
113+ ]
114+
115+ delta. draw ( in: deltaRect, withAttributes: deltaAttributes)
116+ } else if contactType == . BG {
117+ let includesExtra = Storage . shared. contactDelta. value == . include || Storage . shared. contactTrend. value == . include
118+
119+ let maxFontSize : CGFloat = includesExtra ? 160 : 200
120+ let fontSize = maxFontSize - CGFloat( bgValue. count * 15 )
121+ var bgAttributes : [ NSAttributedString . Key : Any ] = [
122+ . font: UIFont . boldSystemFont ( ofSize: fontSize) ,
123+ . foregroundColor: stale ? UIColor . gray : savedTextUIColor,
124+ . paragraphStyle: paragraphStyle
125+ ]
126+
127+ if stale {
128+ // Force background color back to black if stale
129+ UIColor . black. setFill ( )
130+ context. fill ( CGRect ( origin: . zero, size: size) )
131+ bgAttributes [ . strikethroughStyle] = NSUnderlineStyle . single. rawValue
132+ }
81133
82- let extraAttributes : [ NSAttributedString . Key : Any ] = [
83- . font: UIFont . systemFont ( ofSize: 90 ) ,
84- . foregroundColor: UIColor . white,
85- . paragraphStyle: paragraphStyle
86- ]
134+ let bgRect : CGRect = includesExtra
135+ ? CGRect ( x: 0 , y: yOffset - 20 , width: size. width, height: size. height / 2 )
136+ : CGRect ( x: 0 , y: yOffset, width: size. width, height: size. height - 80 )
87137
88- let bgRect = extra. isEmpty
89- ? CGRect ( x: 0 , y: 46 , width: size. width, height: size. height - 80 )
90- : CGRect ( x: 0 , y: 26 , width: size. width, height: size. height / 2 )
138+ bgValue. draw ( in: bgRect, withAttributes: bgAttributes)
91139
92- bgValue. draw ( in: bgRect, withAttributes: bgAttributes)
140+ if includesExtra {
141+ let extraRect = CGRect ( x: 0 , y: size. height / 2 + 6 , width: size. width, height: size. height / 2 - 20 )
142+ let extraAttributes : [ NSAttributedString . Key : Any ] = [
143+ . font: UIFont . systemFont ( ofSize: 90 ) ,
144+ . foregroundColor: stale ? UIColor . gray : savedTextUIColor,
145+ . paragraphStyle: paragraphStyle
146+ ]
93147
94- if ! extra. isEmpty {
95- let extraRect = CGRect ( x : 0 , y : size . height / 2 + 6 , width : size . width , height : size . height / 2 - 20 )
96- extra . draw ( in : extraRect , withAttributes : extraAttributes )
148+ let extra = Storage . shared . contactDelta . value == . include ? delta : trend
149+ extra . draw ( in : extraRect , withAttributes : extraAttributes )
150+ }
97151 }
98152
99153 let image = UIGraphicsGetImageFromCurrentImageContext ( )
0 commit comments