Commit bcaf2779df42c6710e796eeaa740d0f75bbe5bbe
1 parent
bdad0c11
feat(ui):添加watch静态UI
xxx feat(ui):完成watch UI
Showing
70 changed files
with
3073 additions
and
579 deletions
Too many changes to show.
To preserve performance only 70 of 70+ files are displayed.
| @@ -48,6 +48,16 @@ end | @@ -48,6 +48,16 @@ end | ||
| 48 | 48 | ||
| 49 | post_install do |installer| | 49 | post_install do |installer| |
| 50 | installer.pods_project.targets.each do |target| | 50 | installer.pods_project.targets.each do |target| |
| 51 | + is_watchos_target = target.build_configurations.any? do |config| | ||
| 52 | + config.build_settings['WATCHOS_DEPLOYMENT_TARGET'] | ||
| 53 | + end | ||
| 54 | + | ||
| 55 | + next if is_watchos_target | ||
| 56 | + | ||
| 51 | flutter_additional_ios_build_settings(target) | 57 | flutter_additional_ios_build_settings(target) |
| 58 | + | ||
| 59 | + target.build_configurations.each do |config| | ||
| 60 | + config.build_settings['ENABLE_USER_SCRIPT_SANDBOXING'] = 'NO' | ||
| 61 | + end | ||
| 52 | end | 62 | end |
| 53 | end | 63 | end |
| @@ -92,7 +92,7 @@ EXTERNAL SOURCES: | @@ -92,7 +92,7 @@ EXTERNAL SOURCES: | ||
| 92 | :path: ".symlinks/plugins/webview_flutter_wkwebview/darwin" | 92 | :path: ".symlinks/plugins/webview_flutter_wkwebview/darwin" |
| 93 | 93 | ||
| 94 | SPEC CHECKSUMS: | 94 | SPEC CHECKSUMS: |
| 95 | - Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 | 95 | + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 |
| 96 | fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1 | 96 | fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1 |
| 97 | image_cropper: c4326ea50132b1e1564499e5d32a84f01fb03537 | 97 | image_cropper: c4326ea50132b1e1564499e5d32a84f01fb03537 |
| 98 | image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a | 98 | image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a |
| @@ -108,6 +108,6 @@ SPEC CHECKSUMS: | @@ -108,6 +108,6 @@ SPEC CHECKSUMS: | ||
| 108 | TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654 | 108 | TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654 |
| 109 | webview_flutter_wkwebview: 1821ceac936eba6f7984d89a9f3bcb4dea99ebb2 | 109 | webview_flutter_wkwebview: 1821ceac936eba6f7984d89a9f3bcb4dea99ebb2 |
| 110 | 110 | ||
| 111 | -PODFILE CHECKSUM: 11b37be67b2ce5b4cca7654ac969f9b9789ac4bf | 111 | +PODFILE CHECKSUM: c331311b0ccb42e8d5cf37701560080ce4a73ef8 |
| 112 | 112 | ||
| 113 | COCOAPODS: 1.16.2 | 113 | COCOAPODS: 1.16.2 |
| 1 | +// | ||
| 2 | +// DFCoupleStressHomeView.swift | ||
| 3 | +// iwatch | ||
| 4 | +// | ||
| 5 | +// Created by 权海 on 2026/6/15. | ||
| 6 | +// | ||
| 7 | + | ||
| 8 | +import SwiftUI | ||
| 9 | + | ||
| 10 | +struct DFCoupleStressHomeView: View { | ||
| 11 | + var hasData: Bool = true | ||
| 12 | + | ||
| 13 | + var body: some View { | ||
| 14 | + VStack { | ||
| 15 | + DFWatchFigmaHeader(subtitle: hasData ? "我们的状态·HRV" : "我们的状态·实时") | ||
| 16 | + Spacer().frame(height: 9) | ||
| 17 | + HStack(spacing: 4){ | ||
| 18 | + DFCouplePersonCard( | ||
| 19 | + kind:.overloaded, | ||
| 20 | + name: "男朋友", | ||
| 21 | + value: hasData ? "30ms" : "-%", | ||
| 22 | + ).frame(maxWidth: .infinity) | ||
| 23 | + DFCouplePersonCard( | ||
| 24 | + kind: .excellent, | ||
| 25 | + name: "我", | ||
| 26 | + value: hasData ? "80ms" : "-%", | ||
| 27 | + ).frame(maxWidth: .infinity) | ||
| 28 | + } | ||
| 29 | + Spacer().frame(height: 5) | ||
| 30 | + HStack{ | ||
| 31 | + DFWatchHeartBeatCircle(progress: 0, value: nil) | ||
| 32 | + .frame(width: 45, height: 44) | ||
| 33 | + Spacer() | ||
| 34 | + DFWatchCloseCircle(outerProgress: 0, middleProgress: 0, innerProgress: 0) | ||
| 35 | + .frame(width: 44, height: 44) | ||
| 36 | + Spacer() | ||
| 37 | + DFWatchStepCountCircle(progress: 0, value: 0) | ||
| 38 | + .frame(width: 44, height: 44) | ||
| 39 | + } | ||
| 40 | + }.padding(.all, 16) | ||
| 41 | + } | ||
| 42 | +} | ||
| 43 | + | ||
| 44 | +private struct DFCouplePersonCard: View { | ||
| 45 | + let kind: DFWatchStressKind? | ||
| 46 | + let name: String | ||
| 47 | + let value: String? | ||
| 48 | + | ||
| 49 | + var body: some View { | ||
| 50 | + VStack(spacing: 4) { | ||
| 51 | + HStack{} | ||
| 52 | + .frame(width: 55, height: 55) | ||
| 53 | + .background(Color(hex: "#FF0000")) | ||
| 54 | + Text(kind?.title ?? "等待数据") | ||
| 55 | + .font(.system(size: 12, weight: .semibold)) | ||
| 56 | + .foregroundColor(kind?.color ?? Color(hex: "#B0B0B6")) | ||
| 57 | + Text(name) | ||
| 58 | + .font(.system(size: 10, weight: .medium)) | ||
| 59 | + .foregroundColor(.white) | ||
| 60 | + Text(value ?? "--ms") | ||
| 61 | + .font(.system(size: 8, weight: .light)) | ||
| 62 | + .foregroundColor(.white) | ||
| 63 | + } | ||
| 64 | + } | ||
| 65 | +} |
| 1 | +// | ||
| 2 | +// File.swift | ||
| 3 | +// iwatch | ||
| 4 | +// | ||
| 5 | +// Created by 权海 on 2026/6/15. | ||
| 6 | +// | ||
| 7 | + | ||
| 8 | +import SwiftUI | ||
| 9 | + | ||
| 10 | +struct DFSingleStressHomeView: View { | ||
| 11 | + var kind: DFWatchStressKind = .overloaded | ||
| 12 | + var value: String? = "38ms" | ||
| 13 | + var sampleTime: String = "16:06" | ||
| 14 | + | ||
| 15 | + private var valueDesc: String{ | ||
| 16 | + if let value{ | ||
| 17 | + return kind.title + "·" + value | ||
| 18 | + } | ||
| 19 | + return "等待数据" | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + var body: some View { | ||
| 23 | + ZStack(alignment: .top) { | ||
| 24 | + LinearGradient( | ||
| 25 | + colors: [kind.color.opacity(0.3), kind.color.opacity(0)], | ||
| 26 | + startPoint: .top, | ||
| 27 | + endPoint: .bottom | ||
| 28 | + ) | ||
| 29 | + .frame(width: .infinity, height: .infinity) | ||
| 30 | + VStack(spacing: 8){ | ||
| 31 | + DFWatchFigmaHeader(subtitle: "HRV") | ||
| 32 | + .padding(.top, 16) | ||
| 33 | + HStack{} | ||
| 34 | + .frame(width: 78, height: 78) | ||
| 35 | + .background(Color(hex: "#FF0000")) | ||
| 36 | + Text(valueDesc) | ||
| 37 | + .font(.system(size: 16, weight: .semibold)) | ||
| 38 | + .foregroundStyle(value == nil ? Color(hex: "#B0B0B6") : kind.color) | ||
| 39 | + DFWatchFigmaSegmentBar(selected: kind) | ||
| 40 | + .frame(height: 14) | ||
| 41 | + Text(sampleTime) | ||
| 42 | + .font(.system(size: 12)) | ||
| 43 | + .foregroundStyle(.white) | ||
| 44 | + }.padding(.horizontal, value == nil ? 30 : 22) | ||
| 45 | + } | ||
| 46 | + } | ||
| 47 | +} |
| 1 | +// | ||
| 2 | +// DFWatchCloseCircle.swift | ||
| 3 | +// iwatch | ||
| 4 | +// | ||
| 5 | +// Created by 权海 on 2026/6/12. | ||
| 6 | +// | ||
| 7 | + | ||
| 8 | +import SwiftUI | ||
| 9 | + | ||
| 10 | +struct DFWatchCloseCircle: View { | ||
| 11 | + var outerProgress: CGFloat | ||
| 12 | + var middleProgress: CGFloat | ||
| 13 | + var innerProgress: CGFloat | ||
| 14 | + | ||
| 15 | + var outerColor: Color = Color(hex: "#FF5279") | ||
| 16 | + var middleColor: Color = Color(hex: "#3BD49D") | ||
| 17 | + var innerColor: Color = Color(hex: "#7B9BFB") | ||
| 18 | + | ||
| 19 | + var backgroundColor: Color = .black | ||
| 20 | + | ||
| 21 | + var body: some View { | ||
| 22 | + GeometryReader { geometry in | ||
| 23 | + let size = min(geometry.size.width, geometry.size.height) | ||
| 24 | + let lineWidth: CGFloat = size * 0.13 | ||
| 25 | + | ||
| 26 | + ZStack { | ||
| 27 | + backgroundColor | ||
| 28 | + | ||
| 29 | + RingView( | ||
| 30 | + progress: outerProgress, | ||
| 31 | + color: outerColor, | ||
| 32 | + lineWidth: lineWidth | ||
| 33 | + ) | ||
| 34 | + .frame( | ||
| 35 | + width: size * 0.87, | ||
| 36 | + height: size * 0.87 | ||
| 37 | + ) | ||
| 38 | + | ||
| 39 | + RingView( | ||
| 40 | + progress: middleProgress, | ||
| 41 | + color: middleColor, | ||
| 42 | + lineWidth: lineWidth | ||
| 43 | + ) | ||
| 44 | + .frame( | ||
| 45 | + width: size * 0.55, | ||
| 46 | + height: size * 0.55 | ||
| 47 | + ) | ||
| 48 | + | ||
| 49 | + RingView( | ||
| 50 | + progress: innerProgress, | ||
| 51 | + color: innerColor, | ||
| 52 | + lineWidth: lineWidth | ||
| 53 | + ) | ||
| 54 | + .frame( | ||
| 55 | + width: size * 0.24, | ||
| 56 | + height: size * 0.24 | ||
| 57 | + ) | ||
| 58 | + | ||
| 59 | + Circle() | ||
| 60 | + .fill(backgroundColor) | ||
| 61 | + .frame( | ||
| 62 | + width: size * 0.15, | ||
| 63 | + height: size * 0.15 | ||
| 64 | + ) | ||
| 65 | + } | ||
| 66 | + .frame(width: size, height: size) | ||
| 67 | + .clipShape(Circle()) | ||
| 68 | + } | ||
| 69 | + .aspectRatio(1, contentMode: .fit) | ||
| 70 | + } | ||
| 71 | +} | ||
| 72 | +struct RingView: View { | ||
| 73 | + var progress: CGFloat | ||
| 74 | + var color: Color | ||
| 75 | + var lineWidth: CGFloat | ||
| 76 | + | ||
| 77 | + var body: some View { | ||
| 78 | + ZStack{ | ||
| 79 | + Circle() | ||
| 80 | + .stroke( | ||
| 81 | + color, | ||
| 82 | + style: StrokeStyle( | ||
| 83 | + lineWidth: lineWidth, | ||
| 84 | + lineCap: .round, | ||
| 85 | + lineJoin: .round | ||
| 86 | + ) | ||
| 87 | + ) | ||
| 88 | + .opacity(0.3) | ||
| 89 | + Circle() | ||
| 90 | + .trim(from: 0, to: max(0, min(progress, 1))) | ||
| 91 | + .stroke( | ||
| 92 | + color, | ||
| 93 | + style: StrokeStyle( | ||
| 94 | + lineWidth: lineWidth, | ||
| 95 | + lineCap: .round, | ||
| 96 | + lineJoin: .round | ||
| 97 | + ) | ||
| 98 | + ) | ||
| 99 | + .rotationEffect(.degrees(-90)) | ||
| 100 | + } | ||
| 101 | + } | ||
| 102 | +} |
| 1 | +// | ||
| 2 | +// DFWatchCloseView.swift | ||
| 3 | +// iwatch | ||
| 4 | +// | ||
| 5 | +// Created by 权海 on 2026/6/12. | ||
| 6 | +// | ||
| 7 | + | ||
| 8 | +import SwiftUI | ||
| 9 | + | ||
| 10 | + | ||
| 11 | +struct DFWatchCloseView: View { | ||
| 12 | + let closeOnClick: (() -> Void)? | ||
| 13 | + var body: some View { | ||
| 14 | + Button { | ||
| 15 | + closeOnClick?() | ||
| 16 | + } label: { | ||
| 17 | + Image(systemName: "xmark") | ||
| 18 | + .fontWeight(.bold) | ||
| 19 | + .tint(.white) | ||
| 20 | + | ||
| 21 | + } | ||
| 22 | + .frame(width: 32, height: 32) | ||
| 23 | + .background(.white.opacity(0.1)) | ||
| 24 | + .clipShape( | ||
| 25 | + RoundedRectangle(cornerRadius: 16) | ||
| 26 | + ).padding(.leading, 14) | ||
| 27 | + } | ||
| 28 | +} | ||
| 29 | + |
| 1 | +import SwiftUI | ||
| 2 | + | ||
| 3 | +struct DFTodayStatusOverviewFigmaView: View { | ||
| 4 | + var hasData: Bool = true | ||
| 5 | + | ||
| 6 | + private var rows: [DFStatusOverviewRow] { | ||
| 7 | + [ | ||
| 8 | + .init(title: "平均HRV", value: hasData ? "29" : "-", unit: "ms", icon: "waveform.path.ecg", tint: DFWatchFigmaColor.blue), | ||
| 9 | + .init(title: "最新心率", value: hasData ? "79" : "-", unit: "次/分", icon: "heart.fill", tint: DFWatchFigmaColor.red), | ||
| 10 | + .init(title: "步数", value: hasData ? "11198" : "-", unit: "步", icon: "figure.walk", tint: DFWatchFigmaColor.green), | ||
| 11 | + .init(title: "活动记录", value: hasData ? "400" : "-", unit: "大卡", icon: "flame.fill", tint: DFWatchFigmaColor.orange) | ||
| 12 | + ] | ||
| 13 | + } | ||
| 14 | + | ||
| 15 | + var body: some View { | ||
| 16 | + ZStack{ | ||
| 17 | + HStack{ | ||
| 18 | + Spacer() | ||
| 19 | + } | ||
| 20 | + VStack(alignment: .trailing){ | ||
| 21 | + Text(.now, style: .time) | ||
| 22 | + .font(.system(size: 14, weight: .medium)) | ||
| 23 | + .foregroundColor(.white) | ||
| 24 | + Text("我的今日状态") | ||
| 25 | + .lineLimit(1) | ||
| 26 | + .font(.system(size: 16, weight: .medium)) | ||
| 27 | + .minimumScaleFactor(0.7) | ||
| 28 | + .foregroundColor(.white) | ||
| 29 | + ScrollView { | ||
| 30 | + ForEach(rows) { row in | ||
| 31 | + DFStatusOverviewCard(row: row) | ||
| 32 | + .padding(.bottom, 4) | ||
| 33 | + } | ||
| 34 | + Spacer() | ||
| 35 | + .frame(height: 24) | ||
| 36 | + } | ||
| 37 | + }.padding(EdgeInsets(top: 10, leading: 10, bottom: 0, trailing: 10)) | ||
| 38 | + } | ||
| 39 | + } | ||
| 40 | +} | ||
| 41 | + | ||
| 42 | +private struct DFStatusOverviewRow: Identifiable { | ||
| 43 | + let id = UUID() | ||
| 44 | + let title: String | ||
| 45 | + let value: String | ||
| 46 | + let unit: String | ||
| 47 | + let icon: String | ||
| 48 | + let tint: Color | ||
| 49 | +} | ||
| 50 | + | ||
| 51 | +private struct DFStatusOverviewCard: View { | ||
| 52 | + let row: DFStatusOverviewRow | ||
| 53 | + | ||
| 54 | + var body: some View { | ||
| 55 | + HStack(spacing: 0) { | ||
| 56 | + Image(systemName: row.icon) | ||
| 57 | + .frame(width: 18, height: 18) | ||
| 58 | + .foregroundColor(row.tint) | ||
| 59 | + Spacer() | ||
| 60 | + .frame(width: 6) | ||
| 61 | + Text(row.title) | ||
| 62 | + .font(.system(size: 12, weight: .medium)) | ||
| 63 | + .lineLimit(1) | ||
| 64 | + .minimumScaleFactor(0.7) | ||
| 65 | + .foregroundColor(.white) | ||
| 66 | + Spacer(minLength: 0) | ||
| 67 | + Text(row.value) | ||
| 68 | + .font(.system(size: 12, weight: .medium)) | ||
| 69 | + .lineLimit(1) | ||
| 70 | + .minimumScaleFactor(0.7) | ||
| 71 | + .foregroundColor(.white) | ||
| 72 | + Spacer() | ||
| 73 | + .frame(width: 2) | ||
| 74 | + Text(row.unit) | ||
| 75 | + .font(.system(size: 12, weight: .regular)) | ||
| 76 | + .lineLimit(1) | ||
| 77 | + .minimumScaleFactor(0.7) | ||
| 78 | + .foregroundColor(.white.opacity(0.5)) | ||
| 79 | + } | ||
| 80 | + .padding(.horizontal, 8) | ||
| 81 | + .padding(.vertical, 10) | ||
| 82 | + .background(Color.white.opacity(0.10)) | ||
| 83 | + .clipShape(RoundedRectangle(cornerRadius: 10)) | ||
| 84 | + } | ||
| 85 | +} | ||
| 86 | + | ||
| 87 | +struct DFWatchSettingsFigmaView: View { | ||
| 88 | + | ||
| 89 | + var body: some View { | ||
| 90 | + ZStack(alignment: .topTrailing){ | ||
| 91 | + HStack{ | ||
| 92 | + Spacer() | ||
| 93 | + } | ||
| 94 | + VStack(alignment: .leading){ | ||
| 95 | + Spacer() | ||
| 96 | + .frame(height: 14) | ||
| 97 | + DFWatchCloseView { | ||
| 98 | + | ||
| 99 | + } | ||
| 100 | + Spacer() | ||
| 101 | + .frame(height: 6) | ||
| 102 | + HStack{ | ||
| 103 | + Text("刷新模式") | ||
| 104 | + .font(.system(size: 16, weight: .medium)) | ||
| 105 | + .foregroundColor(.white) | ||
| 106 | + DFWatchFigmaProBadge() | ||
| 107 | + Spacer() | ||
| 108 | + }.padding(.leading, 16) | ||
| 109 | + ScrollView { | ||
| 110 | + VStack(alignment: .leading, spacing: 8) { | ||
| 111 | + DFSettingOption(title: "HRV", accessory: .check) | ||
| 112 | + DFSettingOption(title: "实时压力", accessory: .lock) | ||
| 113 | + Text("刷新模式说明") | ||
| 114 | + .font(.system(size: 9, weight: .regular)) | ||
| 115 | + .underline() | ||
| 116 | + .foregroundColor(DFWatchFigmaColor.purpleText) | ||
| 117 | + .padding(.leading, 8) | ||
| 118 | + }.padding(.horizontal, 10) | ||
| 119 | + .padding(.bottom, 24) | ||
| 120 | + } | ||
| 121 | + } | ||
| 122 | + Text(.now, style: .time) | ||
| 123 | + .font(.system(size: 14, weight: .medium)) | ||
| 124 | + .foregroundColor(.white) | ||
| 125 | + .padding(.trailing, 14) | ||
| 126 | + .padding(.top, 10) | ||
| 127 | + } | ||
| 128 | + } | ||
| 129 | +} | ||
| 130 | + | ||
| 131 | +private enum DFSettingAccessory { | ||
| 132 | + case check | ||
| 133 | + case lock | ||
| 134 | +} | ||
| 135 | + | ||
| 136 | +private struct DFSettingOption: View { | ||
| 137 | + let title: String | ||
| 138 | + let accessory: DFSettingAccessory | ||
| 139 | + | ||
| 140 | + var body: some View { | ||
| 141 | + HStack { | ||
| 142 | + Text(title) | ||
| 143 | + .font(.system(size: 12, weight: .medium)) | ||
| 144 | + .foregroundColor(.white) | ||
| 145 | + Spacer() | ||
| 146 | + switch accessory { | ||
| 147 | + case .check: | ||
| 148 | + Image(systemName: "checkmark") | ||
| 149 | + .font(.system(size: 15, weight: .bold)) | ||
| 150 | + .foregroundColor(.white) | ||
| 151 | + case .lock: | ||
| 152 | + Image(systemName: "lock.fill") | ||
| 153 | + .font(.system(size: 15, weight: .medium)) | ||
| 154 | + .foregroundColor(.white.opacity(0.84)) | ||
| 155 | + } | ||
| 156 | + } | ||
| 157 | + .frame(height: 38) | ||
| 158 | + .padding(.horizontal, 8) | ||
| 159 | + .background(Color.white.opacity(0.10)) | ||
| 160 | + .clipShape(RoundedRectangle(cornerRadius: 10)) | ||
| 161 | + } | ||
| 162 | +} | ||
| 163 | + | ||
| 164 | +struct DFProRequiredInfoFigmaView: View { | ||
| 165 | + var body: some View { | ||
| 166 | + ZStack(alignment: .topTrailing){ | ||
| 167 | + HStack{ | ||
| 168 | + Spacer() | ||
| 169 | + } | ||
| 170 | + VStack(alignment: .center){ | ||
| 171 | + Spacer() | ||
| 172 | + .frame(height: 14) | ||
| 173 | + HStack{ | ||
| 174 | + DFWatchCloseView { | ||
| 175 | + | ||
| 176 | + } | ||
| 177 | + Spacer() | ||
| 178 | + } | ||
| 179 | + Spacer() | ||
| 180 | + .frame(height: 6) | ||
| 181 | + DFWatchFigmaProBadge() | ||
| 182 | + Spacer() | ||
| 183 | + .frame(height: 10) | ||
| 184 | + ScrollView { | ||
| 185 | + VStack(alignment: .leading, spacing: 11) { | ||
| 186 | + Text("该功能需要DoubleFeel Pro会员才可使用。你可以前往手机App开通会员。") | ||
| 187 | + Text("如果已经开通会员,但手表端还没有显示,可以先退出当前页面,再重新进入,会员状态通常会自动更新。") | ||
| 188 | + } | ||
| 189 | + .font(.system(size: 11, weight: .regular)) | ||
| 190 | + .foregroundColor(.white) | ||
| 191 | + .lineSpacing(2) | ||
| 192 | + .padding(.horizontal, 12) | ||
| 193 | + .padding(.bottom, 24) | ||
| 194 | + } | ||
| 195 | + } | ||
| 196 | + Text(.now, style: .time) | ||
| 197 | + .font(.system(size: 14, weight: .medium)) | ||
| 198 | + .foregroundColor(.white) | ||
| 199 | + .padding(.trailing, 14) | ||
| 200 | + .padding(.top, 10) | ||
| 201 | + } | ||
| 202 | + } | ||
| 203 | +} | ||
| 204 | + | ||
| 205 | +struct DFRefreshModeInfoFigmaView: View { | ||
| 206 | + var body: some View { | ||
| 207 | + ZStack(alignment: .topTrailing){ | ||
| 208 | + HStack{ | ||
| 209 | + Spacer() | ||
| 210 | + } | ||
| 211 | + VStack(alignment: .leading){ | ||
| 212 | + Spacer() | ||
| 213 | + .frame(height: 14) | ||
| 214 | + DFWatchCloseView { | ||
| 215 | + | ||
| 216 | + } | ||
| 217 | + Spacer() | ||
| 218 | + .frame(height: 8) | ||
| 219 | + ScrollView { | ||
| 220 | + VStack(alignment: .leading){ | ||
| 221 | + Text("HRV") | ||
| 222 | + .font(.system(size: 14, weight: .medium)) | ||
| 223 | + .foregroundColor(.white) | ||
| 224 | + Spacer() | ||
| 225 | + .frame(height: 4) | ||
| 226 | + Text(""" | ||
| 227 | + 一般情况下,Apple Watch 会每隔一段时间自动采集一次 HRV 数据。 | ||
| 228 | + 在佩戴稳定、身体静止或放松状态下,系统更容易获取有效 HRV 数据。 | ||
| 229 | + 剧烈运动、频繁活动、手表佩戴不稳定或刚解锁 Apple Watch 后,数据同步可能会延迟。 | ||
| 230 | + Apple Watch 与 iPhone 的健康数据同步由 Apple Health 系统完成,因此并不会始终实时更新。 | ||
| 231 | + """) | ||
| 232 | + .font(.system(size: 9, weight: .regular)) | ||
| 233 | + .foregroundColor(.white.opacity(0.8)) | ||
| 234 | + Spacer() | ||
| 235 | + .frame(height: 14) | ||
| 236 | + Text("实时压力") | ||
| 237 | + .font(.system(size: 14, weight: .medium)) | ||
| 238 | + .foregroundColor(.white) | ||
| 239 | + Spacer() | ||
| 240 | + .frame(height: 4) | ||
| 241 | + Text(""" | ||
| 242 | +实时压力会结合当前 HRV、心率状态与个人历史数据动态变化。 | ||
| 243 | +通常情况下: | ||
| 244 | + · 手机 App 会自动刷新当天综合压力状态 | ||
| 245 | + · Apple Watch 会显示最近一次的实时压力变化 | ||
| 246 | +由于 Apple Health 数据同步存在延迟,实时压力可能不会立即更新。 | ||
| 247 | +""") | ||
| 248 | + .font(.system(size: 9, weight: .regular)) | ||
| 249 | + .foregroundColor(.white.opacity(0.8)) | ||
| 250 | + Spacer() | ||
| 251 | + .frame(height: 14) | ||
| 252 | + Divider() | ||
| 253 | + .overlay(Color.white.opacity(0.25)) | ||
| 254 | + Spacer() | ||
| 255 | + .frame(height: 14) | ||
| 256 | + Text(""" | ||
| 257 | +以下情况可能导致数据延迟或暂时不可用: | ||
| 258 | + · Apple Watch 处于低电量模式 | ||
| 259 | + · Apple Health 权限未开启 | ||
| 260 | + · 长时间未佩戴 Apple Watch | ||
| 261 | + · 正在运动或身体频繁活动 | ||
| 262 | + · 手表刚解锁或刚重新佩戴 | ||
| 263 | + · 系统尚未完成数据同步 | ||
| 264 | +""") | ||
| 265 | + .font(.system(size: 8, weight: .regular)) | ||
| 266 | + .foregroundColor(.white.opacity(0.8)) | ||
| 267 | + }.padding(.horizontal, 10) | ||
| 268 | + .padding(.bottom, 24) | ||
| 269 | + } | ||
| 270 | + } | ||
| 271 | + VStack(alignment: .trailing){ | ||
| 272 | + Text(.now, style: .time) | ||
| 273 | + .font(.system(size: 14, weight: .medium)) | ||
| 274 | + .foregroundColor(.white) | ||
| 275 | + Text("HRV") | ||
| 276 | + .font(.system(size: 12, weight: .medium)) | ||
| 277 | + .foregroundColor(.white) | ||
| 278 | + .opacity(0.6) | ||
| 279 | + } | ||
| 280 | + .padding(.trailing, 14) | ||
| 281 | + .padding(.top, 10) | ||
| 282 | + } | ||
| 283 | + } | ||
| 284 | +} | ||
| 285 | + | ||
| 286 | +struct DFWatchFigmaDetailPreviewGallery: View { | ||
| 287 | + var body: some View { | ||
| 288 | + TabView { | ||
| 289 | + DFTodayStatusOverviewFigmaView(hasData: true) | ||
| 290 | + DFTodayStatusOverviewFigmaView(hasData: false) | ||
| 291 | + DFWatchSettingsFigmaView() | ||
| 292 | + DFProRequiredInfoFigmaView() | ||
| 293 | + DFRefreshModeInfoFigmaView() | ||
| 294 | + } | ||
| 295 | +#if os(watchOS) | ||
| 296 | + .tabViewStyle(.verticalPage) | ||
| 297 | +#else | ||
| 298 | + .tabViewStyle(.page) | ||
| 299 | +#endif | ||
| 300 | + } | ||
| 301 | +} | ||
| 302 | + | ||
| 303 | +#Preview("Figma watch detail states") { | ||
| 304 | + DFWatchFigmaDetailPreviewGallery() | ||
| 305 | +} |
| 1 | +import SwiftUI | ||
| 2 | +import UIKit | ||
| 3 | + | ||
| 4 | +struct DFDefaultWatchHomeView: View { | ||
| 5 | + @State private var batteryLevel: Float = UIDevice.current.batteryLevel | ||
| 6 | + @State private var batteryState: UIDevice.BatteryState = UIDevice.current.batteryState | ||
| 7 | + | ||
| 8 | + @State private var weekday: String = "" | ||
| 9 | + @State private var day: String = "" | ||
| 10 | + | ||
| 11 | + var hasData: Bool = false | ||
| 12 | + var hrvValue: String = "38ms" | ||
| 13 | + var hrvTime: String = "19:04" | ||
| 14 | + | ||
| 15 | + var batteryColor: Color{ | ||
| 16 | + if batteryLevel < 0.1{ | ||
| 17 | + // 0 | ||
| 18 | + return Color(hex: "#FF0000") | ||
| 19 | + }else if batteryLevel < 40{ | ||
| 20 | + // 25 | ||
| 21 | + return Color(hex: "#FF9A6E") | ||
| 22 | + }else if batteryLevel < 60{ | ||
| 23 | + // 50 | ||
| 24 | + return Color(hex: "#7B9BFB") | ||
| 25 | + }else if batteryLevel < 80{ | ||
| 26 | + // 75 | ||
| 27 | + return Color(hex: "#3BD49D") | ||
| 28 | + }else{ | ||
| 29 | + // 100 | ||
| 30 | + return Color(hex: "#3BD49D") | ||
| 31 | + } | ||
| 32 | + } | ||
| 33 | + var batteryImageName: String{ | ||
| 34 | + if batteryLevel < 0.1{ | ||
| 35 | + // 0 | ||
| 36 | + return "battery.0percent" | ||
| 37 | + }else if batteryLevel < 40{ | ||
| 38 | + // 25 | ||
| 39 | + return "battery.25percent" | ||
| 40 | + }else if batteryLevel < 60{ | ||
| 41 | + // 50 | ||
| 42 | + return "battery.50percent" | ||
| 43 | + }else if batteryLevel < 80{ | ||
| 44 | + // 75 | ||
| 45 | + return "battery.75percent" | ||
| 46 | + }else{ | ||
| 47 | + // 100 | ||
| 48 | + return "battery.100percent" | ||
| 49 | + } | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + var body: some View { | ||
| 53 | + VStack{ | ||
| 54 | + HStack{ | ||
| 55 | + Image(systemName: batteryImageName) | ||
| 56 | + .frame(height: 20) | ||
| 57 | + .tint(batteryColor) | ||
| 58 | + Spacer() | ||
| 59 | + Text(weekday) | ||
| 60 | + .font(.system(size: 16, weight: .medium)) | ||
| 61 | + .foregroundColor(DFWatchFigmaColor.purple) | ||
| 62 | + Spacer().frame(width: 4) | ||
| 63 | + Text(day) | ||
| 64 | + .font(.system(size: 16, weight: .medium)) | ||
| 65 | + .foregroundColor(.white) | ||
| 66 | + } | ||
| 67 | + Text(.now, style: .time) | ||
| 68 | + .font(.system(size: 52, weight: .medium)) | ||
| 69 | + .foregroundColor(.white) | ||
| 70 | + .frame(height: 50) | ||
| 71 | + HStack(alignment: .center){ | ||
| 72 | + HStack{} | ||
| 73 | + .frame(width: 44, height: 44) | ||
| 74 | + .background(Color(hex: "#FF0000")) | ||
| 75 | + Spacer().frame(width: 14) | ||
| 76 | + VStack(alignment: .leading, spacing: 0) { | ||
| 77 | + Text(hasData ? "状态优秀" : "暂无数据") | ||
| 78 | + .font(.system(size: 16, weight: .semibold)) | ||
| 79 | + .foregroundColor(hasData ? DFWatchFigmaColor.green : DFWatchFigmaColor.grayText) | ||
| 80 | + Text(hasData ? "\(hrvValue)·\(hrvTime)" : "--ms·--:--") | ||
| 81 | + .font(.system(size: 12, weight: .medium)) | ||
| 82 | + .foregroundColor(.white) | ||
| 83 | + DFWatchFigmaSegmentBar(selected: nil) | ||
| 84 | + .frame(height: 14) | ||
| 85 | + } | ||
| 86 | + } | ||
| 87 | + HStack{ | ||
| 88 | + DFWatchHeartBeatCircle(progress: 0, value: nil) | ||
| 89 | + .frame(width: 45, height: 44) | ||
| 90 | + Spacer() | ||
| 91 | + DFWatchCloseCircle(outerProgress: 0, middleProgress: 0, innerProgress: 0) | ||
| 92 | + .frame(width: 44, height: 44) | ||
| 93 | + Spacer() | ||
| 94 | + DFWatchStepCountCircle(progress: 0, value: 0) | ||
| 95 | + .frame(width: 44, height: 44) | ||
| 96 | + } | ||
| 97 | + } | ||
| 98 | + .padding(.all, 15) | ||
| 99 | + .onAppear { | ||
| 100 | + updateDate() | ||
| 101 | + updateBattery() | ||
| 102 | + NotificationCenter.default.addObserver( | ||
| 103 | + forName: UIDevice.batteryLevelDidChangeNotification, | ||
| 104 | + object: nil, | ||
| 105 | + queue: .main | ||
| 106 | + ) { _ in | ||
| 107 | + updateBattery() | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + NotificationCenter.default.addObserver( | ||
| 111 | + forName: UIDevice.batteryStateDidChangeNotification, | ||
| 112 | + object: nil, | ||
| 113 | + queue: .main | ||
| 114 | + ) { _ in | ||
| 115 | + updateBattery() | ||
| 116 | + } | ||
| 117 | + } | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + private func updateBattery() { | ||
| 121 | + UIDevice.current.isBatteryMonitoringEnabled = true | ||
| 122 | + batteryLevel = UIDevice.current.batteryLevel | ||
| 123 | + batteryState = UIDevice.current.batteryState | ||
| 124 | + } | ||
| 125 | + private func updateDate(){ | ||
| 126 | + let calendar = Calendar.current | ||
| 127 | + let component = calendar.component(.weekday, from: .now) | ||
| 128 | + weekday = calendar.shortWeekdaySymbols[component].uppercased() | ||
| 129 | + | ||
| 130 | + let formatter = DateFormatter() | ||
| 131 | + formatter.dateFormat = "dd" | ||
| 132 | + day = formatter.string(from: .now) | ||
| 133 | + } | ||
| 134 | +} |
| 1 | +import SwiftUI | ||
| 2 | + | ||
| 3 | +enum DFWatchStressKind: String { | ||
| 4 | + case excellent | ||
| 5 | + case normal | ||
| 6 | + case little | ||
| 7 | + case overloaded | ||
| 8 | + | ||
| 9 | + var title: String { | ||
| 10 | + switch self { | ||
| 11 | + case .excellent: | ||
| 12 | + return "状态优秀" | ||
| 13 | + case .overloaded: | ||
| 14 | + return "压力过载" | ||
| 15 | + case .normal: | ||
| 16 | + return "状态正常" | ||
| 17 | + case .little: | ||
| 18 | + return "注意压力" | ||
| 19 | + } | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + var color: Color { | ||
| 23 | + switch self { | ||
| 24 | + case .excellent: | ||
| 25 | + return DFWatchFigmaColor.green | ||
| 26 | + case .overloaded: | ||
| 27 | + return DFWatchFigmaColor.red | ||
| 28 | + case .normal: | ||
| 29 | + return DFWatchFigmaColor.blue | ||
| 30 | + case .little: | ||
| 31 | + return DFWatchFigmaColor.orange | ||
| 32 | + } | ||
| 33 | + } | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +enum DFWatchFigmaColor { | ||
| 37 | + static let background = Color(hex: "0F0F11") | ||
| 38 | + static let purple = Color(hex: "845EEE") | ||
| 39 | + static let purpleText = Color(hex: "A285F4") | ||
| 40 | + static let green = Color(hex: "3BD49D") | ||
| 41 | + static let red = Color(hex: "FF5279") | ||
| 42 | + static let orange = Color(hex: "FF9A6E") | ||
| 43 | + static let blue = Color(hex: "7B9BFB") | ||
| 44 | + static let grayText = Color(hex: "B0B0B6") | ||
| 45 | + static let grayBar = Color(hex: "78787D") | ||
| 46 | + static let pro = Color(hex: "FFDF51") | ||
| 47 | +} | ||
| 48 | + | ||
| 49 | +struct DFWatchFigmaCanvas<Content: View>: View { | ||
| 50 | + private let designSize = CGSize(width: 352, height: 430) | ||
| 51 | + let content: (CGFloat) -> Content | ||
| 52 | + | ||
| 53 | + init(@ViewBuilder content: @escaping (CGFloat) -> Content) { | ||
| 54 | + self.content = content | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + var body: some View { | ||
| 58 | + GeometryReader { proxy in | ||
| 59 | + let scale = min(proxy.size.width / designSize.width, proxy.size.height / designSize.height) | ||
| 60 | + ZStack { | ||
| 61 | + DFWatchFigmaColor.background | ||
| 62 | + content(scale) | ||
| 63 | + } | ||
| 64 | + .frame(width: designSize.width * scale, height: designSize.height * scale) | ||
| 65 | + .position(x: proxy.size.width / 2, y: proxy.size.height / 2) | ||
| 66 | + .clipped() | ||
| 67 | + } | ||
| 68 | + .background(DFWatchFigmaColor.background) | ||
| 69 | + .ignoresSafeArea() | ||
| 70 | + } | ||
| 71 | +} | ||
| 72 | + | ||
| 73 | +extension CGFloat { | ||
| 74 | + func df(_ scale: CGFloat) -> CGFloat { | ||
| 75 | + self * scale | ||
| 76 | + } | ||
| 77 | +} | ||
| 78 | + | ||
| 79 | +extension Int { | ||
| 80 | + func df(_ scale: CGFloat) -> CGFloat { | ||
| 81 | + CGFloat(self) * scale | ||
| 82 | + } | ||
| 83 | +} | ||
| 84 | + | ||
| 85 | +extension Double { | ||
| 86 | + func df(_ scale: CGFloat) -> CGFloat { | ||
| 87 | + CGFloat(self) * scale | ||
| 88 | + } | ||
| 89 | +} | ||
| 90 | + | ||
| 91 | +struct DFWatchFigmaHeader: View { | ||
| 92 | + var subtitle: String? | ||
| 93 | + | ||
| 94 | + var body: some View { | ||
| 95 | + HStack { | ||
| 96 | + Circle() | ||
| 97 | + .fill(Color.white.opacity(0.12)) | ||
| 98 | + .frame(width: 32, height: 32) | ||
| 99 | + .overlay( | ||
| 100 | + Image(systemName: "gearshape.fill") | ||
| 101 | + .font(.system(size: 14, weight: .medium)) | ||
| 102 | + .foregroundColor(.white) | ||
| 103 | + ) | ||
| 104 | + Spacer() | ||
| 105 | + VStack(alignment: .trailing, spacing: 1) { | ||
| 106 | + Text(.now, style: .time) | ||
| 107 | + .font(.system(size: 14, weight: .medium)) | ||
| 108 | + .foregroundColor(.white) | ||
| 109 | + if let subtitle { | ||
| 110 | + Text(subtitle) | ||
| 111 | + .font(.system(size: 12, weight: .regular)) | ||
| 112 | + .foregroundColor(.white.opacity(0.6)) | ||
| 113 | + } | ||
| 114 | + } | ||
| 115 | + } | ||
| 116 | + } | ||
| 117 | +} | ||
| 118 | + | ||
| 119 | +struct DFWatchFigmaProBadge: View { | ||
| 120 | + var body: some View { | ||
| 121 | + HStack(spacing: 0) { | ||
| 122 | + Image(systemName: "crown.fill") | ||
| 123 | + .font(.system(size: 7, weight: .bold)) | ||
| 124 | + .frame(width: 13, height: 13) | ||
| 125 | + Text("PRO") | ||
| 126 | + .font(.system(size: 9, weight: .medium)) | ||
| 127 | + } | ||
| 128 | + .foregroundColor(DFWatchFigmaColor.background) | ||
| 129 | + .padding(.horizontal, 4) | ||
| 130 | + .background(DFWatchFigmaColor.pro) | ||
| 131 | + .clipShape(Capsule()) | ||
| 132 | + } | ||
| 133 | +} | ||
| 134 | + | ||
| 135 | +struct DFWatchFigmaSegmentBar: View { | ||
| 136 | + let selected: DFWatchStressKind? | ||
| 137 | + | ||
| 138 | + let allKinds: [DFWatchStressKind] = [.overloaded, .little, .normal, .excellent] | ||
| 139 | + let spacing: CGFloat = 2 | ||
| 140 | + | ||
| 141 | + var body: some View { | ||
| 142 | + if let selected{ | ||
| 143 | + GeometryReader { proxy in | ||
| 144 | + HStack(spacing: spacing) { | ||
| 145 | + let extraWidth: CGFloat = proxy.size.width / 5 | ||
| 146 | + let availableWidth = proxy.size.width - CGFloat(allKinds.count - 1) * spacing - extraWidth | ||
| 147 | + let itemWidth = floor(availableWidth/4) | ||
| 148 | + ForEach(allKinds, id: \.rawValue){kind in | ||
| 149 | + DFWatchKindBar(width: selected == kind ? (itemWidth + extraWidth) : itemWidth, kind: kind, selectedKind: selected) | ||
| 150 | + } | ||
| 151 | + } | ||
| 152 | + } | ||
| 153 | + }else{ | ||
| 154 | + Capsule() | ||
| 155 | + .fill(Color(hex: "#78787D")) | ||
| 156 | + .frame(width: .infinity, height: 8) | ||
| 157 | + } | ||
| 158 | + } | ||
| 159 | +} | ||
| 160 | + | ||
| 161 | +struct DFWatchKindBar: View { | ||
| 162 | + let width: CGFloat | ||
| 163 | + let kind: DFWatchStressKind | ||
| 164 | + let selectedKind: DFWatchStressKind | ||
| 165 | + | ||
| 166 | + var isSelected: Bool{ | ||
| 167 | + kind == selectedKind | ||
| 168 | + } | ||
| 169 | + | ||
| 170 | + var body: some View { | ||
| 171 | + ZStack{ | ||
| 172 | + Capsule() | ||
| 173 | + .fill(kind.color) | ||
| 174 | + .frame(width: width, height: 8) | ||
| 175 | + if isSelected{ | ||
| 176 | + Capsule() | ||
| 177 | + .stroke(kind.color, lineWidth: 4) | ||
| 178 | + .frame(width: 14, height: 14) | ||
| 179 | + .background(.black) | ||
| 180 | + } | ||
| 181 | + } | ||
| 182 | + } | ||
| 183 | +} | ||
| 184 | + | ||
| 185 | +struct DFWatchFigmaBottomMetrics: View { | ||
| 186 | + let scale: CGFloat | ||
| 187 | + var hasData: Bool | ||
| 188 | + var heartRate: String = "89" | ||
| 189 | + var steps: String = "2486" | ||
| 190 | + | ||
| 191 | + var body: some View { | ||
| 192 | + ZStack { | ||
| 193 | + DFMetricBubble(scale: scale, value: hasData ? heartRate : "0", icon: "heart.fill", tint: DFWatchFigmaColor.red) | ||
| 194 | + .position(x: 69.df(scale), y: 362.df(scale)) | ||
| 195 | + DFMetricBubble(scale: scale, value: "", icon: "sparkles", tint: DFWatchFigmaColor.purple) | ||
| 196 | + .position(x: 176.df(scale), y: 362.df(scale)) | ||
| 197 | + DFMetricBubble(scale: scale, value: hasData ? steps : "0", icon: "figure.walk", tint: DFWatchFigmaColor.green) | ||
| 198 | + .position(x: 287.df(scale), y: 362.df(scale)) | ||
| 199 | + } | ||
| 200 | + } | ||
| 201 | +} | ||
| 202 | + | ||
| 203 | +private struct DFMetricBubble: View { | ||
| 204 | + let scale: CGFloat | ||
| 205 | + let value: String | ||
| 206 | + let icon: String | ||
| 207 | + let tint: Color | ||
| 208 | + | ||
| 209 | + var body: some View { | ||
| 210 | + ZStack { | ||
| 211 | + Circle() | ||
| 212 | + .fill(Color.white.opacity(0.10)) | ||
| 213 | + .frame(width: 71.df(scale), height: 71.df(scale)) | ||
| 214 | + Circle() | ||
| 215 | + .trim(from: 0, to: value.isEmpty ? 1 : 0.72) | ||
| 216 | + .stroke(tint, style: StrokeStyle(lineWidth: max(1, 5.df(scale)), lineCap: .round)) | ||
| 217 | + .rotationEffect(.degrees(-90)) | ||
| 218 | + .frame(width: 63.df(scale), height: 63.df(scale)) | ||
| 219 | + VStack(spacing: 7.df(scale)) { | ||
| 220 | + Text(value) | ||
| 221 | + .font(.system(size: 18.df(scale), weight: .medium)) | ||
| 222 | + .foregroundColor(.white) | ||
| 223 | + .opacity(value.isEmpty ? 0 : 1) | ||
| 224 | + Image(systemName: icon) | ||
| 225 | + .font(.system(size: 18.df(scale), weight: .semibold)) | ||
| 226 | + .foregroundColor(.white) | ||
| 227 | + } | ||
| 228 | + } | ||
| 229 | + .frame(width: 82.df(scale), height: 82.df(scale)) | ||
| 230 | + } | ||
| 231 | +} |
| 1 | +// | ||
| 2 | +// DFWatchHeartBeatCircle.swift | ||
| 3 | +// iwatch | ||
| 4 | +// | ||
| 5 | +// Created by 权海 on 2026/6/12. | ||
| 6 | +// | ||
| 7 | + | ||
| 8 | +import SwiftUI | ||
| 9 | + | ||
| 10 | +struct DFWatchHeartBeatCircle: View { | ||
| 11 | + /// 0...1 | ||
| 12 | + var progress: CGFloat | ||
| 13 | + | ||
| 14 | + /// 中间显示的分数 | ||
| 15 | + var value: Int? | ||
| 16 | + | ||
| 17 | + var lineWidth: CGFloat = 6 | ||
| 18 | + var markerSize: CGFloat = 8 | ||
| 19 | + | ||
| 20 | + private let gradientColors: [Color] = [ | ||
| 21 | + Color(hex: "#3BD49D"), | ||
| 22 | + Color(hex: "#7B9BFB"), | ||
| 23 | + Color(hex: "#FF9A6E"), | ||
| 24 | + Color(hex: "#FF5279") | ||
| 25 | + ] | ||
| 26 | + | ||
| 27 | + var body: some View { | ||
| 28 | + GeometryReader { geometry in | ||
| 29 | + let size = min(geometry.size.width, geometry.size.height) | ||
| 30 | + let arcSize = size - markerSize | ||
| 31 | + let startAngle: CGFloat = 90 | ||
| 32 | + let sweepAngle: CGFloat = 270 | ||
| 33 | + let rotation = 0.128 * 360 | ||
| 34 | + | ||
| 35 | + let clampedProgress = min(max(progress, 0), 1) | ||
| 36 | + let markerAngle = (startAngle + rotation) + sweepAngle * clampedProgress | ||
| 37 | + | ||
| 38 | + ZStack { | ||
| 39 | + Color.black | ||
| 40 | + // 渐变进度圆弧 | ||
| 41 | + Circle() | ||
| 42 | + .trim(from: 0.25, to: 1) | ||
| 43 | + .stroke( | ||
| 44 | + AngularGradient( | ||
| 45 | + gradient: Gradient(colors: gradientColors), | ||
| 46 | + center: .center, | ||
| 47 | + startAngle: .degrees(Double(startAngle)), | ||
| 48 | + endAngle: .degrees(Double(startAngle + sweepAngle)) | ||
| 49 | + ), | ||
| 50 | + style: StrokeStyle( | ||
| 51 | + lineWidth: lineWidth, | ||
| 52 | + lineCap: .round, | ||
| 53 | + lineJoin: .round | ||
| 54 | + ) | ||
| 55 | + ) | ||
| 56 | + .frame(width: arcSize, height: arcSize) | ||
| 57 | + .rotationEffect(.degrees(rotation)) | ||
| 58 | + .opacity(value == nil ? 0.3 : 1) | ||
| 59 | + | ||
| 60 | + // 中间数值 | ||
| 61 | + Text("\(value ?? 0)") | ||
| 62 | + .font(.system(size: 9, weight: .bold, design: .rounded)) | ||
| 63 | + .lineLimit(1) | ||
| 64 | + .foregroundStyle(.white) | ||
| 65 | + .frame(maxWidth: geometry.size.width - 20) | ||
| 66 | + .minimumScaleFactor(0.8) | ||
| 67 | + .offset(y: -size * 0.02) | ||
| 68 | + | ||
| 69 | + // 底部爱心 | ||
| 70 | + HeartBadgeView() | ||
| 71 | + .frame(width: 10, height: 10) | ||
| 72 | + .offset(y: geometry.size.height - 28) | ||
| 73 | + | ||
| 74 | + // 当前进度 marker | ||
| 75 | + if let _ = value{ | ||
| 76 | + let markerPoint = pointOnCircle( | ||
| 77 | + angle: markerAngle, | ||
| 78 | + radius: arcSize / 2, | ||
| 79 | + center: CGPoint(x: size / 2, y: size / 2) | ||
| 80 | + ) | ||
| 81 | + | ||
| 82 | + Circle() | ||
| 83 | + .fill(.clear) | ||
| 84 | + .frame(width: markerSize, height: markerSize) | ||
| 85 | + .overlay( | ||
| 86 | + Circle() | ||
| 87 | + .stroke(Color.black, lineWidth: 2) | ||
| 88 | + ) | ||
| 89 | + .position(markerPoint) | ||
| 90 | + } | ||
| 91 | + } | ||
| 92 | + .frame(width: size, height: size) | ||
| 93 | + } | ||
| 94 | + .aspectRatio(1, contentMode: .fit) | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + private func pointOnCircle( | ||
| 98 | + angle: CGFloat, | ||
| 99 | + radius: CGFloat, | ||
| 100 | + center: CGPoint | ||
| 101 | + ) -> CGPoint { | ||
| 102 | + let radians = angle * .pi / 180 | ||
| 103 | + return CGPoint( | ||
| 104 | + x: center.x + cos(radians) * radius, | ||
| 105 | + y: center.y + sin(radians) * radius | ||
| 106 | + ) | ||
| 107 | + } | ||
| 108 | +} | ||
| 109 | + | ||
| 110 | +struct HeartBadgeView: View { | ||
| 111 | + var body: some View { | ||
| 112 | + ZStack { | ||
| 113 | + Image(systemName: "heart.fill") | ||
| 114 | + .resizable() | ||
| 115 | + .scaledToFit() | ||
| 116 | + .foregroundStyle(Color(hex: "#FF5279")) | ||
| 117 | + | ||
| 118 | + Image(systemName: "waveform.path.ecg") | ||
| 119 | + .font(.system(size: 8, weight: .bold)) | ||
| 120 | + .minimumScaleFactor(0.9) | ||
| 121 | + .foregroundStyle(.black) | ||
| 122 | + .scaledToFit() | ||
| 123 | + .padding(.all, 2) | ||
| 124 | + } | ||
| 125 | + } | ||
| 126 | +} |
| 1 | +// | ||
| 2 | +// DFWatchStepCountCircle.swift | ||
| 3 | +// iwatch | ||
| 4 | +// | ||
| 5 | +// Created by 权海 on 2026/6/12. | ||
| 6 | +// | ||
| 7 | + | ||
| 8 | +import SwiftUI | ||
| 9 | + | ||
| 10 | +struct DFWatchStepCountCircle: View { | ||
| 11 | + /// 0...1 | ||
| 12 | + var progress: CGFloat | ||
| 13 | + | ||
| 14 | + /// 中间显示的分数 | ||
| 15 | + var value: Int | ||
| 16 | + | ||
| 17 | + var lineWidth: CGFloat = 6 | ||
| 18 | + var markerSize: CGFloat = 8 | ||
| 19 | + | ||
| 20 | + private let gradientColors: [Color] = [ | ||
| 21 | + Color(hex: "#845EEE"), | ||
| 22 | + Color(hex: "#845EEE"), | ||
| 23 | + ] | ||
| 24 | + | ||
| 25 | + var body: some View { | ||
| 26 | + GeometryReader { geometry in | ||
| 27 | + let size = min(geometry.size.width, geometry.size.height) | ||
| 28 | + let arcSize = size - markerSize | ||
| 29 | + let startAngle: CGFloat = 90 | ||
| 30 | + let sweepAngle: CGFloat = 270 | ||
| 31 | + let rotation = 0.128 * 360 | ||
| 32 | + | ||
| 33 | + let clampedProgress = min(max(progress, 0), 1) | ||
| 34 | + | ||
| 35 | + ZStack { | ||
| 36 | + Color.black | ||
| 37 | + // 渐变进度圆弧 | ||
| 38 | + Circle() | ||
| 39 | + .trim(from: 0.25, to: 1) | ||
| 40 | + .stroke( | ||
| 41 | + AngularGradient( | ||
| 42 | + gradient: Gradient(colors: gradientColors), | ||
| 43 | + center: .center, | ||
| 44 | + startAngle: .degrees(Double(startAngle)), | ||
| 45 | + endAngle: .degrees(Double(startAngle + sweepAngle)) | ||
| 46 | + ), | ||
| 47 | + style: StrokeStyle( | ||
| 48 | + lineWidth: lineWidth, | ||
| 49 | + lineCap: .round, | ||
| 50 | + lineJoin: .round | ||
| 51 | + ) | ||
| 52 | + ) | ||
| 53 | + .frame(width: arcSize, height: arcSize) | ||
| 54 | + .rotationEffect(.degrees(rotation)) | ||
| 55 | + .opacity(0.3) | ||
| 56 | + Circle() | ||
| 57 | + .trim(from: 0.25, to: 0.25 + progress * 3/4) | ||
| 58 | + .stroke( | ||
| 59 | + AngularGradient( | ||
| 60 | + gradient: Gradient(colors: gradientColors), | ||
| 61 | + center: .center, | ||
| 62 | + startAngle: .degrees(Double(startAngle)), | ||
| 63 | + endAngle: .degrees(Double(startAngle + sweepAngle)) | ||
| 64 | + ), | ||
| 65 | + style: StrokeStyle( | ||
| 66 | + lineWidth: lineWidth, | ||
| 67 | + lineCap: .round, | ||
| 68 | + lineJoin: .round | ||
| 69 | + ) | ||
| 70 | + ) | ||
| 71 | + .frame(width: arcSize, height: arcSize) | ||
| 72 | + .rotationEffect(.degrees(rotation)) | ||
| 73 | + | ||
| 74 | + // 中间数值 | ||
| 75 | + Text("\(value)") | ||
| 76 | + .font(.system(size: 9, weight: .bold, design: .rounded)) | ||
| 77 | + .lineLimit(1) | ||
| 78 | + .foregroundStyle(.white) | ||
| 79 | + .frame(maxWidth: geometry.size.width - 20) | ||
| 80 | + .minimumScaleFactor(0.8) | ||
| 81 | + .offset(y: -size * 0.02) | ||
| 82 | + | ||
| 83 | + // 底部爱心 | ||
| 84 | + FootprintsIcon(color: gradientColors.first!) | ||
| 85 | + .frame(width: 14, height: 14) | ||
| 86 | + .offset(y: geometry.size.height - 28) | ||
| 87 | + } | ||
| 88 | + .frame(width: size, height: size) | ||
| 89 | + } | ||
| 90 | + .aspectRatio(1, contentMode: .fit) | ||
| 91 | + } | ||
| 92 | +} | ||
| 93 | + | ||
| 94 | +struct FootprintsIcon: View { | ||
| 95 | + var color: Color | ||
| 96 | + | ||
| 97 | + var body: some View { | ||
| 98 | + Image(systemName: "shoeprints.fill") | ||
| 99 | + .resizable() | ||
| 100 | + .scaledToFit() | ||
| 101 | + .foregroundStyle(color) | ||
| 102 | + } | ||
| 103 | +} |
| @@ -49,8 +49,8 @@ class StatusComparisonViewModel: ObservableObject { | @@ -49,8 +49,8 @@ class StatusComparisonViewModel: ObservableObject { | ||
| 49 | 49 | ||
| 50 | private func getMyWatchTheme() { | 50 | private func getMyWatchTheme() { |
| 51 | // 1️⃣ 先从 UserDefaults 读取缓存 | 51 | // 1️⃣ 先从 UserDefaults 读取缓存 |
| 52 | - let defaults = UserDefaults(suiteName: "group.com.luiz.doublefeel.watchkitapp") | ||
| 53 | - if let jsonString = defaults?.string(forKey: "myWatchTheme"), | 52 | + let defaults = AppGroupConstants.defaults |
| 53 | + if let jsonString = defaults?.string(forKey: AppGroupConstants.Key.myWatchTheme), | ||
| 54 | let jsonData = jsonString.data(using: .utf8), | 54 | let jsonData = jsonString.data(using: .utf8), |
| 55 | let cachedModel = try? JSONDecoder().decode(WatchThemeModel.self, from: jsonData) { | 55 | let cachedModel = try? JSONDecoder().decode(WatchThemeModel.self, from: jsonData) { |
| 56 | // 先展示缓存数据 | 56 | // 先展示缓存数据 |
| @@ -87,8 +87,8 @@ class StatusComparisonViewModel: ObservableObject { | @@ -87,8 +87,8 @@ class StatusComparisonViewModel: ObservableObject { | ||
| 87 | guard WatchUserinfoManager.share.myUserinfo?.isPaired == true else { return } | 87 | guard WatchUserinfoManager.share.myUserinfo?.isPaired == true else { return } |
| 88 | 88 | ||
| 89 | // 1️⃣ 先从 UserDefaults 读取缓存 | 89 | // 1️⃣ 先从 UserDefaults 读取缓存 |
| 90 | - let defaults = UserDefaults(suiteName: "group.com.luiz.doublefeel.watchkitapp") | ||
| 91 | - if let jsonString = defaults?.string(forKey: "otherWatchTheme"), | 90 | + let defaults = AppGroupConstants.defaults |
| 91 | + if let jsonString = defaults?.string(forKey: AppGroupConstants.Key.otherWatchTheme), | ||
| 92 | let jsonData = jsonString.data(using: .utf8), | 92 | let jsonData = jsonString.data(using: .utf8), |
| 93 | let cachedModel = try? JSONDecoder().decode(WatchThemeModel.self, from: jsonData) { | 93 | let cachedModel = try? JSONDecoder().decode(WatchThemeModel.self, from: jsonData) { |
| 94 | // 先展示缓存数据 | 94 | // 先展示缓存数据 |
| @@ -167,7 +167,7 @@ class StatusComparisonViewModel: ObservableObject { | @@ -167,7 +167,7 @@ class StatusComparisonViewModel: ObservableObject { | ||
| 167 | guard let fileName else {return} | 167 | guard let fileName else {return} |
| 168 | 168 | ||
| 169 | guard let containerURL = FileManager.default.containerURL( | 169 | guard let containerURL = FileManager.default.containerURL( |
| 170 | - forSecurityApplicationGroupIdentifier: "group.com.luiz.doublefeel.watchkitapp" | 170 | + forSecurityApplicationGroupIdentifier: AppGroupConstants.identifier |
| 171 | ) else { | 171 | ) else { |
| 172 | print("❌ App Group 容器不存在,请检查配置") | 172 | print("❌ App Group 容器不存在,请检查配置") |
| 173 | return | 173 | return |
| @@ -497,18 +497,18 @@ extension WatchDataManager { | @@ -497,18 +497,18 @@ extension WatchDataManager { | ||
| 497 | } | 497 | } |
| 498 | 498 | ||
| 499 | private func saveHRVValue(_ value: Double) { | 499 | private func saveHRVValue(_ value: Double) { |
| 500 | - let defaults = UserDefaults(suiteName: "group.com.luiz.doublefeel.watchkitapp") | ||
| 501 | - defaults?.set(value, forKey: "latestHRV") | 500 | + let defaults = AppGroupConstants.defaults |
| 501 | + defaults?.set(value, forKey: AppGroupConstants.Key.latestHRV) | ||
| 502 | } | 502 | } |
| 503 | 503 | ||
| 504 | private func saveHRVBaselineValue(_ value: Double?) { | 504 | private func saveHRVBaselineValue(_ value: Double?) { |
| 505 | - let defaults = UserDefaults(suiteName: "group.com.luiz.doublefeel.watchkitapp") | ||
| 506 | - defaults?.set(value, forKey: "latestHRVBaseline") | 505 | + let defaults = AppGroupConstants.defaults |
| 506 | + defaults?.set(value, forKey: AppGroupConstants.Key.latestHRVBaseline) | ||
| 507 | } | 507 | } |
| 508 | 508 | ||
| 509 | private func saveStepCountValue(_ value: Int) { | 509 | private func saveStepCountValue(_ value: Int) { |
| 510 | - let defaults = UserDefaults(suiteName: "group.com.luiz.doublefeel.watchkitapp") | ||
| 511 | - defaults?.set(value, forKey: "latestStepCount") | 510 | + let defaults = AppGroupConstants.defaults |
| 511 | + defaults?.set(value, forKey: AppGroupConstants.Key.latestStepCount) | ||
| 512 | } | 512 | } |
| 513 | } | 513 | } |
| 514 | 514 |
| @@ -70,7 +70,7 @@ class WatchUserinfoManager: ObservableObject { | @@ -70,7 +70,7 @@ class WatchUserinfoManager: ObservableObject { | ||
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | private func saveMyUserCharacter(_ character: Int) { | 72 | private func saveMyUserCharacter(_ character: Int) { |
| 73 | - let defaults = UserDefaults(suiteName: "group.com.luiz.doublefeel.watchkitapp") | ||
| 74 | - defaults?.set(character, forKey: "myUserCharacter") | 73 | + let defaults = AppGroupConstants.defaults |
| 74 | + defaults?.set(character, forKey: AppGroupConstants.Key.myUserCharacter) | ||
| 75 | } | 75 | } |
| 76 | } | 76 | } |
| @@ -3,12 +3,15 @@ | @@ -3,12 +3,15 @@ | ||
| 3 | archiveVersion = 1; | 3 | archiveVersion = 1; |
| 4 | classes = { | 4 | classes = { |
| 5 | }; | 5 | }; |
| 6 | - objectVersion = 54; | 6 | + objectVersion = 77; |
| 7 | objects = { | 7 | objects = { |
| 8 | 8 | ||
| 9 | /* Begin PBXBuildFile section */ | 9 | /* Begin PBXBuildFile section */ |
| 10 | 0798E27DB95F2A823881D75A /* Pods_Runner_Watch_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A488EFC0BD642ED604C4E43 /* Pods_Runner_Watch_App.framework */; }; | 10 | 0798E27DB95F2A823881D75A /* Pods_Runner_Watch_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A488EFC0BD642ED604C4E43 /* Pods_Runner_Watch_App.framework */; }; |
| 11 | 20A3C4994C9B0F60D1F3BCBE /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8DC48B3736B7FEB548D0E65 /* Pods_Runner.framework */; }; | 11 | 20A3C4994C9B0F60D1F3BCBE /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8DC48B3736B7FEB548D0E65 /* Pods_Runner.framework */; }; |
| 12 | + 66FB19002FDBC80100F515B4 /* Runner Watch App/FigmaHome/DFWatchFigmaTokens.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66FB19032FDBC80100F515B4 /* Runner Watch App/FigmaHome/DFWatchFigmaTokens.swift */; }; | ||
| 13 | + 66FB19012FDBC80100F515B4 /* Runner Watch App/FigmaHome/DFWatchFigmaHomeViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66FB19042FDBC80100F515B4 /* Runner Watch App/FigmaHome/DFWatchFigmaHomeViews.swift */; }; | ||
| 14 | + 66FB19022FDBC80100F515B4 /* Runner Watch App/FigmaHome/DFWatchFigmaDetailViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66FB19052FDBC80100F515B4 /* Runner Watch App/FigmaHome/DFWatchFigmaDetailViews.swift */; }; | ||
| 12 | 66FBE58C2FDA4F0F00F515B4 /* Runner Watch App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 66FBE58B2FDA4F0F00F515B4 /* Runner Watch App.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; | 15 | 66FBE58C2FDA4F0F00F515B4 /* Runner Watch App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 66FBE58B2FDA4F0F00F515B4 /* Runner Watch App.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; |
| 13 | /* End PBXBuildFile section */ | 16 | /* End PBXBuildFile section */ |
| 14 | 17 | ||
| @@ -51,6 +54,9 @@ | @@ -51,6 +54,9 @@ | ||
| 51 | 27E4D5A8995AB1EC91E7330E /* Pods-Runner Watch App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner Watch App.debug.xcconfig"; path = "Target Support Files/Pods-Runner Watch App/Pods-Runner Watch App.debug.xcconfig"; sourceTree = "<group>"; }; | 54 | 27E4D5A8995AB1EC91E7330E /* Pods-Runner Watch App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner Watch App.debug.xcconfig"; path = "Target Support Files/Pods-Runner Watch App/Pods-Runner Watch App.debug.xcconfig"; sourceTree = "<group>"; }; |
| 52 | 4A9B7C102FDB0A1200F515B4 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; }; | 55 | 4A9B7C102FDB0A1200F515B4 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; }; |
| 53 | 4A9B7C112FDB0A1200F515B4 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; }; | 56 | 4A9B7C112FDB0A1200F515B4 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; }; |
| 57 | + 66FB19032FDBC80100F515B4 /* Runner Watch App/FigmaHome/DFWatchFigmaTokens.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Runner Watch App/FigmaHome/DFWatchFigmaTokens.swift"; sourceTree = "<group>"; }; | ||
| 58 | + 66FB19042FDBC80100F515B4 /* Runner Watch App/FigmaHome/DFWatchFigmaHomeViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Runner Watch App/FigmaHome/DFWatchFigmaHomeViews.swift"; sourceTree = "<group>"; }; | ||
| 59 | + 66FB19052FDBC80100F515B4 /* Runner Watch App/FigmaHome/DFWatchFigmaDetailViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Runner Watch App/FigmaHome/DFWatchFigmaDetailViews.swift"; sourceTree = "<group>"; }; | ||
| 54 | 66FBE57E2FDA4F0E00F515B4 /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; | 60 | 66FBE57E2FDA4F0E00F515B4 /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; |
| 55 | 66FBE58B2FDA4F0F00F515B4 /* Runner Watch App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Runner Watch App.app"; sourceTree = BUILT_PRODUCTS_DIR; }; | 61 | 66FBE58B2FDA4F0F00F515B4 /* Runner Watch App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Runner Watch App.app"; sourceTree = BUILT_PRODUCTS_DIR; }; |
| 56 | 66FBE5A72FDA518C00F515B4 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; | 62 | 66FBE5A72FDA518C00F515B4 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; |
| @@ -63,6 +69,13 @@ | @@ -63,6 +69,13 @@ | ||
| 63 | /* End PBXFileReference section */ | 69 | /* End PBXFileReference section */ |
| 64 | 70 | ||
| 65 | /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ | 71 | /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ |
| 72 | + 66FB17B22FDB9E3200F515B4 /* Exceptions for "Runner" folder in "Runner Watch App" target */ = { | ||
| 73 | + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; | ||
| 74 | + membershipExceptions = ( | ||
| 75 | + Shared/AppGroupConstants.swift, | ||
| 76 | + ); | ||
| 77 | + target = 66FBE58A2FDA4F0F00F515B4 /* Runner Watch App */; | ||
| 78 | + }; | ||
| 66 | 66FBE6202FDA6E7600F515B4 /* Exceptions for "Runner" folder in "Runner" target */ = { | 79 | 66FBE6202FDA6E7600F515B4 /* Exceptions for "Runner" folder in "Runner" target */ = { |
| 67 | isa = PBXFileSystemSynchronizedBuildFileExceptionSet; | 80 | isa = PBXFileSystemSynchronizedBuildFileExceptionSet; |
| 68 | membershipExceptions = ( | 81 | membershipExceptions = ( |
| @@ -77,14 +90,13 @@ | @@ -77,14 +90,13 @@ | ||
| 77 | isa = PBXFileSystemSynchronizedRootGroup; | 90 | isa = PBXFileSystemSynchronizedRootGroup; |
| 78 | exceptions = ( | 91 | exceptions = ( |
| 79 | 66FBE6202FDA6E7600F515B4 /* Exceptions for "Runner" folder in "Runner" target */, | 92 | 66FBE6202FDA6E7600F515B4 /* Exceptions for "Runner" folder in "Runner" target */, |
| 93 | + 66FB17B22FDB9E3200F515B4 /* Exceptions for "Runner" folder in "Runner Watch App" target */, | ||
| 80 | ); | 94 | ); |
| 81 | path = Runner; | 95 | path = Runner; |
| 82 | sourceTree = "<group>"; | 96 | sourceTree = "<group>"; |
| 83 | }; | 97 | }; |
| 84 | 66FBE58F2FDA4F0F00F515B4 /* Runner Watch App */ = { | 98 | 66FBE58F2FDA4F0F00F515B4 /* Runner Watch App */ = { |
| 85 | isa = PBXFileSystemSynchronizedRootGroup; | 99 | isa = PBXFileSystemSynchronizedRootGroup; |
| 86 | - exceptions = ( | ||
| 87 | - ); | ||
| 88 | path = "Runner Watch App"; | 100 | path = "Runner Watch App"; |
| 89 | sourceTree = "<group>"; | 101 | sourceTree = "<group>"; |
| 90 | }; | 102 | }; |
| @@ -119,10 +131,21 @@ | @@ -119,10 +131,21 @@ | ||
| 119 | path = Flutter; | 131 | path = Flutter; |
| 120 | sourceTree = "<group>"; | 132 | sourceTree = "<group>"; |
| 121 | }; | 133 | }; |
| 134 | + 66FB19072FDBC80100F515B4 /* FigmaHome Preview Sources */ = { | ||
| 135 | + isa = PBXGroup; | ||
| 136 | + children = ( | ||
| 137 | + 66FB19052FDBC80100F515B4 /* Runner Watch App/FigmaHome/DFWatchFigmaDetailViews.swift */, | ||
| 138 | + 66FB19042FDBC80100F515B4 /* Runner Watch App/FigmaHome/DFWatchFigmaHomeViews.swift */, | ||
| 139 | + 66FB19032FDBC80100F515B4 /* Runner Watch App/FigmaHome/DFWatchFigmaTokens.swift */, | ||
| 140 | + ); | ||
| 141 | + name = "FigmaHome Preview Sources"; | ||
| 142 | + sourceTree = "<group>"; | ||
| 143 | + }; | ||
| 122 | 66FBE5752FDA4F0E00F515B4 = { | 144 | 66FBE5752FDA4F0E00F515B4 = { |
| 123 | isa = PBXGroup; | 145 | isa = PBXGroup; |
| 124 | children = ( | 146 | children = ( |
| 125 | 66FBE5BF2FDA51A300F515B4 /* doublefeel-watch-app-extensionExtension.entitlements */, | 147 | 66FBE5BF2FDA51A300F515B4 /* doublefeel-watch-app-extensionExtension.entitlements */, |
| 148 | + 66FB19072FDBC80100F515B4 /* FigmaHome Preview Sources */, | ||
| 126 | 66FBE5802FDA4F0E00F515B4 /* Runner */, | 149 | 66FBE5802FDA4F0E00F515B4 /* Runner */, |
| 127 | 66FBE58F2FDA4F0F00F515B4 /* Runner Watch App */, | 150 | 66FBE58F2FDA4F0F00F515B4 /* Runner Watch App */, |
| 128 | 4A9B7C122FDB0A1200F515B4 /* Flutter */, | 151 | 4A9B7C122FDB0A1200F515B4 /* Flutter */, |
| @@ -280,10 +303,14 @@ | @@ -280,10 +303,14 @@ | ||
| 280 | inputFileListPaths = ( | 303 | inputFileListPaths = ( |
| 281 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", | 304 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", |
| 282 | ); | 305 | ); |
| 306 | + inputPaths = ( | ||
| 307 | + ); | ||
| 283 | name = "[CP] Copy Pods Resources"; | 308 | name = "[CP] Copy Pods Resources"; |
| 284 | outputFileListPaths = ( | 309 | outputFileListPaths = ( |
| 285 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", | 310 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", |
| 286 | ); | 311 | ); |
| 312 | + outputPaths = ( | ||
| 313 | + ); | ||
| 287 | runOnlyForDeploymentPostprocessing = 0; | 314 | runOnlyForDeploymentPostprocessing = 0; |
| 288 | shellPath = /bin/sh; | 315 | shellPath = /bin/sh; |
| 289 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; | 316 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; |
| @@ -354,10 +381,14 @@ | @@ -354,10 +381,14 @@ | ||
| 354 | inputFileListPaths = ( | 381 | inputFileListPaths = ( |
| 355 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", | 382 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", |
| 356 | ); | 383 | ); |
| 384 | + inputPaths = ( | ||
| 385 | + ); | ||
| 357 | name = "[CP] Embed Pods Frameworks"; | 386 | name = "[CP] Embed Pods Frameworks"; |
| 358 | outputFileListPaths = ( | 387 | outputFileListPaths = ( |
| 359 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", | 388 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", |
| 360 | ); | 389 | ); |
| 390 | + outputPaths = ( | ||
| 391 | + ); | ||
| 361 | runOnlyForDeploymentPostprocessing = 0; | 392 | runOnlyForDeploymentPostprocessing = 0; |
| 362 | shellPath = /bin/sh; | 393 | shellPath = /bin/sh; |
| 363 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; | 394 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; |
| @@ -371,10 +402,14 @@ | @@ -371,10 +402,14 @@ | ||
| 371 | inputFileListPaths = ( | 402 | inputFileListPaths = ( |
| 372 | "${PODS_ROOT}/Target Support Files/Pods-Runner Watch App/Pods-Runner Watch App-frameworks-${CONFIGURATION}-input-files.xcfilelist", | 403 | "${PODS_ROOT}/Target Support Files/Pods-Runner Watch App/Pods-Runner Watch App-frameworks-${CONFIGURATION}-input-files.xcfilelist", |
| 373 | ); | 404 | ); |
| 405 | + inputPaths = ( | ||
| 406 | + ); | ||
| 374 | name = "[CP] Embed Pods Frameworks"; | 407 | name = "[CP] Embed Pods Frameworks"; |
| 375 | outputFileListPaths = ( | 408 | outputFileListPaths = ( |
| 376 | "${PODS_ROOT}/Target Support Files/Pods-Runner Watch App/Pods-Runner Watch App-frameworks-${CONFIGURATION}-output-files.xcfilelist", | 409 | "${PODS_ROOT}/Target Support Files/Pods-Runner Watch App/Pods-Runner Watch App-frameworks-${CONFIGURATION}-output-files.xcfilelist", |
| 377 | ); | 410 | ); |
| 411 | + outputPaths = ( | ||
| 412 | + ); | ||
| 378 | runOnlyForDeploymentPostprocessing = 0; | 413 | runOnlyForDeploymentPostprocessing = 0; |
| 379 | shellPath = /bin/sh; | 414 | shellPath = /bin/sh; |
| 380 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner Watch App/Pods-Runner Watch App-frameworks.sh\"\n"; | 415 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner Watch App/Pods-Runner Watch App-frameworks.sh\"\n"; |
| @@ -416,6 +451,9 @@ | @@ -416,6 +451,9 @@ | ||
| 416 | isa = PBXSourcesBuildPhase; | 451 | isa = PBXSourcesBuildPhase; |
| 417 | buildActionMask = 2147483647; | 452 | buildActionMask = 2147483647; |
| 418 | files = ( | 453 | files = ( |
| 454 | + 66FB19022FDBC80100F515B4 /* Runner Watch App/FigmaHome/DFWatchFigmaDetailViews.swift in Sources */, | ||
| 455 | + 66FB19012FDBC80100F515B4 /* Runner Watch App/FigmaHome/DFWatchFigmaHomeViews.swift in Sources */, | ||
| 456 | + 66FB19002FDBC80100F515B4 /* Runner Watch App/FigmaHome/DFWatchFigmaTokens.swift in Sources */, | ||
| 419 | ); | 457 | ); |
| 420 | runOnlyForDeploymentPostprocessing = 0; | 458 | runOnlyForDeploymentPostprocessing = 0; |
| 421 | }; | 459 | }; |
| @@ -567,6 +605,7 @@ | @@ -567,6 +605,7 @@ | ||
| 567 | "@executable_path/Frameworks", | 605 | "@executable_path/Frameworks", |
| 568 | ); | 606 | ); |
| 569 | MARKETING_VERSION = 1.4.0; | 607 | MARKETING_VERSION = 1.4.0; |
| 608 | + ONLY_ACTIVE_ARCH = YES; | ||
| 570 | PRODUCT_BUNDLE_IDENTIFIER = com.luiz.doublefeel.watchkitapp; | 609 | PRODUCT_BUNDLE_IDENTIFIER = com.luiz.doublefeel.watchkitapp; |
| 571 | PRODUCT_NAME = "$(TARGET_NAME)"; | 610 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 572 | SDKROOT = watchos; | 611 | SDKROOT = watchos; |
| @@ -670,6 +709,7 @@ | @@ -670,6 +709,7 @@ | ||
| 670 | "@executable_path/Frameworks", | 709 | "@executable_path/Frameworks", |
| 671 | ); | 710 | ); |
| 672 | MARKETING_VERSION = 2.5.0; | 711 | MARKETING_VERSION = 2.5.0; |
| 712 | + ONLY_ACTIVE_ARCH = YES; | ||
| 673 | OTHER_LDFLAGS = ( | 713 | OTHER_LDFLAGS = ( |
| 674 | "$(inherited)", | 714 | "$(inherited)", |
| 675 | "-framework", | 715 | "-framework", |
| @@ -29,6 +29,9 @@ class AppDelegate: FlutterAppDelegate { | @@ -29,6 +29,9 @@ class AppDelegate: FlutterAppDelegate { | ||
| 29 | 29 | ||
| 30 | flutterEngine.run() | 30 | flutterEngine.run() |
| 31 | GeneratedPluginRegistrant.register(with: flutterEngine) | 31 | GeneratedPluginRegistrant.register(with: flutterEngine) |
| 32 | + NativePigeonRegistrar.register(binaryMessenger: flutterEngine.binaryMessenger) | ||
| 33 | + WatchConnectivityService.shared.activate() | ||
| 34 | + HealthKitService.shared.startBackgroundObserversIfNeeded() | ||
| 32 | 35 | ||
| 33 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) | 36 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) |
| 34 | } | 37 | } |
ios/Runner/HealthKit/HealthDataReader.swift
0 → 100644
| 1 | +import Foundation | ||
| 2 | +import HealthKit | ||
| 3 | + | ||
| 4 | +/// Focused HealthKit query helper. It mirrors the original SwiftUI project data | ||
| 5 | +/// coverage while avoiding dependencies on the old network and user modules. | ||
| 6 | +final class HealthDataReader { | ||
| 7 | + private let healthStore: HKHealthStore | ||
| 8 | + | ||
| 9 | + init(healthStore: HKHealthStore) { | ||
| 10 | + self.healthStore = healthStore | ||
| 11 | + | ||
| 12 | + } | ||
| 13 | + | ||
| 14 | + func collectRecentData(startDate: Date, endDate: Date) async throws -> NativeHealthSyncSummary { | ||
| 15 | + async let hrv = fetchQuantitySamples( | ||
| 16 | + identifier: .heartRateVariabilitySDNN, | ||
| 17 | + dataType: .hrv, | ||
| 18 | + unit: .secondUnit(with: .milli), | ||
| 19 | + startDate: startDate, | ||
| 20 | + endDate: endDate | ||
| 21 | + ) | ||
| 22 | + async let heart = fetchHeartRateFamily(startDate: startDate, endDate: endDate) | ||
| 23 | + async let oxygen = fetchQuantitySamples( | ||
| 24 | + identifier: .oxygenSaturation, | ||
| 25 | + dataType: .oxygenSaturation, | ||
| 26 | + unit: .percent(), | ||
| 27 | + startDate: startDate, | ||
| 28 | + endDate: endDate | ||
| 29 | + ) | ||
| 30 | + async let activeEnergy = fetchQuantitySamples( | ||
| 31 | + identifier: .activeEnergyBurned, | ||
| 32 | + dataType: .activeEnergy, | ||
| 33 | + unit: .kilocalorie(), | ||
| 34 | + startDate: startDate, | ||
| 35 | + endDate: endDate | ||
| 36 | + ) | ||
| 37 | + async let exercise = fetchQuantitySamples( | ||
| 38 | + identifier: .appleExerciseTime, | ||
| 39 | + dataType: .exercise, | ||
| 40 | + unit: .minute(), | ||
| 41 | + startDate: startDate, | ||
| 42 | + endDate: endDate | ||
| 43 | + ) | ||
| 44 | + async let stand = fetchQuantitySamples( | ||
| 45 | + identifier: .appleStandTime, | ||
| 46 | + dataType: .stand, | ||
| 47 | + unit: .minute(), | ||
| 48 | + startDate: startDate, | ||
| 49 | + endDate: endDate | ||
| 50 | + ) | ||
| 51 | + async let steps = fetchDailyCumulativeSamples( | ||
| 52 | + identifier: .stepCount, | ||
| 53 | + dataType: .steps, | ||
| 54 | + unit: .count(), | ||
| 55 | + startDate: startDate, | ||
| 56 | + endDate: endDate | ||
| 57 | + ) | ||
| 58 | + async let wristTemp = fetchQuantitySamples( | ||
| 59 | + identifier: .appleSleepingWristTemperature, | ||
| 60 | + dataType: .sleepingWristTemperature, | ||
| 61 | + unit: .degreeCelsius(), | ||
| 62 | + startDate: startDate, | ||
| 63 | + endDate: endDate | ||
| 64 | + ) | ||
| 65 | + async let respiratory = fetchQuantitySamples( | ||
| 66 | + identifier: .respiratoryRate, | ||
| 67 | + dataType: .respiratoryRate, | ||
| 68 | + unit: HKUnit.count().unitDivided(by: .minute()), | ||
| 69 | + startDate: startDate, | ||
| 70 | + endDate: endDate | ||
| 71 | + ) | ||
| 72 | + async let rhythm = fetchIrregularHeartRhythmEvents(startDate: startDate, endDate: endDate) | ||
| 73 | + async let sleep = fetchSleepIntervals(startDate: startDate, endDate: endDate) | ||
| 74 | + | ||
| 75 | + let hrvPoints = try await hrv | ||
| 76 | + let heartPoints = try await heart | ||
| 77 | + let oxygenPoints = try await oxygen | ||
| 78 | + let activeEnergyPoints = try await activeEnergy | ||
| 79 | + let exercisePoints = try await exercise | ||
| 80 | + let standPoints = try await stand | ||
| 81 | + let stepPoints = try await steps | ||
| 82 | + let wristTempPoints = try await wristTemp | ||
| 83 | + let respiratoryPoints = try await respiratory | ||
| 84 | + let rhythmPoints = try await rhythm | ||
| 85 | + let sleepIntervals = try await sleep | ||
| 86 | + | ||
| 87 | + return NativeHealthSyncSummary( | ||
| 88 | + commonCount: hrvPoints.count | ||
| 89 | + + heartPoints.count | ||
| 90 | + + oxygenPoints.count | ||
| 91 | + + activeEnergyPoints.count | ||
| 92 | + + exercisePoints.count | ||
| 93 | + + standPoints.count | ||
| 94 | + + stepPoints.count | ||
| 95 | + + wristTempPoints.count | ||
| 96 | + + respiratoryPoints.count | ||
| 97 | + + rhythmPoints.count, | ||
| 98 | + sleepCount: sleepIntervals.count, | ||
| 99 | + startedAt: startDate, | ||
| 100 | + endedAt: endDate | ||
| 101 | + ) | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + func fetchLatestHRV() async throws -> Double? { | ||
| 105 | + guard let type = NativeHealthTypeCatalog.quantity(.heartRateVariabilitySDNN) else { | ||
| 106 | + throw NativeHealthKitError.invalidType("heartRateVariabilitySDNN") | ||
| 107 | + } | ||
| 108 | + let sample = try await fetchLatestQuantitySample(type: type) | ||
| 109 | + return sample?.quantity.doubleValue(for: .secondUnit(with: .milli)) | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + func fetchTodayStepCount() async throws -> Int { | ||
| 113 | + guard let type = NativeHealthTypeCatalog.quantity(.stepCount) else { | ||
| 114 | + throw NativeHealthKitError.invalidType("stepCount") | ||
| 115 | + } | ||
| 116 | + let startOfDay = Calendar.current.startOfDay(for: Date()) | ||
| 117 | + let predicate = HKQuery.predicateForSamples(withStart: startOfDay, end: Date()) | ||
| 118 | + | ||
| 119 | + return try await withCheckedThrowingContinuation { continuation in | ||
| 120 | + let query = HKStatisticsQuery( | ||
| 121 | + quantityType: type, | ||
| 122 | + quantitySamplePredicate: predicate, | ||
| 123 | + options: .cumulativeSum | ||
| 124 | + ) { _, statistics, error in | ||
| 125 | + if let error { | ||
| 126 | + continuation.resume(throwing: error) | ||
| 127 | + return | ||
| 128 | + } | ||
| 129 | + let value = statistics?.sumQuantity()?.doubleValue(for: .count()) ?? 0 | ||
| 130 | + continuation.resume(returning: Int(value)) | ||
| 131 | + } | ||
| 132 | + healthStore.execute(query) | ||
| 133 | + } | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + private func fetchHeartRateFamily(startDate: Date, endDate: Date) async throws -> [NativeHealthDataPoint] { | ||
| 137 | + async let heartRate = fetchQuantitySamples( | ||
| 138 | + identifier: .heartRate, | ||
| 139 | + dataType: .heartRate, | ||
| 140 | + unit: HKUnit.count().unitDivided(by: .minute()), | ||
| 141 | + startDate: startDate, | ||
| 142 | + endDate: endDate | ||
| 143 | + ) | ||
| 144 | + async let walking = fetchQuantitySamples( | ||
| 145 | + identifier: .walkingHeartRateAverage, | ||
| 146 | + dataType: .walkingHeartRate, | ||
| 147 | + unit: HKUnit.count().unitDivided(by: .minute()), | ||
| 148 | + startDate: startDate, | ||
| 149 | + endDate: endDate | ||
| 150 | + ) | ||
| 151 | + async let resting = fetchQuantitySamples( | ||
| 152 | + identifier: .restingHeartRate, | ||
| 153 | + dataType: .restingHeartRate, | ||
| 154 | + unit: HKUnit.count().unitDivided(by: .minute()), | ||
| 155 | + startDate: startDate, | ||
| 156 | + endDate: endDate | ||
| 157 | + ) | ||
| 158 | + return try await heartRate + walking + resting | ||
| 159 | + } | ||
| 160 | + | ||
| 161 | + private func fetchQuantitySamples( | ||
| 162 | + identifier: HKQuantityTypeIdentifier, | ||
| 163 | + dataType: NativeHealthDataType, | ||
| 164 | + unit: HKUnit, | ||
| 165 | + startDate: Date, | ||
| 166 | + endDate: Date | ||
| 167 | + ) async throws -> [NativeHealthDataPoint] { | ||
| 168 | + guard let type = NativeHealthTypeCatalog.quantity(identifier) else { | ||
| 169 | + throw NativeHealthKitError.invalidType(identifier.rawValue) | ||
| 170 | + } | ||
| 171 | + let predicate = HKQuery.predicateForSamples( | ||
| 172 | + withStart: startDate, | ||
| 173 | + end: endDate, | ||
| 174 | + options: .strictStartDate | ||
| 175 | + ) | ||
| 176 | + let sort = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: true) | ||
| 177 | + | ||
| 178 | + return try await withCheckedThrowingContinuation { continuation in | ||
| 179 | + let query = HKSampleQuery( | ||
| 180 | + sampleType: type, | ||
| 181 | + predicate: predicate, | ||
| 182 | + limit: HKObjectQueryNoLimit, | ||
| 183 | + sortDescriptors: [sort] | ||
| 184 | + ) { _, samples, error in | ||
| 185 | + if let error { | ||
| 186 | + continuation.resume(throwing: error) | ||
| 187 | + return | ||
| 188 | + } | ||
| 189 | + let points = (samples as? [HKQuantitySample] ?? []).map { sample in | ||
| 190 | + NativeHealthDataPoint( | ||
| 191 | + dataType: dataType, | ||
| 192 | + time: sample.startDate.timeIntervalSince1970, | ||
| 193 | + value: sample.quantity.doubleValue(for: unit) | ||
| 194 | + ) | ||
| 195 | + } | ||
| 196 | + continuation.resume(returning: points) | ||
| 197 | + } | ||
| 198 | + healthStore.execute(query) | ||
| 199 | + } | ||
| 200 | + } | ||
| 201 | + | ||
| 202 | + private func fetchDailyCumulativeSamples( | ||
| 203 | + identifier: HKQuantityTypeIdentifier, | ||
| 204 | + dataType: NativeHealthDataType, | ||
| 205 | + unit: HKUnit, | ||
| 206 | + startDate: Date, | ||
| 207 | + endDate: Date | ||
| 208 | + ) async throws -> [NativeHealthDataPoint] { | ||
| 209 | + guard let type = NativeHealthTypeCatalog.quantity(identifier) else { | ||
| 210 | + throw NativeHealthKitError.invalidType(identifier.rawValue) | ||
| 211 | + } | ||
| 212 | + var interval = DateComponents() | ||
| 213 | + interval.day = 1 | ||
| 214 | + let anchorDate = Calendar.current.startOfDay(for: startDate) | ||
| 215 | + let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate) | ||
| 216 | + | ||
| 217 | + return try await withCheckedThrowingContinuation { continuation in | ||
| 218 | + let query = HKStatisticsCollectionQuery( | ||
| 219 | + quantityType: type, | ||
| 220 | + quantitySamplePredicate: predicate, | ||
| 221 | + options: .cumulativeSum, | ||
| 222 | + anchorDate: anchorDate, | ||
| 223 | + intervalComponents: interval | ||
| 224 | + ) | ||
| 225 | + query.initialResultsHandler = { _, collection, error in | ||
| 226 | + if let error { | ||
| 227 | + continuation.resume(throwing: error) | ||
| 228 | + return | ||
| 229 | + } | ||
| 230 | + var points: [NativeHealthDataPoint] = [] | ||
| 231 | + collection?.enumerateStatistics(from: startDate, to: endDate) { statistics, _ in | ||
| 232 | + guard let value = statistics.sumQuantity()?.doubleValue(for: unit) else { return } | ||
| 233 | + points.append( | ||
| 234 | + NativeHealthDataPoint( | ||
| 235 | + dataType: dataType, | ||
| 236 | + time: statistics.startDate.timeIntervalSince1970, | ||
| 237 | + value: value | ||
| 238 | + ) | ||
| 239 | + ) | ||
| 240 | + } | ||
| 241 | + continuation.resume(returning: points) | ||
| 242 | + } | ||
| 243 | + healthStore.execute(query) | ||
| 244 | + } | ||
| 245 | + } | ||
| 246 | + | ||
| 247 | + private func fetchSleepIntervals(startDate: Date, endDate: Date) async throws -> [NativeSleepInterval] { | ||
| 248 | + guard let type = NativeHealthTypeCatalog.category(.sleepAnalysis) else { | ||
| 249 | + throw NativeHealthKitError.invalidType("sleepAnalysis") | ||
| 250 | + } | ||
| 251 | + let predicate = HKQuery.predicateForSamples( | ||
| 252 | + withStart: startDate, | ||
| 253 | + end: endDate, | ||
| 254 | + options: .strictStartDate | ||
| 255 | + ) | ||
| 256 | + let sort = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: true) | ||
| 257 | + | ||
| 258 | + return try await withCheckedThrowingContinuation { continuation in | ||
| 259 | + let query = HKSampleQuery( | ||
| 260 | + sampleType: type, | ||
| 261 | + predicate: predicate, | ||
| 262 | + limit: HKObjectQueryNoLimit, | ||
| 263 | + sortDescriptors: [sort] | ||
| 264 | + ) { _, samples, error in | ||
| 265 | + if let error { | ||
| 266 | + continuation.resume(throwing: error) | ||
| 267 | + return | ||
| 268 | + } | ||
| 269 | + let intervals = (samples as? [HKCategorySample] ?? []).map { sample in | ||
| 270 | + NativeSleepInterval( | ||
| 271 | + dataType: sample.value, | ||
| 272 | + fromTime: sample.startDate.timeIntervalSince1970, | ||
| 273 | + toTime: sample.endDate.timeIntervalSince1970 | ||
| 274 | + ) | ||
| 275 | + } | ||
| 276 | + continuation.resume(returning: intervals) | ||
| 277 | + } | ||
| 278 | + healthStore.execute(query) | ||
| 279 | + } | ||
| 280 | + } | ||
| 281 | + | ||
| 282 | + private func fetchIrregularHeartRhythmEvents( | ||
| 283 | + startDate: Date, | ||
| 284 | + endDate: Date | ||
| 285 | + ) async throws -> [NativeHealthDataPoint] { | ||
| 286 | + guard let type = NativeHealthTypeCatalog.category(.irregularHeartRhythmEvent) else { | ||
| 287 | + throw NativeHealthKitError.invalidType("irregularHeartRhythmEvent") | ||
| 288 | + } | ||
| 289 | + let predicate = HKQuery.predicateForSamples( | ||
| 290 | + withStart: startDate, | ||
| 291 | + end: endDate, | ||
| 292 | + options: .strictStartDate | ||
| 293 | + ) | ||
| 294 | + let sort = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: true) | ||
| 295 | + | ||
| 296 | + return try await withCheckedThrowingContinuation { continuation in | ||
| 297 | + let query = HKSampleQuery( | ||
| 298 | + sampleType: type, | ||
| 299 | + predicate: predicate, | ||
| 300 | + limit: HKObjectQueryNoLimit, | ||
| 301 | + sortDescriptors: [sort] | ||
| 302 | + ) { _, samples, error in | ||
| 303 | + if let error { | ||
| 304 | + continuation.resume(throwing: error) | ||
| 305 | + return | ||
| 306 | + } | ||
| 307 | + let points = (samples as? [HKCategorySample] ?? []).map { sample in | ||
| 308 | + NativeHealthDataPoint( | ||
| 309 | + dataType: .irregularHeartRhythm, | ||
| 310 | + time: sample.startDate.timeIntervalSince1970, | ||
| 311 | + value: Double(sample.value) | ||
| 312 | + ) | ||
| 313 | + } | ||
| 314 | + continuation.resume(returning: points) | ||
| 315 | + } | ||
| 316 | + healthStore.execute(query) | ||
| 317 | + } | ||
| 318 | + } | ||
| 319 | + | ||
| 320 | + private func fetchLatestQuantitySample(type: HKQuantityType) async throws -> HKQuantitySample? { | ||
| 321 | + let sort = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false) | ||
| 322 | + return try await withCheckedThrowingContinuation { continuation in | ||
| 323 | + let query = HKSampleQuery( | ||
| 324 | + sampleType: type, | ||
| 325 | + predicate: nil, | ||
| 326 | + limit: 1, | ||
| 327 | + sortDescriptors: [sort] | ||
| 328 | + ) { _, samples, error in | ||
| 329 | + if let error { | ||
| 330 | + continuation.resume(throwing: error) | ||
| 331 | + return | ||
| 332 | + } | ||
| 333 | + continuation.resume(returning: samples?.first as? HKQuantitySample) | ||
| 334 | + } | ||
| 335 | + healthStore.execute(query) | ||
| 336 | + } | ||
| 337 | + } | ||
| 338 | +} |
ios/Runner/HealthKit/HealthKitService.swift
0 → 100644
| 1 | +import Foundation | ||
| 2 | +import HealthKit | ||
| 3 | + | ||
| 4 | +/// Native Apple Health service for the Flutter host API. | ||
| 5 | +/// | ||
| 6 | +/// Responsibilities: | ||
| 7 | +/// - request/read HealthKit permissions | ||
| 8 | +/// - read the health data types used by the original SwiftUI app | ||
| 9 | +/// - keep Watch complication values fresh in the shared App Group | ||
| 10 | +/// - register background observers so HealthKit changes refresh local state | ||
| 11 | +final class HealthKitService { | ||
| 12 | + static let shared = HealthKitService() | ||
| 13 | + | ||
| 14 | + private let healthStore = HKHealthStore() | ||
| 15 | + private let syncStore = HealthSyncStateStore() | ||
| 16 | + private lazy var reader = HealthDataReader(healthStore: healthStore) | ||
| 17 | + private var observersStarted = false | ||
| 18 | + | ||
| 19 | + private init() {} | ||
| 20 | + | ||
| 21 | + var isHealthDataAvailable: Bool { | ||
| 22 | + HKHealthStore.isHealthDataAvailable() | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + func requestAuthorization(completion: @escaping (Bool, Error?) -> Void) { | ||
| 26 | + guard isHealthDataAvailable else { | ||
| 27 | + completion(false, NativeHealthKitError.healthDataUnavailable) | ||
| 28 | + return | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + healthStore.requestAuthorization( | ||
| 32 | + toShare: NativeHealthTypeCatalog.writeTypes, | ||
| 33 | + read: NativeHealthTypeCatalog.readTypes | ||
| 34 | + ) { success, error in | ||
| 35 | + completion(success, error) | ||
| 36 | + } | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + func shouldRequestAuthorization() async -> Bool { | ||
| 40 | + guard isHealthDataAvailable else { return false } | ||
| 41 | + do { | ||
| 42 | + let status = try await healthStore.statusForAuthorizationRequest( | ||
| 43 | + toShare: NativeHealthTypeCatalog.writeTypes, | ||
| 44 | + read: NativeHealthTypeCatalog.readTypes | ||
| 45 | + ) | ||
| 46 | + return status == .shouldRequest | ||
| 47 | + } catch { | ||
| 48 | + return true | ||
| 49 | + } | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + func startBackgroundObserversIfNeeded() { | ||
| 53 | + guard isHealthDataAvailable, !observersStarted else { return } | ||
| 54 | + observersStarted = true | ||
| 55 | + | ||
| 56 | + for sampleType in NativeHealthTypeCatalog.observedTypes { | ||
| 57 | + enableBackgroundDelivery(for: sampleType) | ||
| 58 | + let query = HKObserverQuery(sampleType: sampleType, predicate: nil) { [weak self] _, completion, error in | ||
| 59 | + guard error == nil else { | ||
| 60 | + completion() | ||
| 61 | + return | ||
| 62 | + } | ||
| 63 | + Task { | ||
| 64 | + await self?.handleObservedChange(sampleType) | ||
| 65 | + completion() | ||
| 66 | + } | ||
| 67 | + } | ||
| 68 | + healthStore.execute(query) | ||
| 69 | + } | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + func performLocalSync() async -> NativeHealthSyncSummary { | ||
| 73 | + guard isHealthDataAvailable else { | ||
| 74 | + return NativeHealthSyncSummary(commonCount: 0, sleepCount: 0, startedAt: Date(), endedAt: Date()) | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + let startDate = earliestStartDate() | ||
| 78 | + let endDate = Date() | ||
| 79 | + | ||
| 80 | + do { | ||
| 81 | + let summary = try await reader.collectRecentData(startDate: startDate, endDate: endDate) | ||
| 82 | + NativeHealthDataType.allCases | ||
| 83 | + .filter { $0 != .unknown } | ||
| 84 | + .forEach { syncStore.save(date: endDate, for: $0) } | ||
| 85 | + await refreshSharedWatchValues() | ||
| 86 | + startBackgroundObserversIfNeeded() | ||
| 87 | + return summary | ||
| 88 | + } catch { | ||
| 89 | + await refreshSharedWatchValues() | ||
| 90 | + return NativeHealthSyncSummary(commonCount: 0, sleepCount: 0, startedAt: startDate, endedAt: endDate) | ||
| 91 | + } | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + func refreshSharedWatchValues() async { | ||
| 95 | + do { | ||
| 96 | + if let hrv = try await reader.fetchLatestHRV() { | ||
| 97 | + AppGroupConstants.defaults?.set(hrv, forKey: AppGroupConstants.Key.latestHRV) | ||
| 98 | + } | ||
| 99 | + } catch { | ||
| 100 | + // Keep the previous widget value when a single read fails. | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + do { | ||
| 104 | + let steps = try await reader.fetchTodayStepCount() | ||
| 105 | + AppGroupConstants.defaults?.set(steps, forKey: AppGroupConstants.Key.latestStepCount) | ||
| 106 | + } catch { | ||
| 107 | + // Keep the previous widget value when a single read fails. | ||
| 108 | + } | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + private func earliestStartDate() -> Date { | ||
| 112 | + NativeHealthDataType.allCases | ||
| 113 | + .filter { $0 != .unknown } | ||
| 114 | + .map { syncStore.startDate(for: $0) } | ||
| 115 | + .min() ?? Calendar.current.startOfDay(for: Date()) | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + private func enableBackgroundDelivery(for sampleType: HKSampleType) { | ||
| 119 | + let frequency: HKUpdateFrequency = sampleType.identifier == HKQuantityTypeIdentifier.stepCount.rawValue | ||
| 120 | + ? .hourly | ||
| 121 | + : .immediate | ||
| 122 | + | ||
| 123 | + healthStore.enableBackgroundDelivery(for: sampleType, frequency: frequency) { success, error in | ||
| 124 | + if let error { | ||
| 125 | + print("HealthKit background delivery failed: \(sampleType.identifier), \(error.localizedDescription)") | ||
| 126 | + } else { | ||
| 127 | + print("HealthKit background delivery \(success ? "enabled" : "not enabled"): \(sampleType.identifier)") | ||
| 128 | + } | ||
| 129 | + } | ||
| 130 | + } | ||
| 131 | + | ||
| 132 | + private func handleObservedChange(_ sampleType: HKSampleType) async { | ||
| 133 | + switch sampleType.identifier { | ||
| 134 | + case HKQuantityTypeIdentifier.heartRateVariabilitySDNN.rawValue, | ||
| 135 | + HKQuantityTypeIdentifier.stepCount.rawValue: | ||
| 136 | + await refreshSharedWatchValues() | ||
| 137 | + default: | ||
| 138 | + break | ||
| 139 | + } | ||
| 140 | + | ||
| 141 | + if let type = NativeHealthDataType(sampleTypeIdentifier: sampleType.identifier) { | ||
| 142 | + syncStore.save(date: Date(), for: type) | ||
| 143 | + } | ||
| 144 | + } | ||
| 145 | +} | ||
| 146 | + | ||
| 147 | +private extension NativeHealthDataType { | ||
| 148 | + init?(sampleTypeIdentifier: String) { | ||
| 149 | + switch sampleTypeIdentifier { | ||
| 150 | + case HKQuantityTypeIdentifier.heartRateVariabilitySDNN.rawValue: | ||
| 151 | + self = .hrv | ||
| 152 | + case HKQuantityTypeIdentifier.heartRate.rawValue: | ||
| 153 | + self = .heartRate | ||
| 154 | + case HKQuantityTypeIdentifier.stepCount.rawValue: | ||
| 155 | + self = .steps | ||
| 156 | + case HKQuantityTypeIdentifier.oxygenSaturation.rawValue: | ||
| 157 | + self = .oxygenSaturation | ||
| 158 | + case HKQuantityTypeIdentifier.activeEnergyBurned.rawValue: | ||
| 159 | + self = .activeEnergy | ||
| 160 | + case HKQuantityTypeIdentifier.appleExerciseTime.rawValue: | ||
| 161 | + self = .exercise | ||
| 162 | + case HKQuantityTypeIdentifier.appleStandTime.rawValue: | ||
| 163 | + self = .stand | ||
| 164 | + case HKQuantityTypeIdentifier.appleSleepingWristTemperature.rawValue: | ||
| 165 | + self = .sleepingWristTemperature | ||
| 166 | + case HKQuantityTypeIdentifier.respiratoryRate.rawValue: | ||
| 167 | + self = .respiratoryRate | ||
| 168 | + case HKCategoryTypeIdentifier.sleepAnalysis.rawValue: | ||
| 169 | + self = .sleep | ||
| 170 | + case HKCategoryTypeIdentifier.irregularHeartRhythmEvent.rawValue: | ||
| 171 | + self = .irregularHeartRhythm | ||
| 172 | + default: | ||
| 173 | + return nil | ||
| 174 | + } | ||
| 175 | + } | ||
| 176 | +} | ||
| 177 | + |
ios/Runner/HealthKit/HealthKitTypes.swift
0 → 100644
| 1 | +import Foundation | ||
| 2 | +import HealthKit | ||
| 3 | + | ||
| 4 | +enum NativeHealthKitError: LocalizedError { | ||
| 5 | + case healthDataUnavailable | ||
| 6 | + case invalidType(String) | ||
| 7 | + case noData | ||
| 8 | + | ||
| 9 | + var errorDescription: String? { | ||
| 10 | + switch self { | ||
| 11 | + case .healthDataUnavailable: | ||
| 12 | + return "当前设备不支持 HealthKit" | ||
| 13 | + case .invalidType(let identifier): | ||
| 14 | + return "无效的健康数据类型:\(identifier)" | ||
| 15 | + case .noData: | ||
| 16 | + return "没有找到健康数据" | ||
| 17 | + } | ||
| 18 | + } | ||
| 19 | +} | ||
| 20 | + | ||
| 21 | +/// Raw values match the original SwiftUI project server contract. | ||
| 22 | +enum NativeHealthDataType: Int, Codable, CaseIterable { | ||
| 23 | + case unknown = 0 | ||
| 24 | + case hrv = 1 | ||
| 25 | + case heartRate = 2 | ||
| 26 | + case oxygenSaturation = 3 | ||
| 27 | + case activeEnergy = 4 | ||
| 28 | + case exercise = 5 | ||
| 29 | + case stand = 6 | ||
| 30 | + case steps = 7 | ||
| 31 | + case walkingHeartRate = 8 | ||
| 32 | + case restingHeartRate = 9 | ||
| 33 | + case sleepingHeartRate = 10 | ||
| 34 | + case sleepingWristTemperature = 11 | ||
| 35 | + case respiratoryRate = 12 | ||
| 36 | + case irregularHeartRhythm = 13 | ||
| 37 | + case sleep = 100 | ||
| 38 | +} | ||
| 39 | + | ||
| 40 | +struct NativeHealthDataPoint: Codable { | ||
| 41 | + let dataType: NativeHealthDataType | ||
| 42 | + let time: TimeInterval | ||
| 43 | + let value: Double | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +struct NativeSleepInterval: Codable { | ||
| 47 | + let dataType: Int | ||
| 48 | + let fromTime: TimeInterval | ||
| 49 | + let toTime: TimeInterval | ||
| 50 | +} | ||
| 51 | + | ||
| 52 | +struct NativeHealthSyncSummary { | ||
| 53 | + var commonCount = 0 | ||
| 54 | + var sleepCount = 0 | ||
| 55 | + var startedAt: Date | ||
| 56 | + var endedAt: Date | ||
| 57 | +} | ||
| 58 | + | ||
| 59 | +enum NativeHealthTypeCatalog { | ||
| 60 | + static var readTypes: Set<HKObjectType> { | ||
| 61 | + var types = Set<HKObjectType>() | ||
| 62 | + [ | ||
| 63 | + quantity(.heartRate), | ||
| 64 | + quantity(.heartRateVariabilitySDNN), | ||
| 65 | + quantity(.stepCount), | ||
| 66 | + quantity(.oxygenSaturation), | ||
| 67 | + quantity(.activeEnergyBurned), | ||
| 68 | + quantity(.appleExerciseTime), | ||
| 69 | + quantity(.appleStandTime), | ||
| 70 | + quantity(.walkingHeartRateAverage), | ||
| 71 | + quantity(.restingHeartRate), | ||
| 72 | + quantity(.appleSleepingWristTemperature), | ||
| 73 | + quantity(.respiratoryRate), | ||
| 74 | + category(.sleepAnalysis), | ||
| 75 | + category(.irregularHeartRhythmEvent), | ||
| 76 | + ].compactMap { $0 }.forEach { types.insert($0) } | ||
| 77 | + types.insert(HKObjectType.activitySummaryType()) | ||
| 78 | + return types | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + static var writeTypes: Set<HKSampleType> { | ||
| 82 | + // Kept from the original app. The current Flutter flow mostly reads data, | ||
| 83 | + // but requesting this keeps the permission surface compatible. | ||
| 84 | + Set([quantity(.stepCount)].compactMap { $0 }) | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + static var observedTypes: Set<HKSampleType> { | ||
| 88 | + Set([ | ||
| 89 | + quantity(.heartRate), | ||
| 90 | + quantity(.heartRateVariabilitySDNN), | ||
| 91 | + quantity(.stepCount), | ||
| 92 | + quantity(.oxygenSaturation), | ||
| 93 | + quantity(.activeEnergyBurned), | ||
| 94 | + quantity(.appleExerciseTime), | ||
| 95 | + quantity(.appleStandTime), | ||
| 96 | + quantity(.appleSleepingWristTemperature), | ||
| 97 | + quantity(.respiratoryRate), | ||
| 98 | + category(.sleepAnalysis), | ||
| 99 | + category(.irregularHeartRhythmEvent), | ||
| 100 | + ].compactMap { $0 }) | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + static func quantity(_ identifier: HKQuantityTypeIdentifier) -> HKQuantityType? { | ||
| 104 | + HKObjectType.quantityType(forIdentifier: identifier) | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + static func category(_ identifier: HKCategoryTypeIdentifier) -> HKCategoryType? { | ||
| 108 | + HKObjectType.categoryType(forIdentifier: identifier) | ||
| 109 | + } | ||
| 110 | +} |
| 1 | +import Foundation | ||
| 2 | + | ||
| 3 | +/// Stores the last successful local HealthKit sync boundary. | ||
| 4 | +/// This is intentionally independent from the old SwiftUI user/network layer. | ||
| 5 | +struct HealthSyncStateStore { | ||
| 6 | + private let defaults: UserDefaults | ||
| 7 | + private let keyPrefix = "native_health_sync_latest_time_" | ||
| 8 | + | ||
| 9 | + init(defaults: UserDefaults = .standard) { | ||
| 10 | + self.defaults = defaults | ||
| 11 | + } | ||
| 12 | + | ||
| 13 | + func lastSyncDate(for type: NativeHealthDataType) -> Date? { | ||
| 14 | + let timestamp = defaults.double(forKey: key(for: type)) | ||
| 15 | + return timestamp > 0 ? Date(timeIntervalSince1970: timestamp) : nil | ||
| 16 | + } | ||
| 17 | + | ||
| 18 | + func save(date: Date, for type: NativeHealthDataType) { | ||
| 19 | + defaults.set(date.timeIntervalSince1970, forKey: key(for: type)) | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + func startDate(for type: NativeHealthDataType, fallbackWeeks: Int = 1) -> Date { | ||
| 23 | + if let date = lastSyncDate(for: type) { | ||
| 24 | + return date | ||
| 25 | + } | ||
| 26 | + let fallback = Calendar.current.date(byAdding: .weekOfYear, value: -fallbackWeeks, to: Date()) | ||
| 27 | + return Calendar.current.startOfDay(for: fallback ?? Date()) | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + private func key(for type: NativeHealthDataType) -> String { | ||
| 31 | + "\(keyPrefix)\(type.rawValue)" | ||
| 32 | + } | ||
| 33 | +} | ||
| 34 | + |
| @@ -26,6 +26,12 @@ | @@ -26,6 +26,12 @@ | ||
| 26 | <key>NSAllowsArbitraryLoads</key> | 26 | <key>NSAllowsArbitraryLoads</key> |
| 27 | <true/> | 27 | <true/> |
| 28 | </dict> | 28 | </dict> |
| 29 | + <key>NSHealthShareUsageDescription</key> | ||
| 30 | + <string>需要读取 Apple Health 中的心率、HRV、睡眠、步数、活动能量等数据,用于展示健康状态并同步到 Apple Watch。</string> | ||
| 31 | + <key>NSHealthUpdateUsageDescription</key> | ||
| 32 | + <string>需要写入少量健康数据权限以保持与旧版 Apple Health 同步流程兼容。</string> | ||
| 33 | + <key>NSPhotoLibraryUsageDescription</key> | ||
| 34 | + <string>需要从系统相册选择图片,用于创建自定义 Apple Watch 表盘主题。</string> | ||
| 29 | <key>UIDesignRequiresCompatibility</key> | 35 | <key>UIDesignRequiresCompatibility</key> |
| 30 | <true/> | 36 | <true/> |
| 31 | <key>CADisableMinimumFrameDurationOnPhone</key> | 37 | <key>CADisableMinimumFrameDurationOnPhone</key> |
| @@ -20,6 +20,12 @@ | @@ -20,6 +20,12 @@ | ||
| 20 | <key>NSAllowsArbitraryLoads</key> | 20 | <key>NSAllowsArbitraryLoads</key> |
| 21 | <true/> | 21 | <true/> |
| 22 | </dict> | 22 | </dict> |
| 23 | + <key>NSHealthShareUsageDescription</key> | ||
| 24 | + <string>需要读取 Apple Health 中的心率、HRV、睡眠、步数、活动能量等数据,用于展示健康状态并同步到 Apple Watch。</string> | ||
| 25 | + <key>NSHealthUpdateUsageDescription</key> | ||
| 26 | + <string>需要写入少量健康数据权限以保持与旧版 Apple Health 同步流程兼容。</string> | ||
| 27 | + <key>NSPhotoLibraryUsageDescription</key> | ||
| 28 | + <string>需要从系统相册选择图片,用于创建自定义 Apple Watch 表盘主题。</string> | ||
| 23 | <key>UIDesignRequiresCompatibility</key> | 29 | <key>UIDesignRequiresCompatibility</key> |
| 24 | <true/> | 30 | <true/> |
| 25 | <key>CADisableMinimumFrameDurationOnPhone</key> | 31 | <key>CADisableMinimumFrameDurationOnPhone</key> |
ios/Runner/Pigeon/HealthKitHostApiImpl.swift
0 → 100644
| 1 | +import Foundation | ||
| 2 | + | ||
| 3 | +final class HealthKitHostApiImpl: HealthKitHostApi { | ||
| 4 | + private let service: HealthKitService | ||
| 5 | + | ||
| 6 | + init(service: HealthKitService = .shared) { | ||
| 7 | + self.service = service | ||
| 8 | + } | ||
| 9 | + | ||
| 10 | + func checkHealthAppAuthorization() throws -> Bool { | ||
| 11 | + service.isHealthDataAvailable && !runBlocking { | ||
| 12 | + await self.service.shouldRequestAuthorization() | ||
| 13 | + } | ||
| 14 | + } | ||
| 15 | + | ||
| 16 | + func getHealthServerAuthUrl() throws -> String { | ||
| 17 | + // Apple Health authorization is system-managed, not URL based. | ||
| 18 | + "" | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + func requestHealthClientAuthorization() throws -> Bool { | ||
| 22 | + let result = runAuthorizationRequest() | ||
| 23 | + if result.success { | ||
| 24 | + service.startBackgroundObserversIfNeeded() | ||
| 25 | + Task { | ||
| 26 | + await service.refreshSharedWatchValues() | ||
| 27 | + } | ||
| 28 | + } | ||
| 29 | + if let error = result.error { | ||
| 30 | + throw error | ||
| 31 | + } | ||
| 32 | + return result.success | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + func cancelHealthAppAuthorization() throws -> Bool { | ||
| 36 | + // iOS does not let apps revoke HealthKit permission programmatically. | ||
| 37 | + // Users must revoke access in Settings > Health > Data Access & Devices. | ||
| 38 | + false | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + func performHealthUpload() throws -> HealthUploadResult { | ||
| 42 | + let summary = runBlocking { | ||
| 43 | + await self.service.performLocalSync() | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + return HealthUploadResult( | ||
| 47 | + commonUploadSuccess: summary.commonCount >= 0, | ||
| 48 | + sleepUploadSuccess: summary.sleepCount >= 0, | ||
| 49 | + errorMessage: nil | ||
| 50 | + ) | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + private func runAuthorizationRequest() -> (success: Bool, error: Error?) { | ||
| 54 | + var result: (Bool, Error?) = (false, nil) | ||
| 55 | + let semaphore = DispatchSemaphore(value: 0) | ||
| 56 | + service.requestAuthorization { success, error in | ||
| 57 | + result = (success, error) | ||
| 58 | + semaphore.signal() | ||
| 59 | + } | ||
| 60 | + waitForSemaphore(semaphore) | ||
| 61 | + return result | ||
| 62 | + } | ||
| 63 | +} | ||
| 64 | + | ||
| 65 | +private func runBlocking<T>(_ operation: @escaping () async -> T) -> T { | ||
| 66 | + let semaphore = DispatchSemaphore(value: 0) | ||
| 67 | + var result: T? | ||
| 68 | + Task { | ||
| 69 | + result = await operation() | ||
| 70 | + semaphore.signal() | ||
| 71 | + } | ||
| 72 | + waitForSemaphore(semaphore) | ||
| 73 | + return result! | ||
| 74 | +} | ||
| 75 | + | ||
| 76 | +private func waitForSemaphore(_ semaphore: DispatchSemaphore) { | ||
| 77 | + if Thread.isMainThread { | ||
| 78 | + while semaphore.wait(timeout: .now() + 0.05) == .timedOut { | ||
| 79 | + RunLoop.main.run(mode: .default, before: Date(timeIntervalSinceNow: 0.05)) | ||
| 80 | + } | ||
| 81 | + } else { | ||
| 82 | + semaphore.wait() | ||
| 83 | + } | ||
| 84 | +} |
| 1 | import Flutter | 1 | import Flutter |
| 2 | import Foundation | 2 | import Foundation |
| 3 | -import PhotosUI | ||
| 4 | -import UIKit | ||
| 5 | -import Vision | ||
| 6 | - | ||
| 7 | -private let unsupported = "HealthKit / WearEngine / Alipay are not supported on iOS in this build." | ||
| 8 | - | ||
| 9 | -final class HealthKitHostApiStub: HealthKitHostApi { | ||
| 10 | - func checkHealthAppAuthorization() throws -> Bool { false } | ||
| 11 | - func getHealthServerAuthUrl() throws -> String { "" } | ||
| 12 | - func requestHealthClientAuthorization() throws -> Bool { false } | ||
| 13 | - func cancelHealthAppAuthorization() throws -> Bool { false } | ||
| 14 | - func performHealthUpload() throws -> HealthUploadResult { | ||
| 15 | - HealthUploadResult(commonUploadSuccess: false, sleepUploadSuccess: false, errorMessage: unsupported) | ||
| 16 | - } | ||
| 17 | -} | ||
| 18 | - | ||
| 19 | -final class WearEngineHostApiStub: WearEngineHostApi { | ||
| 20 | - func hasAvailableDevices() throws -> Bool { false } | ||
| 21 | - func checkConnectedDevice() throws -> WearDeviceInfo? { nil } | ||
| 22 | - func registerMessageReceiver() throws -> Bool { false } | ||
| 23 | - func sendTextMessage(message: String) throws -> Bool { false } | ||
| 24 | - func sendWatchSyncPayload(jsonPayload: String) throws -> Bool { false } | ||
| 25 | - func pickImageAndRemoveBackground() throws -> String? { | ||
| 26 | - if #available(iOS 14.0, *) { | ||
| 27 | - return WatchThemeImagePicker().pickImageAndRemoveBackground() | ||
| 28 | - } | ||
| 29 | - return nil | ||
| 30 | - } | ||
| 31 | -} | ||
| 32 | 3 | ||
| 33 | final class AlipayHostApiStub: AlipayHostApi { | 4 | final class AlipayHostApiStub: AlipayHostApi { |
| 34 | func launchAliPay(prepayData: String) throws -> AliPayResultCode { .unsupported } | 5 | func launchAliPay(prepayData: String) throws -> AliPayResultCode { .unsupported } |
| @@ -36,203 +7,10 @@ final class AlipayHostApiStub: AlipayHostApi { | @@ -36,203 +7,10 @@ final class AlipayHostApiStub: AlipayHostApi { | ||
| 36 | 7 | ||
| 37 | enum NativePigeonRegistrar { | 8 | enum NativePigeonRegistrar { |
| 38 | static func register(binaryMessenger: FlutterBinaryMessenger) { | 9 | static func register(binaryMessenger: FlutterBinaryMessenger) { |
| 39 | - HealthKitHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: HealthKitHostApiStub()) | ||
| 40 | - WearEngineHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: WearEngineHostApiStub()) | 10 | + HealthKitHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: HealthKitHostApiImpl()) |
| 11 | + WearEngineHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: WearEngineHostApiImpl()) | ||
| 41 | AlipayHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: AlipayHostApiStub()) | 12 | AlipayHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: AlipayHostApiStub()) |
| 42 | PlatformHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: PlatformHostApiImpl()) | 13 | PlatformHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: PlatformHostApiImpl()) |
| 43 | } | 14 | } |
| 44 | } | 15 | } |
| 45 | 16 | ||
| 46 | -@available(iOS 14.0, *) | ||
| 47 | -private final class WatchThemeImagePicker: NSObject, PHPickerViewControllerDelegate { | ||
| 48 | - private var continuation: CheckedContinuation<String?, Never>? | ||
| 49 | - | ||
| 50 | - func pickImageAndRemoveBackground() -> String? { | ||
| 51 | - if Thread.isMainThread { | ||
| 52 | - return runOnMain() | ||
| 53 | - } | ||
| 54 | - | ||
| 55 | - var result: String? | ||
| 56 | - let semaphore = DispatchSemaphore(value: 0) | ||
| 57 | - DispatchQueue.main.async { | ||
| 58 | - result = self.runOnMain() | ||
| 59 | - semaphore.signal() | ||
| 60 | - } | ||
| 61 | - semaphore.wait() | ||
| 62 | - return result | ||
| 63 | - } | ||
| 64 | - | ||
| 65 | - private func runOnMain() -> String? { | ||
| 66 | - var configuration = PHPickerConfiguration(photoLibrary: .shared()) | ||
| 67 | - configuration.filter = .images | ||
| 68 | - configuration.selectionLimit = 1 | ||
| 69 | - | ||
| 70 | - guard let presenter = UIApplication.shared.topMostViewController else { | ||
| 71 | - return nil | ||
| 72 | - } | ||
| 73 | - | ||
| 74 | - let picker = PHPickerViewController(configuration: configuration) | ||
| 75 | - picker.delegate = self | ||
| 76 | - | ||
| 77 | - return waitForPickerResult(picker: picker, presenter: presenter) | ||
| 78 | - } | ||
| 79 | - | ||
| 80 | - private func waitForPickerResult( | ||
| 81 | - picker: PHPickerViewController, | ||
| 82 | - presenter: UIViewController | ||
| 83 | - ) -> String? { | ||
| 84 | - var pickedPath: String? | ||
| 85 | - let semaphore = DispatchSemaphore(value: 0) | ||
| 86 | - | ||
| 87 | - Task { @MainActor in | ||
| 88 | - pickedPath = await withCheckedContinuation { continuation in | ||
| 89 | - self.continuation = continuation | ||
| 90 | - presenter.present(picker, animated: true) | ||
| 91 | - } | ||
| 92 | - semaphore.signal() | ||
| 93 | - } | ||
| 94 | - | ||
| 95 | - while semaphore.wait(timeout: .now() + 0.05) == .timedOut { | ||
| 96 | - RunLoop.main.run(mode: .default, before: Date(timeIntervalSinceNow: 0.05)) | ||
| 97 | - } | ||
| 98 | - return pickedPath | ||
| 99 | - } | ||
| 100 | - | ||
| 101 | - nonisolated func picker( | ||
| 102 | - _ picker: PHPickerViewController, | ||
| 103 | - didFinishPicking results: [PHPickerResult] | ||
| 104 | - ) { | ||
| 105 | - Task { @MainActor in | ||
| 106 | - picker.dismiss(animated: true) | ||
| 107 | - | ||
| 108 | - guard let provider = results.first?.itemProvider, | ||
| 109 | - provider.canLoadObject(ofClass: UIImage.self) else { | ||
| 110 | - continuation?.resume(returning: nil) | ||
| 111 | - continuation = nil | ||
| 112 | - return | ||
| 113 | - } | ||
| 114 | - | ||
| 115 | - provider.loadObject(ofClass: UIImage.self) { object, _ in | ||
| 116 | - Task { @MainActor in | ||
| 117 | - guard let image = object as? UIImage else { | ||
| 118 | - self.continuation?.resume(returning: nil) | ||
| 119 | - self.continuation = nil | ||
| 120 | - return | ||
| 121 | - } | ||
| 122 | - let processed = await WatchThemeImageProcessor.removeBackground(from: image) | ||
| 123 | - let path = WatchThemeImageProcessor.savePNG(processed) | ||
| 124 | - self.continuation?.resume(returning: path) | ||
| 125 | - self.continuation = nil | ||
| 126 | - } | ||
| 127 | - } | ||
| 128 | - } | ||
| 129 | - } | ||
| 130 | -} | ||
| 131 | - | ||
| 132 | -private enum WatchThemeImageProcessor { | ||
| 133 | - static func removeBackground(from image: UIImage) async -> UIImage { | ||
| 134 | - guard #available(iOS 17.0, *), | ||
| 135 | - let cgImage = image.normalizedCGImage else { | ||
| 136 | - return image | ||
| 137 | - } | ||
| 138 | - | ||
| 139 | - return await Task.detached(priority: .userInitiated) { | ||
| 140 | - let request = VNGenerateForegroundInstanceMaskRequest() | ||
| 141 | - let handler = VNImageRequestHandler(cgImage: cgImage) | ||
| 142 | - | ||
| 143 | - do { | ||
| 144 | - try handler.perform([request]) | ||
| 145 | - guard let observation = request.results?.first else { | ||
| 146 | - return image | ||
| 147 | - } | ||
| 148 | - | ||
| 149 | - let mask = try observation.generateScaledMaskForImage( | ||
| 150 | - forInstances: observation.allInstances, | ||
| 151 | - from: handler | ||
| 152 | - ) | ||
| 153 | - return composite(image: cgImage, mask: mask) ?? image | ||
| 154 | - } catch { | ||
| 155 | - return image | ||
| 156 | - } | ||
| 157 | - }.value | ||
| 158 | - } | ||
| 159 | - | ||
| 160 | - static func savePNG(_ image: UIImage) -> String? { | ||
| 161 | - guard let data = image.pngData() else { return nil } | ||
| 162 | - let directory = FileManager.default.temporaryDirectory | ||
| 163 | - .appendingPathComponent("watch_theme", isDirectory: true) | ||
| 164 | - | ||
| 165 | - do { | ||
| 166 | - try FileManager.default.createDirectory( | ||
| 167 | - at: directory, | ||
| 168 | - withIntermediateDirectories: true | ||
| 169 | - ) | ||
| 170 | - let file = directory.appendingPathComponent("\(UUID().uuidString).png") | ||
| 171 | - try data.write(to: file, options: .atomic) | ||
| 172 | - return file.path | ||
| 173 | - } catch { | ||
| 174 | - return nil | ||
| 175 | - } | ||
| 176 | - } | ||
| 177 | - | ||
| 178 | - private static func composite(image: CGImage, mask: CVPixelBuffer) -> UIImage? { | ||
| 179 | - let ciImage = CIImage(cgImage: image) | ||
| 180 | - let ciMask = CIImage(cvPixelBuffer: mask) | ||
| 181 | - guard let filter = CIFilter(name: "CIBlendWithMask") else { | ||
| 182 | - return nil | ||
| 183 | - } | ||
| 184 | - filter.setValue(ciImage, forKey: kCIInputImageKey) | ||
| 185 | - filter.setValue(ciMask, forKey: kCIInputMaskImageKey) | ||
| 186 | - filter.setValue( | ||
| 187 | - CIImage(color: .clear).cropped(to: ciImage.extent), | ||
| 188 | - forKey: kCIInputBackgroundImageKey | ||
| 189 | - ) | ||
| 190 | - | ||
| 191 | - guard let output = filter.outputImage, | ||
| 192 | - let cgOutput = CIContext().createCGImage(output, from: ciImage.extent) else { | ||
| 193 | - return nil | ||
| 194 | - } | ||
| 195 | - return UIImage(cgImage: cgOutput, scale: 1, orientation: .up) | ||
| 196 | - } | ||
| 197 | -} | ||
| 198 | - | ||
| 199 | -private extension UIImage { | ||
| 200 | - var normalizedCGImage: CGImage? { | ||
| 201 | - if imageOrientation == .up, let cgImage { | ||
| 202 | - return cgImage | ||
| 203 | - } | ||
| 204 | - | ||
| 205 | - let format = UIGraphicsImageRendererFormat.default() | ||
| 206 | - format.scale = scale | ||
| 207 | - let renderer = UIGraphicsImageRenderer(size: size, format: format) | ||
| 208 | - return renderer.image { _ in | ||
| 209 | - draw(in: CGRect(origin: .zero, size: size)) | ||
| 210 | - }.cgImage | ||
| 211 | - } | ||
| 212 | -} | ||
| 213 | - | ||
| 214 | -private extension UIApplication { | ||
| 215 | - var topMostViewController: UIViewController? { | ||
| 216 | - connectedScenes | ||
| 217 | - .compactMap { $0 as? UIWindowScene } | ||
| 218 | - .flatMap(\.windows) | ||
| 219 | - .first { $0.isKeyWindow }? | ||
| 220 | - .rootViewController? | ||
| 221 | - .topMostPresented | ||
| 222 | - } | ||
| 223 | -} | ||
| 224 | - | ||
| 225 | -private extension UIViewController { | ||
| 226 | - var topMostPresented: UIViewController { | ||
| 227 | - if let presentedViewController { | ||
| 228 | - return presentedViewController.topMostPresented | ||
| 229 | - } | ||
| 230 | - if let navigationController = self as? UINavigationController { | ||
| 231 | - return navigationController.visibleViewController?.topMostPresented ?? navigationController | ||
| 232 | - } | ||
| 233 | - if let tabBarController = self as? UITabBarController { | ||
| 234 | - return tabBarController.selectedViewController?.topMostPresented ?? tabBarController | ||
| 235 | - } | ||
| 236 | - return self | ||
| 237 | - } | ||
| 238 | -} |
| 1 | +import Foundation | ||
| 2 | +import PhotosUI | ||
| 3 | +import UIKit | ||
| 4 | +import Vision | ||
| 5 | + | ||
| 6 | +final class WearEngineHostApiImpl: WearEngineHostApi { | ||
| 7 | + private let watchService: WatchConnectivityService | ||
| 8 | + | ||
| 9 | + init(watchService: WatchConnectivityService = .shared) { | ||
| 10 | + self.watchService = watchService | ||
| 11 | + } | ||
| 12 | + | ||
| 13 | + func hasAvailableDevices() throws -> Bool { | ||
| 14 | + watchService.hasAvailableDevices() | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + func checkConnectedDevice() throws -> WearDeviceInfo? { | ||
| 18 | + watchService.currentDeviceInfo() | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + func registerMessageReceiver() throws -> Bool { | ||
| 22 | + watchService.activate() | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + func sendTextMessage(message: String) throws -> Bool { | ||
| 26 | + watchService.sendTextMessage(message) | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + func sendWatchSyncPayload(jsonPayload: String) throws -> Bool { | ||
| 30 | + watchService.syncPayload(jsonPayload: jsonPayload) | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + func pickImageAndRemoveBackground() throws -> String? { | ||
| 34 | + if #available(iOS 14.0, *) { | ||
| 35 | + return WatchThemeImagePicker().pickImageAndRemoveBackground() | ||
| 36 | + } | ||
| 37 | + return nil | ||
| 38 | + } | ||
| 39 | +} | ||
| 40 | + | ||
| 41 | +@available(iOS 14.0, *) | ||
| 42 | +private final class WatchThemeImagePicker: NSObject, PHPickerViewControllerDelegate { | ||
| 43 | + private var continuation: CheckedContinuation<String?, Never>? | ||
| 44 | + | ||
| 45 | + func pickImageAndRemoveBackground() -> String? { | ||
| 46 | + if Thread.isMainThread { | ||
| 47 | + return runOnMain() | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + var result: String? | ||
| 51 | + let semaphore = DispatchSemaphore(value: 0) | ||
| 52 | + DispatchQueue.main.async { | ||
| 53 | + result = self.runOnMain() | ||
| 54 | + semaphore.signal() | ||
| 55 | + } | ||
| 56 | + semaphore.wait() | ||
| 57 | + return result | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + private func runOnMain() -> String? { | ||
| 61 | + var configuration = PHPickerConfiguration(photoLibrary: .shared()) | ||
| 62 | + configuration.filter = .images | ||
| 63 | + configuration.selectionLimit = 1 | ||
| 64 | + | ||
| 65 | + guard let presenter = UIApplication.shared.topMostViewController else { | ||
| 66 | + return nil | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + let picker = PHPickerViewController(configuration: configuration) | ||
| 70 | + picker.delegate = self | ||
| 71 | + | ||
| 72 | + return waitForPickerResult(picker: picker, presenter: presenter) | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + private func waitForPickerResult( | ||
| 76 | + picker: PHPickerViewController, | ||
| 77 | + presenter: UIViewController | ||
| 78 | + ) -> String? { | ||
| 79 | + var pickedPath: String? | ||
| 80 | + let semaphore = DispatchSemaphore(value: 0) | ||
| 81 | + | ||
| 82 | + Task { @MainActor in | ||
| 83 | + pickedPath = await withCheckedContinuation { continuation in | ||
| 84 | + self.continuation = continuation | ||
| 85 | + presenter.present(picker, animated: true) | ||
| 86 | + } | ||
| 87 | + semaphore.signal() | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + while semaphore.wait(timeout: .now() + 0.05) == .timedOut { | ||
| 91 | + RunLoop.main.run(mode: .default, before: Date(timeIntervalSinceNow: 0.05)) | ||
| 92 | + } | ||
| 93 | + return pickedPath | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + nonisolated func picker( | ||
| 97 | + _ picker: PHPickerViewController, | ||
| 98 | + didFinishPicking results: [PHPickerResult] | ||
| 99 | + ) { | ||
| 100 | + Task { @MainActor in | ||
| 101 | + picker.dismiss(animated: true) | ||
| 102 | + | ||
| 103 | + guard let provider = results.first?.itemProvider, | ||
| 104 | + provider.canLoadObject(ofClass: UIImage.self) else { | ||
| 105 | + continuation?.resume(returning: nil) | ||
| 106 | + continuation = nil | ||
| 107 | + return | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + provider.loadObject(ofClass: UIImage.self) { object, _ in | ||
| 111 | + Task { @MainActor in | ||
| 112 | + guard let image = object as? UIImage else { | ||
| 113 | + self.continuation?.resume(returning: nil) | ||
| 114 | + self.continuation = nil | ||
| 115 | + return | ||
| 116 | + } | ||
| 117 | + let processed = await WatchThemeImageProcessor.removeBackground(from: image) | ||
| 118 | + let path = WatchThemeImageProcessor.savePNG(processed) | ||
| 119 | + self.continuation?.resume(returning: path) | ||
| 120 | + self.continuation = nil | ||
| 121 | + } | ||
| 122 | + } | ||
| 123 | + } | ||
| 124 | + } | ||
| 125 | +} | ||
| 126 | + | ||
| 127 | +private enum WatchThemeImageProcessor { | ||
| 128 | + static func removeBackground(from image: UIImage) async -> UIImage { | ||
| 129 | + guard #available(iOS 17.0, *), | ||
| 130 | + let cgImage = image.normalizedCGImage else { | ||
| 131 | + return image | ||
| 132 | + } | ||
| 133 | + | ||
| 134 | + return await Task.detached(priority: .userInitiated) { | ||
| 135 | + let request = VNGenerateForegroundInstanceMaskRequest() | ||
| 136 | + let handler = VNImageRequestHandler(cgImage: cgImage) | ||
| 137 | + | ||
| 138 | + do { | ||
| 139 | + try handler.perform([request]) | ||
| 140 | + guard let observation = request.results?.first else { | ||
| 141 | + return image | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + let mask = try observation.generateScaledMaskForImage( | ||
| 145 | + forInstances: observation.allInstances, | ||
| 146 | + from: handler | ||
| 147 | + ) | ||
| 148 | + return composite(image: cgImage, mask: mask) ?? image | ||
| 149 | + } catch { | ||
| 150 | + return image | ||
| 151 | + } | ||
| 152 | + }.value | ||
| 153 | + } | ||
| 154 | + | ||
| 155 | + static func savePNG(_ image: UIImage) -> String? { | ||
| 156 | + guard let data = image.pngData() else { return nil } | ||
| 157 | + let directory = FileManager.default.temporaryDirectory | ||
| 158 | + .appendingPathComponent("watch_theme", isDirectory: true) | ||
| 159 | + | ||
| 160 | + do { | ||
| 161 | + try FileManager.default.createDirectory( | ||
| 162 | + at: directory, | ||
| 163 | + withIntermediateDirectories: true | ||
| 164 | + ) | ||
| 165 | + let file = directory.appendingPathComponent("\(UUID().uuidString).png") | ||
| 166 | + try data.write(to: file, options: .atomic) | ||
| 167 | + return file.path | ||
| 168 | + } catch { | ||
| 169 | + return nil | ||
| 170 | + } | ||
| 171 | + } | ||
| 172 | + | ||
| 173 | + private static func composite(image: CGImage, mask: CVPixelBuffer) -> UIImage? { | ||
| 174 | + let ciImage = CIImage(cgImage: image) | ||
| 175 | + let ciMask = CIImage(cvPixelBuffer: mask) | ||
| 176 | + guard let filter = CIFilter(name: "CIBlendWithMask") else { | ||
| 177 | + return nil | ||
| 178 | + } | ||
| 179 | + filter.setValue(ciImage, forKey: kCIInputImageKey) | ||
| 180 | + filter.setValue(ciMask, forKey: kCIInputMaskImageKey) | ||
| 181 | + filter.setValue( | ||
| 182 | + CIImage(color: .clear).cropped(to: ciImage.extent), | ||
| 183 | + forKey: kCIInputBackgroundImageKey | ||
| 184 | + ) | ||
| 185 | + | ||
| 186 | + guard let output = filter.outputImage, | ||
| 187 | + let cgOutput = CIContext().createCGImage(output, from: ciImage.extent) else { | ||
| 188 | + return nil | ||
| 189 | + } | ||
| 190 | + return UIImage(cgImage: cgOutput, scale: 1, orientation: .up) | ||
| 191 | + } | ||
| 192 | +} | ||
| 193 | + | ||
| 194 | +private extension UIImage { | ||
| 195 | + var normalizedCGImage: CGImage? { | ||
| 196 | + if imageOrientation == .up, let cgImage { | ||
| 197 | + return cgImage | ||
| 198 | + } | ||
| 199 | + | ||
| 200 | + let format = UIGraphicsImageRendererFormat.default() | ||
| 201 | + format.scale = scale | ||
| 202 | + let renderer = UIGraphicsImageRenderer(size: size, format: format) | ||
| 203 | + return renderer.image { _ in | ||
| 204 | + draw(in: CGRect(origin: .zero, size: size)) | ||
| 205 | + }.cgImage | ||
| 206 | + } | ||
| 207 | +} | ||
| 208 | + | ||
| 209 | +private extension UIApplication { | ||
| 210 | + var topMostViewController: UIViewController? { | ||
| 211 | + connectedScenes | ||
| 212 | + .compactMap { $0 as? UIWindowScene } | ||
| 213 | + .flatMap(\.windows) | ||
| 214 | + .first { $0.isKeyWindow }? | ||
| 215 | + .rootViewController? | ||
| 216 | + .topMostPresented | ||
| 217 | + } | ||
| 218 | +} | ||
| 219 | + | ||
| 220 | +private extension UIViewController { | ||
| 221 | + var topMostPresented: UIViewController { | ||
| 222 | + if let presentedViewController { | ||
| 223 | + return presentedViewController.topMostPresented | ||
| 224 | + } | ||
| 225 | + if let navigationController = self as? UINavigationController { | ||
| 226 | + return navigationController.visibleViewController?.topMostPresented ?? navigationController | ||
| 227 | + } | ||
| 228 | + if let tabBarController = self as? UITabBarController { | ||
| 229 | + return tabBarController.selectedViewController?.topMostPresented ?? tabBarController | ||
| 230 | + } | ||
| 231 | + return self | ||
| 232 | + } | ||
| 233 | +} | ||
| 234 | + |
| @@ -8,6 +8,10 @@ | @@ -8,6 +8,10 @@ | ||
| 8 | <true/> | 8 | <true/> |
| 9 | <key>com.apple.developer.healthkit.background-delivery</key> | 9 | <key>com.apple.developer.healthkit.background-delivery</key> |
| 10 | <true/> | 10 | <true/> |
| 11 | + <key>com.apple.security.application-groups</key> | ||
| 12 | + <array> | ||
| 13 | + <string>group.com.luiz.doublefeel.watchkitapp</string> | ||
| 14 | + </array> | ||
| 11 | <key>com.apple.security.cs.allow-jit</key> | 15 | <key>com.apple.security.cs.allow-jit</key> |
| 12 | <true/> | 16 | <true/> |
| 13 | <key>com.apple.security.cs.allow-unsigned-executable-memory</key> | 17 | <key>com.apple.security.cs.allow-unsigned-executable-memory</key> |
| @@ -8,5 +8,9 @@ | @@ -8,5 +8,9 @@ | ||
| 8 | <true/> | 8 | <true/> |
| 9 | <key>com.apple.developer.healthkit.background-delivery</key> | 9 | <key>com.apple.developer.healthkit.background-delivery</key> |
| 10 | <true/> | 10 | <true/> |
| 11 | + <key>com.apple.security.application-groups</key> | ||
| 12 | + <array> | ||
| 13 | + <string>group.com.luiz.doublefeel.watchkitapp</string> | ||
| 14 | + </array> | ||
| 11 | </dict> | 15 | </dict> |
| 12 | </plist> | 16 | </plist> |
ios/Runner/Shared/AppGroupConstants.swift
0 → 100644
| 1 | +import Foundation | ||
| 2 | + | ||
| 3 | +/// Shared container used by the iPhone app, Watch app, and Widget extension. | ||
| 4 | +/// Keep these keys aligned with `ios/Runner Watch App` and the watch extension. | ||
| 5 | +enum AppGroupConstants { | ||
| 6 | + static let identifier = "group.com.luiz.doublefeel.watchkitapp" | ||
| 7 | + | ||
| 8 | + enum Key { | ||
| 9 | + static let latestHRV = "latestHRV" | ||
| 10 | + static let latestHRVBaseline = "latestHRVBaseline" | ||
| 11 | + static let latestStepCount = "latestStepCount" | ||
| 12 | + static let myWatchTheme = "myWatchTheme" | ||
| 13 | + static let otherWatchTheme = "otherWatchTheme" | ||
| 14 | + static let myUserCharacter = "myUserCharacter" | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + static var defaults: UserDefaults? { | ||
| 18 | + UserDefaults(suiteName: identifier) | ||
| 19 | + } | ||
| 20 | +} | ||
| 21 | + |
| 1 | +import Foundation | ||
| 2 | +import UIKit | ||
| 3 | +import WatchConnectivity | ||
| 4 | + | ||
| 5 | +/// iPhone-side WatchConnectivity wrapper. | ||
| 6 | +/// This keeps Flutter-facing WearEngine APIs mapped to Apple Watch behavior. | ||
| 7 | +final class WatchConnectivityService: NSObject { | ||
| 8 | + static let shared = WatchConnectivityService() | ||
| 9 | + | ||
| 10 | + private(set) var isPaired = false | ||
| 11 | + private(set) var isReachable = false | ||
| 12 | + private(set) var isWatchAppInstalled = false | ||
| 13 | + | ||
| 14 | + private override init() { | ||
| 15 | + super.init() | ||
| 16 | + } | ||
| 17 | + | ||
| 18 | + @discardableResult | ||
| 19 | + func activate() -> Bool { | ||
| 20 | + guard WCSession.isSupported() else { return false } | ||
| 21 | + let session = WCSession.default | ||
| 22 | + session.delegate = self | ||
| 23 | + session.activate() | ||
| 24 | + refreshState() | ||
| 25 | + return true | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + func refreshState() { | ||
| 29 | + guard WCSession.isSupported() else { | ||
| 30 | + isPaired = false | ||
| 31 | + isReachable = false | ||
| 32 | + isWatchAppInstalled = false | ||
| 33 | + return | ||
| 34 | + } | ||
| 35 | + let session = WCSession.default | ||
| 36 | + isPaired = session.isPaired | ||
| 37 | + isReachable = session.isReachable | ||
| 38 | + isWatchAppInstalled = session.isWatchAppInstalled | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + func hasAvailableDevices() -> Bool { | ||
| 42 | + activate() | ||
| 43 | + refreshState() | ||
| 44 | + return isPaired && isWatchAppInstalled | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + func currentDeviceInfo() -> WearDeviceInfo? { | ||
| 48 | + activate() | ||
| 49 | + refreshState() | ||
| 50 | + guard isPaired else { return nil } | ||
| 51 | + return WearDeviceInfo( | ||
| 52 | + deviceId: nil, | ||
| 53 | + deviceName: "Apple Watch", | ||
| 54 | + isConnected: isReachable && isWatchAppInstalled | ||
| 55 | + ) | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + func sendTextMessage(_ message: String) -> Bool { | ||
| 59 | + activate() | ||
| 60 | + guard WCSession.default.isReachable else { return false } | ||
| 61 | + WCSession.default.sendMessage(["message": message], replyHandler: nil) { error in | ||
| 62 | + print("Watch message failed: \(error.localizedDescription)") | ||
| 63 | + } | ||
| 64 | + return true | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + func syncPayload(jsonPayload: String) -> Bool { | ||
| 68 | + activate() | ||
| 69 | + guard let data = jsonPayload.data(using: .utf8), | ||
| 70 | + let object = try? JSONSerialization.jsonObject(with: data), | ||
| 71 | + let payload = object as? [String: Any] else { | ||
| 72 | + return false | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + persistThemeIfPresent(payload: payload, originalJSON: jsonPayload) | ||
| 76 | + | ||
| 77 | + guard WCSession.isSupported(), WCSession.default.isPaired, WCSession.default.isWatchAppInstalled else { | ||
| 78 | + return false | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + WCSession.default.transferUserInfo(payload) | ||
| 82 | + try? WCSession.default.updateApplicationContext(payload) | ||
| 83 | + sendWatchThemeChangedMessageIfNeeded(payload: payload) | ||
| 84 | + return true | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + func sendWatchThemeChangedMessage() { | ||
| 88 | + activate() | ||
| 89 | + guard WCSession.default.isReachable else { return } | ||
| 90 | + WCSession.default.sendMessage(["command": "watchThemeChanged"], replyHandler: nil) { error in | ||
| 91 | + print("Watch theme change message failed: \(error.localizedDescription)") | ||
| 92 | + } | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + private func sendWatchThemeChangedMessageIfNeeded(payload: [String: Any]) { | ||
| 96 | + if payload["myWatchTheme"] != nil || payload["watchTheme"] != nil || payload["theme"] != nil { | ||
| 97 | + sendWatchThemeChangedMessage() | ||
| 98 | + } | ||
| 99 | + } | ||
| 100 | + | ||
| 101 | + private func persistThemeIfPresent(payload: [String: Any], originalJSON: String) { | ||
| 102 | + if let theme = payload["myWatchTheme"] as? String { | ||
| 103 | + WatchThemeStore.shared.saveThemeJSONString(theme) | ||
| 104 | + return | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + let themeObject = payload["watchTheme"] ?? payload["theme"] | ||
| 108 | + guard let themeObject, | ||
| 109 | + JSONSerialization.isValidJSONObject(themeObject), | ||
| 110 | + let data = try? JSONSerialization.data(withJSONObject: themeObject), | ||
| 111 | + let json = String(data: data, encoding: .utf8) else { | ||
| 112 | + if payload["isWatchTheme"] as? Bool == true { | ||
| 113 | + WatchThemeStore.shared.saveThemeJSONString(originalJSON) | ||
| 114 | + } | ||
| 115 | + return | ||
| 116 | + } | ||
| 117 | + WatchThemeStore.shared.saveThemeJSONString(json) | ||
| 118 | + } | ||
| 119 | +} | ||
| 120 | + | ||
| 121 | +extension WatchConnectivityService: WCSessionDelegate { | ||
| 122 | + func session( | ||
| 123 | + _ session: WCSession, | ||
| 124 | + activationDidCompleteWith activationState: WCSessionActivationState, | ||
| 125 | + error: Error? | ||
| 126 | + ) { | ||
| 127 | + refreshState() | ||
| 128 | + if let error { | ||
| 129 | + print("WatchConnectivity activation failed: \(error.localizedDescription)") | ||
| 130 | + } | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + func sessionDidBecomeInactive(_ session: WCSession) { | ||
| 134 | + refreshState() | ||
| 135 | + } | ||
| 136 | + | ||
| 137 | + func sessionDidDeactivate(_ session: WCSession) { | ||
| 138 | + refreshState() | ||
| 139 | + session.activate() | ||
| 140 | + } | ||
| 141 | + | ||
| 142 | + func sessionReachabilityDidChange(_ session: WCSession) { | ||
| 143 | + refreshState() | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + func session(_ session: WCSession, didReceiveMessage message: [String: Any]) { | ||
| 147 | + if message["command"] as? String == "statusPulseRefresh" { | ||
| 148 | + Task { | ||
| 149 | + await HealthKitService.shared.refreshSharedWatchValues() | ||
| 150 | + } | ||
| 151 | + } | ||
| 152 | + } | ||
| 153 | + | ||
| 154 | + func session( | ||
| 155 | + _ session: WCSession, | ||
| 156 | + didReceiveMessage message: [String: Any], | ||
| 157 | + replyHandler: @escaping ([String: Any]) -> Void | ||
| 158 | + ) { | ||
| 159 | + if message["command"] as? String == "statusPulseRefresh" { | ||
| 160 | + Task { | ||
| 161 | + await HealthKitService.shared.refreshSharedWatchValues() | ||
| 162 | + replyHandler(["success": true]) | ||
| 163 | + } | ||
| 164 | + } else { | ||
| 165 | + replyHandler(["success": true]) | ||
| 166 | + } | ||
| 167 | + } | ||
| 168 | +} | ||
| 169 | + |
ios/Runner/Watch/WatchThemeStore.swift
0 → 100644
| 1 | +import Foundation | ||
| 2 | + | ||
| 3 | +/// Shared Watch theme persistence. | ||
| 4 | +/// The Watch app and Widget extension read this exact JSON string from App Group. | ||
| 5 | +final class WatchThemeStore { | ||
| 6 | + static let shared = WatchThemeStore() | ||
| 7 | + | ||
| 8 | + private init() {} | ||
| 9 | + | ||
| 10 | + @discardableResult | ||
| 11 | + func saveThemeJSONString(_ json: String) -> Bool { | ||
| 12 | + guard json.data(using: .utf8) != nil else { return false } | ||
| 13 | + AppGroupConstants.defaults?.set(json, forKey: AppGroupConstants.Key.myWatchTheme) | ||
| 14 | + return true | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + func clearTheme() { | ||
| 18 | + AppGroupConstants.defaults?.removeObject(forKey: AppGroupConstants.Key.myWatchTheme) | ||
| 19 | + } | ||
| 20 | +} | ||
| 21 | + |
ios/build/.last_build_id
deleted
100644 → 0
| 1 | -c134254776a5ccd144c8a5e627d78c10 |
This diff could not be displayed because it is too large.
| 1 | -{"buildConfigurations":[{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98e8fb97e4d8fc6b27d73383f393c88ecd","buildSettings":{"CODE_SIGNING_ALLOWED":"NO","CODE_SIGNING_IDENTITY":"-","CODE_SIGNING_REQUIRED":"NO","CONFIGURATION_BUILD_DIR":"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/TOCropViewController","EXPANDED_CODE_SIGN_IDENTITY":"-","IBSC_MODULE":"TOCropViewController","INFOPLIST_FILE":"Target Support Files/TOCropViewController/ResourceBundle-TOCropViewControllerBundle-TOCropViewController-Info.plist","IPHONEOS_DEPLOYMENT_TARGET":"11.0","ONLY_ACTIVE_ARCH":"NO","PRODUCT_NAME":"TOCropViewControllerBundle","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","WRAPPER_EXTENSION":"bundle"},"guid":"bfdfe7dc352907fc980b868725387e98b75d1048f68d3bb5985c770bd41c3964","name":"Debug"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98646a08d73cb1b05edde4b2af727d2773","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CODE_SIGNING_ALLOWED":"NO","CODE_SIGNING_IDENTITY":"-","CODE_SIGNING_REQUIRED":"NO","CONFIGURATION_BUILD_DIR":"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/TOCropViewController","EXPANDED_CODE_SIGN_IDENTITY":"-","IBSC_MODULE":"TOCropViewController","INFOPLIST_FILE":"Target Support Files/TOCropViewController/ResourceBundle-TOCropViewControllerBundle-TOCropViewController-Info.plist","IPHONEOS_DEPLOYMENT_TARGET":"11.0","PRODUCT_NAME":"TOCropViewControllerBundle","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","WRAPPER_EXTENSION":"bundle"},"guid":"bfdfe7dc352907fc980b868725387e98e9947f3bc60a9a376173a132421b2278","name":"Profile"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98646a08d73cb1b05edde4b2af727d2773","buildSettings":{"CODE_SIGNING_ALLOWED":"NO","CODE_SIGNING_IDENTITY":"-","CODE_SIGNING_REQUIRED":"NO","CONFIGURATION_BUILD_DIR":"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/TOCropViewController","EXPANDED_CODE_SIGN_IDENTITY":"-","IBSC_MODULE":"TOCropViewController","INFOPLIST_FILE":"Target Support Files/TOCropViewController/ResourceBundle-TOCropViewControllerBundle-TOCropViewController-Info.plist","IPHONEOS_DEPLOYMENT_TARGET":"11.0","PRODUCT_NAME":"TOCropViewControllerBundle","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","WRAPPER_EXTENSION":"bundle"},"guid":"bfdfe7dc352907fc980b868725387e983751bd601c66cda9f4c7c8b8aed619ef","name":"Release"}],"buildPhases":[{"buildFiles":[],"guid":"bfdfe7dc352907fc980b868725387e98c2917c5d20bcd9c007f37af8611de2a6","type":"com.apple.buildphase.sources"},{"buildFiles":[],"guid":"bfdfe7dc352907fc980b868725387e986afffa2cfd2f9512aa233f5cc63fb570","type":"com.apple.buildphase.frameworks"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e98b3f0c66e128d272bb1288ea8780dc9b4","guid":"bfdfe7dc352907fc980b868725387e9803b9b1af2d5261479f7f93af035f84ad"},{"fileReference":"bfdfe7dc352907fc980b868725387e98943d94e1dca9370afcbc7d2e9f0abbad","guid":"bfdfe7dc352907fc980b868725387e98437b8113d0c0f7efa46ee29b722c79cb"},{"fileReference":"bfdfe7dc352907fc980b868725387e98e20d522eae8962c5e533c7c249450ba1","guid":"bfdfe7dc352907fc980b868725387e9895e3b6c1100dde567d8298da959ab892"},{"fileReference":"bfdfe7dc352907fc980b868725387e9875b23b6d8bdbfaadd15a183140d79f69","guid":"bfdfe7dc352907fc980b868725387e984f33ed908452ea332941903c0411b3ef"},{"fileReference":"bfdfe7dc352907fc980b868725387e98e0f2b7de1c1bc04e1ffe46c68ce33ad8","guid":"bfdfe7dc352907fc980b868725387e9826d2fa37af38d4d77992079be9e49bbe"},{"fileReference":"bfdfe7dc352907fc980b868725387e987b5a1086ea8be4d9b051d3ce7999d172","guid":"bfdfe7dc352907fc980b868725387e98c047cfdc484f9f46ed24109dd7964cab"},{"fileReference":"bfdfe7dc352907fc980b868725387e98d2785ef910e4b678590a3f07d27b2b26","guid":"bfdfe7dc352907fc980b868725387e98f17441845b3284079bd0a8af3efb9a94"},{"fileReference":"bfdfe7dc352907fc980b868725387e98436252fbeeed3265eb2cee95eb12a14a","guid":"bfdfe7dc352907fc980b868725387e9853a0872068d9802e4e7ef5e78684536a"},{"fileReference":"bfdfe7dc352907fc980b868725387e98a143bbb2156d53dcb475bf3467818a00","guid":"bfdfe7dc352907fc980b868725387e98cd6cb21ecbed9ef5249db5427b34f309"},{"fileReference":"bfdfe7dc352907fc980b868725387e989326c93489133be54ec1c5ce883f7f95","guid":"bfdfe7dc352907fc980b868725387e987086f18c8f04746040ad04d1495e5987"},{"fileReference":"bfdfe7dc352907fc980b868725387e98ab86e2bc0e96a3e47efb5fb4938bd05b","guid":"bfdfe7dc352907fc980b868725387e98deb061d220cf256291574c978433b9bd"},{"fileReference":"bfdfe7dc352907fc980b868725387e9899763c221a73e8f5860bb7297c2ecadf","guid":"bfdfe7dc352907fc980b868725387e98fb2ce2a54d4830268408f0da53955eb2"},{"fileReference":"bfdfe7dc352907fc980b868725387e984a55026799f54413f94c8d617bcee1bc","guid":"bfdfe7dc352907fc980b868725387e98dde0ca6b9e6a722f948c8fad38c22a95"},{"fileReference":"bfdfe7dc352907fc980b868725387e98f458e319d28f436491479bf5bc69bee4","guid":"bfdfe7dc352907fc980b868725387e982d460c5e8f6550cbc140db774b40752f"},{"fileReference":"bfdfe7dc352907fc980b868725387e9871360fc12faa4a6a46c3a5000eb78525","guid":"bfdfe7dc352907fc980b868725387e9827c4023f7130ab90afc00f1434340393"},{"fileReference":"bfdfe7dc352907fc980b868725387e980aa48715923cc7d3226a6daf26d515a7","guid":"bfdfe7dc352907fc980b868725387e981c750fb4de8e6f8f5bd31359a47e1e42"},{"fileReference":"bfdfe7dc352907fc980b868725387e98fea9ee4c790269b2b255fbda65d21cd5","guid":"bfdfe7dc352907fc980b868725387e980b4535c2eb5efe032efcd7ea90b8a666"},{"fileReference":"bfdfe7dc352907fc980b868725387e98aaded417427f59091e2272bec6813a42","guid":"bfdfe7dc352907fc980b868725387e980ba4c57bb4bd7bdcd21c04570f5a2e1f"},{"fileReference":"bfdfe7dc352907fc980b868725387e98f350348517c2cecb7d07354784c3bbc9","guid":"bfdfe7dc352907fc980b868725387e9811adc361209945de86a86b7fa1d7c3f8"},{"fileReference":"bfdfe7dc352907fc980b868725387e98a7ea5aa63b71073b08e7b415f74ed663","guid":"bfdfe7dc352907fc980b868725387e98492985e800f2423459efcfef10270692"},{"fileReference":"bfdfe7dc352907fc980b868725387e98247133e54555136c6e141473046f7e08","guid":"bfdfe7dc352907fc980b868725387e983b073fd21e782463067704b20c7aecb4"},{"fileReference":"bfdfe7dc352907fc980b868725387e9852f6cd5cdac1d0f84fae787b09c92c74","guid":"bfdfe7dc352907fc980b868725387e988c218a129ee9337c6f4c9fc9c31651bf"},{"fileReference":"bfdfe7dc352907fc980b868725387e98683dc56e238d9ccd0a2cd3b2d7b2e363","guid":"bfdfe7dc352907fc980b868725387e98a48f6a439944fbae7cbacffb33bb0a8f"},{"fileReference":"bfdfe7dc352907fc980b868725387e9845f0c14bbe37bdcf2d4d3c5941ee46f5","guid":"bfdfe7dc352907fc980b868725387e98d607d60302022bf876d39aa1bd69cdf0"},{"fileReference":"bfdfe7dc352907fc980b868725387e982672050c2030427d057debb1ea0c3df9","guid":"bfdfe7dc352907fc980b868725387e9849687d7ad52891093076ae6016a26da4"},{"fileReference":"bfdfe7dc352907fc980b868725387e98f474de454e032ecc10c533fc7fe161f0","guid":"bfdfe7dc352907fc980b868725387e982151f9e4c972b8a27c807903709a5cdd"},{"fileReference":"bfdfe7dc352907fc980b868725387e98a67747b5a77b0d264643563f3e216e7c","guid":"bfdfe7dc352907fc980b868725387e981e9f0720b7996cbbc5d1fed40f0912af"},{"fileReference":"bfdfe7dc352907fc980b868725387e986721d9a64a6948ad719ee0ca618d0d98","guid":"bfdfe7dc352907fc980b868725387e98daf88f1e9c8b88debe0db8a5a7ca7fc3"},{"fileReference":"bfdfe7dc352907fc980b868725387e98917e258ffc6052c99dbd63f347526ff6","guid":"bfdfe7dc352907fc980b868725387e98af9dace882a7c96a865bcef174d45792"},{"fileReference":"bfdfe7dc352907fc980b868725387e9814511951012b4191c2433d5a379eb9a1","guid":"bfdfe7dc352907fc980b868725387e9802f8dba89062e5de6c4c276fd7aa8b36"},{"fileReference":"bfdfe7dc352907fc980b868725387e987f3b32d8f963147c42e467390e392744","guid":"bfdfe7dc352907fc980b868725387e98b89ee2bf44b66263aa19e3fe8e7ae16a"}],"guid":"bfdfe7dc352907fc980b868725387e98311b3e21fcbc0f104d22472972191841","type":"com.apple.buildphase.resources"}],"buildRules":[],"dependencies":[],"guid":"bfdfe7dc352907fc980b868725387e986dbfa2df59ddcae0f992dedaee8f3553","name":"TOCropViewController-TOCropViewControllerBundle","productReference":{"guid":"bfdfe7dc352907fc980b868725387e980e69b2358d36eb6c2616a1dbbe45f585","name":"TOCropViewControllerBundle.bundle","type":"product"},"productTypeIdentifier":"com.apple.product-type.bundle","provisioningSourceData":[{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Debug","provisioningStyle":0},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Profile","provisioningStyle":0},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Release","provisioningStyle":0}],"type":"standard"} |
| 1 | -{"buildConfigurations":[{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e9872c3a102c2f55c6f0eee5833107fac63","buildSettings":{"CODE_SIGNING_ALLOWED":"NO","CODE_SIGNING_IDENTITY":"-","CODE_SIGNING_REQUIRED":"NO","CONFIGURATION_BUILD_DIR":"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/sqflite_darwin","EXPANDED_CODE_SIGN_IDENTITY":"-","IBSC_MODULE":"sqflite_darwin","INFOPLIST_FILE":"Target Support Files/sqflite_darwin/ResourceBundle-sqflite_darwin_privacy-sqflite_darwin-Info.plist","IPHONEOS_DEPLOYMENT_TARGET":"12.0","ONLY_ACTIVE_ARCH":"NO","PRODUCT_NAME":"sqflite_darwin_privacy","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","WRAPPER_EXTENSION":"bundle"},"guid":"bfdfe7dc352907fc980b868725387e98c19358e0d8fc0ff82ae574564065ed6b","name":"Debug"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98d1a455bf7417c3aeb645db18ef108a94","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CODE_SIGNING_ALLOWED":"NO","CODE_SIGNING_IDENTITY":"-","CODE_SIGNING_REQUIRED":"NO","CONFIGURATION_BUILD_DIR":"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/sqflite_darwin","EXPANDED_CODE_SIGN_IDENTITY":"-","IBSC_MODULE":"sqflite_darwin","INFOPLIST_FILE":"Target Support Files/sqflite_darwin/ResourceBundle-sqflite_darwin_privacy-sqflite_darwin-Info.plist","IPHONEOS_DEPLOYMENT_TARGET":"12.0","PRODUCT_NAME":"sqflite_darwin_privacy","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","WRAPPER_EXTENSION":"bundle"},"guid":"bfdfe7dc352907fc980b868725387e98afb87e3af52050c61b6e42f9c8a3883b","name":"Profile"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98d1a455bf7417c3aeb645db18ef108a94","buildSettings":{"CODE_SIGNING_ALLOWED":"NO","CODE_SIGNING_IDENTITY":"-","CODE_SIGNING_REQUIRED":"NO","CONFIGURATION_BUILD_DIR":"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/sqflite_darwin","EXPANDED_CODE_SIGN_IDENTITY":"-","IBSC_MODULE":"sqflite_darwin","INFOPLIST_FILE":"Target Support Files/sqflite_darwin/ResourceBundle-sqflite_darwin_privacy-sqflite_darwin-Info.plist","IPHONEOS_DEPLOYMENT_TARGET":"12.0","PRODUCT_NAME":"sqflite_darwin_privacy","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","WRAPPER_EXTENSION":"bundle"},"guid":"bfdfe7dc352907fc980b868725387e98e4f2c507ffb311a50a95aa349d7e88ed","name":"Release"}],"buildPhases":[{"buildFiles":[],"guid":"bfdfe7dc352907fc980b868725387e98ac7d823da02db5d40c302ec94ea301a3","type":"com.apple.buildphase.sources"},{"buildFiles":[],"guid":"bfdfe7dc352907fc980b868725387e980633102ccc15b88a476e0ed7ba1ff323","type":"com.apple.buildphase.frameworks"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e98d815319f529deb57d9d10e32525da1ef","guid":"bfdfe7dc352907fc980b868725387e988dcb3837b0f66cc15fcbb54ada836554"}],"guid":"bfdfe7dc352907fc980b868725387e98451cbac7e109149bf793647b9c462224","type":"com.apple.buildphase.resources"}],"buildRules":[],"dependencies":[],"guid":"bfdfe7dc352907fc980b868725387e9883134bb5f399cb37a1eb075d4fea30d8","name":"sqflite_darwin-sqflite_darwin_privacy","productReference":{"guid":"bfdfe7dc352907fc980b868725387e9849c1d4b1200fcbf6f387f94121c7d0bf","name":"sqflite_darwin_privacy.bundle","type":"product"},"productTypeIdentifier":"com.apple.product-type.bundle","provisioningSourceData":[{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Debug","provisioningStyle":0},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Profile","provisioningStyle":0},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Release","provisioningStyle":0}],"type":"standard"} |
| 1 | -{"buildConfigurations":[{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98493610a1174f3f4f738833de56d1328f","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","GCC_PREFIX_HEADER":"Target Support Files/webview_flutter_wkwebview/webview_flutter_wkwebview-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/webview_flutter_wkwebview/webview_flutter_wkwebview-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","IPHONEOS_DEPLOYMENT_TARGET":"12.0","MODULEMAP_FILE":"Target Support Files/webview_flutter_wkwebview/webview_flutter_wkwebview.modulemap","ONLY_ACTIVE_ARCH":"NO","OTHER_LDFLAGS":"$(inherited) -framework Flutter","PRODUCT_MODULE_NAME":"webview_flutter_wkwebview","PRODUCT_NAME":"webview_flutter_wkwebview","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e98d6da72224a0002e2c8d4ad6a4140a101","name":"Debug"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e986361f96ad5d0d9cdb22ee6aa8a152702","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","GCC_PREFIX_HEADER":"Target Support Files/webview_flutter_wkwebview/webview_flutter_wkwebview-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/webview_flutter_wkwebview/webview_flutter_wkwebview-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","IPHONEOS_DEPLOYMENT_TARGET":"12.0","MODULEMAP_FILE":"Target Support Files/webview_flutter_wkwebview/webview_flutter_wkwebview.modulemap","OTHER_LDFLAGS":"$(inherited) -framework Flutter","PRODUCT_MODULE_NAME":"webview_flutter_wkwebview","PRODUCT_NAME":"webview_flutter_wkwebview","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e984cab3693a69eb1c553c791758183685f","name":"Profile"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e986361f96ad5d0d9cdb22ee6aa8a152702","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","GCC_PREFIX_HEADER":"Target Support Files/webview_flutter_wkwebview/webview_flutter_wkwebview-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/webview_flutter_wkwebview/webview_flutter_wkwebview-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","IPHONEOS_DEPLOYMENT_TARGET":"12.0","MODULEMAP_FILE":"Target Support Files/webview_flutter_wkwebview/webview_flutter_wkwebview.modulemap","OTHER_LDFLAGS":"$(inherited) -framework Flutter","PRODUCT_MODULE_NAME":"webview_flutter_wkwebview","PRODUCT_NAME":"webview_flutter_wkwebview","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e98f480631d57f584ae82569893436b3d19","name":"Release"}],"buildPhases":[{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e98e100b69887046077a7bf8a9cf060b929","guid":"bfdfe7dc352907fc980b868725387e98faa2d77c0ef7eb87f7f8421e51c1b40a","headerVisibility":"public"}],"guid":"bfdfe7dc352907fc980b868725387e98a1e3dac75ef5081293a3cce12f493338","type":"com.apple.buildphase.headers"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e986ff29dbf77026e2fd214aacc48415f97","guid":"bfdfe7dc352907fc980b868725387e981de55d3cb88db838f3f39ff1797f33ce"},{"fileReference":"bfdfe7dc352907fc980b868725387e9820d9fff640378ae11fbdb2bba644e272","guid":"bfdfe7dc352907fc980b868725387e980d29368ece429919c8416c926d20a1b2"},{"fileReference":"bfdfe7dc352907fc980b868725387e98974b7125c3956adff996755a1b114006","guid":"bfdfe7dc352907fc980b868725387e98a435f50ff0ee12aa4ee277e694f2d126"},{"fileReference":"bfdfe7dc352907fc980b868725387e98fba6a1bdafdc072d807182e50958ac26","guid":"bfdfe7dc352907fc980b868725387e9818698cd69a8dd984343dd8261e191bfe"},{"fileReference":"bfdfe7dc352907fc980b868725387e9807fa3ed9a1c3d073fb8ed93058a7d538","guid":"bfdfe7dc352907fc980b868725387e987b439d64aac2afa2aed5d37bc7b53769"},{"fileReference":"bfdfe7dc352907fc980b868725387e9824e336744c0d7621c05a0223e51f3f66","guid":"bfdfe7dc352907fc980b868725387e9818e2a45f7d284b0dd902ab3482fc9edb"},{"fileReference":"bfdfe7dc352907fc980b868725387e9857e538de55d68f7ce78f685c1cd73410","guid":"bfdfe7dc352907fc980b868725387e98ffd34fa8ce89c4ce6d50467fae983432"},{"fileReference":"bfdfe7dc352907fc980b868725387e982da8ffef86ceaf65ed1f58ad933d36d8","guid":"bfdfe7dc352907fc980b868725387e98292490ce28c17daff90e0e782d73f784"},{"fileReference":"bfdfe7dc352907fc980b868725387e98ce9ba0fba9b5e90953d30114104d18bc","guid":"bfdfe7dc352907fc980b868725387e9834f73e42a69fc8b030848f28d070a368"},{"fileReference":"bfdfe7dc352907fc980b868725387e98a310832039952a0739ca7694f7ad758e","guid":"bfdfe7dc352907fc980b868725387e9898302af153ac1de4bd9428fca3bb8787"},{"fileReference":"bfdfe7dc352907fc980b868725387e9891936b22d4461ddb3e4165f1a75492dd","guid":"bfdfe7dc352907fc980b868725387e98ed9c33c1bf4f8fcd55bba266c67d062a"},{"fileReference":"bfdfe7dc352907fc980b868725387e9816b4b9d8465e3dd15680823043c778bb","guid":"bfdfe7dc352907fc980b868725387e98464a60c69b30cf4487e30f2e39b99e0a"},{"fileReference":"bfdfe7dc352907fc980b868725387e98ce6725437b5f43c0bd6ef1ccca2be082","guid":"bfdfe7dc352907fc980b868725387e984e94890557c10720442eefca0758fb7c"},{"fileReference":"bfdfe7dc352907fc980b868725387e986957040d9dced8053bb6e8f373a49a06","guid":"bfdfe7dc352907fc980b868725387e9801dbd92bfe45922f943df863b92756e6"},{"fileReference":"bfdfe7dc352907fc980b868725387e98a64df73b74f54e7dabd523cef6eea263","guid":"bfdfe7dc352907fc980b868725387e98695f5b329bfc5e6e8a12c3ef049fd84c"},{"fileReference":"bfdfe7dc352907fc980b868725387e985543c3a955f9d511d6e7251e7b4f383d","guid":"bfdfe7dc352907fc980b868725387e9872af5e523f4a9b056594a6e3963880d9"},{"fileReference":"bfdfe7dc352907fc980b868725387e98792b7a5a9cbe04e9c7b7d002ea638e83","guid":"bfdfe7dc352907fc980b868725387e98cabaf14c8327f9e739a1581549abcfba"},{"fileReference":"bfdfe7dc352907fc980b868725387e98e581dc86e636a128fcc7362ffedfe3b7","guid":"bfdfe7dc352907fc980b868725387e989b8f377ba0e2936c53f2b68c58a2468f"},{"fileReference":"bfdfe7dc352907fc980b868725387e9858cff6fc49e9b7824e719efa2a279eb8","guid":"bfdfe7dc352907fc980b868725387e98756f0f8bc30293af870fd37ef4e582e1"},{"fileReference":"bfdfe7dc352907fc980b868725387e980c66c03c8dc7e7282d701e3891d179cb","guid":"bfdfe7dc352907fc980b868725387e98a495bec27416fe0ef6d4c382d92bd37e"},{"fileReference":"bfdfe7dc352907fc980b868725387e98d93aa4a0ffddd857d5b5afe3f1789e0e","guid":"bfdfe7dc352907fc980b868725387e98dbe4cb3179877b97fd1cd1b9c7b0d657"},{"fileReference":"bfdfe7dc352907fc980b868725387e98755928d01ec4e183f6c6ad5dd1695876","guid":"bfdfe7dc352907fc980b868725387e98766ed63c43677a312cd576b6ecfa968c"},{"fileReference":"bfdfe7dc352907fc980b868725387e98efc85ac7c715782d5e65417979a219b9","guid":"bfdfe7dc352907fc980b868725387e9848489c7f67c84ff61cfb0ea1ec45d107"},{"fileReference":"bfdfe7dc352907fc980b868725387e988bef82569b24b5f6c612471073c56493","guid":"bfdfe7dc352907fc980b868725387e987259ad0ccd853df9dbdf20df62b01bf2"},{"fileReference":"bfdfe7dc352907fc980b868725387e98e98acb700f264a4d35f96d3e3a8e7192","guid":"bfdfe7dc352907fc980b868725387e9891cdd6b5f47999868b621c0cac86717d"},{"fileReference":"bfdfe7dc352907fc980b868725387e986bcadc9db0e23f509801ea7c18f17d3e","guid":"bfdfe7dc352907fc980b868725387e98bc9ffc416d43c25db0eba7374081cc24"},{"fileReference":"bfdfe7dc352907fc980b868725387e98272adc89cc3be2f5bac7bc2bd91672ed","guid":"bfdfe7dc352907fc980b868725387e989f67bad303f2c7a18d6663b9004e7f7f"},{"fileReference":"bfdfe7dc352907fc980b868725387e98dc6cb65d5dd165e65b7fbfcc7dc4421e","guid":"bfdfe7dc352907fc980b868725387e9862ed648bd55286684f95fcc2fdaa477b"},{"fileReference":"bfdfe7dc352907fc980b868725387e9833a097b5db2a7b844364a9486bb9616f","guid":"bfdfe7dc352907fc980b868725387e982552ba38ca9f397d8f80abfd80676bc3"},{"fileReference":"bfdfe7dc352907fc980b868725387e9811087db04d1b6ae8e562b162a7bde163","guid":"bfdfe7dc352907fc980b868725387e98d195323b373d63793cdede578ba29670"},{"fileReference":"bfdfe7dc352907fc980b868725387e987b63bd86e178dd1a4cd5fdf23764582d","guid":"bfdfe7dc352907fc980b868725387e989f08d7c9104548f24fc29052371ddadd"},{"fileReference":"bfdfe7dc352907fc980b868725387e98369effa6d8c6f49ab6a964ee2e41f64e","guid":"bfdfe7dc352907fc980b868725387e987ec55db088ddc2cfce45820bbd5664e8"},{"fileReference":"bfdfe7dc352907fc980b868725387e980a7f96f6c054a6b00bbe8edd3e5e02dc","guid":"bfdfe7dc352907fc980b868725387e9834fcf0d5ba9575313556f54273579df8"},{"fileReference":"bfdfe7dc352907fc980b868725387e9816892ac189479c50552d8df59101d469","guid":"bfdfe7dc352907fc980b868725387e980040a96cdcfcca3605783952ba372b88"},{"fileReference":"bfdfe7dc352907fc980b868725387e98d35f2da81360644fe00f5e7f5c5945d2","guid":"bfdfe7dc352907fc980b868725387e98447fdb1e6faad86182194fdfd9dfa3e4"},{"fileReference":"bfdfe7dc352907fc980b868725387e98176f3e1e76204f06691b39d943dccd53","guid":"bfdfe7dc352907fc980b868725387e982a39c7e1197817396058f11573213f14"},{"fileReference":"bfdfe7dc352907fc980b868725387e981ce32df686de5ed232b0c0ee3fcd4c2f","guid":"bfdfe7dc352907fc980b868725387e988fbe3b540434ba51c47bff24442a163e"},{"fileReference":"bfdfe7dc352907fc980b868725387e98e318650c8871ff158cbd37f8183721bc","guid":"bfdfe7dc352907fc980b868725387e98930b3ec41868a6d90b18606db9285027"},{"fileReference":"bfdfe7dc352907fc980b868725387e98386ba49f024aea24034bf28582dbecf4","guid":"bfdfe7dc352907fc980b868725387e98d259083229b4eed28de4a364a94222ca"},{"fileReference":"bfdfe7dc352907fc980b868725387e98d6c137c1d8c257e5b352ba97472bf7bf","guid":"bfdfe7dc352907fc980b868725387e981b32f3680c2b08fba50eeb04efa7482a"},{"fileReference":"bfdfe7dc352907fc980b868725387e98cb546a8f7bdc2047fbe4a99c1563566d","guid":"bfdfe7dc352907fc980b868725387e98eebbce98b73db8698586b64728bd46aa"},{"fileReference":"bfdfe7dc352907fc980b868725387e983a1098d9b7174aa713f6eb4dab0d9ad3","guid":"bfdfe7dc352907fc980b868725387e981fb47af3fa29c91f647f1ad2f650ec8d"},{"fileReference":"bfdfe7dc352907fc980b868725387e989c1cb5357bcb6e5e3a472f4e95d10dbd","guid":"bfdfe7dc352907fc980b868725387e98c65d236fa956eb88c1809b17ad57212b"}],"guid":"bfdfe7dc352907fc980b868725387e980055fbf3a11bc15129d28ebbe5cc1480","type":"com.apple.buildphase.sources"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e9867aa7825f197b5c58a57320b4ccdc64e","guid":"bfdfe7dc352907fc980b868725387e987ead9e15fc09a570554fa963eed1137d"}],"guid":"bfdfe7dc352907fc980b868725387e98da9cd099e7190758cfc42156c0f4e548","type":"com.apple.buildphase.frameworks"},{"buildFiles":[{"guid":"bfdfe7dc352907fc980b868725387e98b8defae1b39d33d1c1b6a7ec3e8cf51c","targetReference":"bfdfe7dc352907fc980b868725387e987c93e943aa0a38b5f6684beaf6b4a3a1"}],"guid":"bfdfe7dc352907fc980b868725387e987fd8694c1d36b88c99f78feb0d04dd25","type":"com.apple.buildphase.resources"}],"buildRules":[],"dependencies":[{"guid":"bfdfe7dc352907fc980b868725387e989da425bb6d6d5d8dbb95e4afffb82217","name":"Flutter"},{"guid":"bfdfe7dc352907fc980b868725387e987c93e943aa0a38b5f6684beaf6b4a3a1","name":"webview_flutter_wkwebview-webview_flutter_wkwebview_privacy"}],"guid":"bfdfe7dc352907fc980b868725387e988efdc4dd0ac29b43123295eca853f4ed","name":"webview_flutter_wkwebview","predominantSourceCodeLanguage":"Xcode.SourceCodeLanguage.Swift","productReference":{"guid":"bfdfe7dc352907fc980b868725387e980823710353e0487822d6da09bf8d6254","name":"webview_flutter_wkwebview.framework","type":"product"},"productTypeIdentifier":"com.apple.product-type.framework","provisioningSourceData":[{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Debug","provisioningStyle":1},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Profile","provisioningStyle":1},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Release","provisioningStyle":1}],"type":"standard"} |
| 1 | -{"buildConfigurations":[{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98890adb2b2b98fc70f20de16996216002","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","GCC_PREFIX_HEADER":"Target Support Files/image_picker_ios/image_picker_ios-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/image_picker_ios/image_picker_ios-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","IPHONEOS_DEPLOYMENT_TARGET":"12.0","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks @loader_path/Frameworks","MODULEMAP_FILE":"Target Support Files/image_picker_ios/image_picker_ios.modulemap","ONLY_ACTIVE_ARCH":"NO","OTHER_LDFLAGS":"$(inherited) -framework Flutter","PRODUCT_MODULE_NAME":"image_picker_ios","PRODUCT_NAME":"image_picker_ios","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e9864dece58fe8898e7f0b46391fe2d091f","name":"Debug"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98221c7220f1a43556e154d0e84bf28676","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","GCC_PREFIX_HEADER":"Target Support Files/image_picker_ios/image_picker_ios-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/image_picker_ios/image_picker_ios-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","IPHONEOS_DEPLOYMENT_TARGET":"12.0","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks @loader_path/Frameworks","MODULEMAP_FILE":"Target Support Files/image_picker_ios/image_picker_ios.modulemap","OTHER_LDFLAGS":"$(inherited) -framework Flutter","PRODUCT_MODULE_NAME":"image_picker_ios","PRODUCT_NAME":"image_picker_ios","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e981b08ec94c515cc2b895c3ae9b4c03834","name":"Profile"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98221c7220f1a43556e154d0e84bf28676","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","GCC_PREFIX_HEADER":"Target Support Files/image_picker_ios/image_picker_ios-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/image_picker_ios/image_picker_ios-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","IPHONEOS_DEPLOYMENT_TARGET":"12.0","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks @loader_path/Frameworks","MODULEMAP_FILE":"Target Support Files/image_picker_ios/image_picker_ios.modulemap","OTHER_LDFLAGS":"$(inherited) -framework Flutter","PRODUCT_MODULE_NAME":"image_picker_ios","PRODUCT_NAME":"image_picker_ios","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e982b03ce4519745554a82e5cffe58f25e2","name":"Release"}],"buildPhases":[{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e9887cb71ec0a197ed2946aed6024d1dad1","guid":"bfdfe7dc352907fc980b868725387e98036d5aec29b54b7dd40e90098fa1a783","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e9892d65c42b66fdd6d568c50d0e873ca05","guid":"bfdfe7dc352907fc980b868725387e9884122a5495886f08a28766f6766ed12d","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e988061950042bff325e12ce6e07163bdf3","guid":"bfdfe7dc352907fc980b868725387e9878792dd23cc884d01a42ee927a76af76","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e98314704ab4b99c3ad78a115a96a37a641","guid":"bfdfe7dc352907fc980b868725387e98ac4ce8757c9fefe5d0b2b79262ebbaeb","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e9874a54da90eac84404a6d42381d295f8d","guid":"bfdfe7dc352907fc980b868725387e9816643affd079e3f5d29a95b3d4ac8518","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e984ae39a9f5ce5801d979fc71dc42040a4","guid":"bfdfe7dc352907fc980b868725387e9833ff3f2578502bb2f88e252ec33d5fb3","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e986d4a8dba098c26b0e6cb26abd8e5b82f","guid":"bfdfe7dc352907fc980b868725387e9822ff4bab74a9180dcd559e6228895c11","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e98001a1216fcba8a5077639ed936eedf22","guid":"bfdfe7dc352907fc980b868725387e984f06794600a72eff5e8b6bd1bfb1381c","headerVisibility":"public"}],"guid":"bfdfe7dc352907fc980b868725387e987c543487347cac13b6462fae62598e7f","type":"com.apple.buildphase.headers"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e981f6c3202c5e8ea43fabe23d4ac249aa8","guid":"bfdfe7dc352907fc980b868725387e9839e6fa4fb580d35845b455f3349208ed"},{"fileReference":"bfdfe7dc352907fc980b868725387e98d3353819e36d78fafa3c90288aa829a8","guid":"bfdfe7dc352907fc980b868725387e987f4d20a804f6cc54ba49f23a03bba3ff"},{"fileReference":"bfdfe7dc352907fc980b868725387e9879f3ff725c0c37a2851096c74b7aa01c","guid":"bfdfe7dc352907fc980b868725387e986de844193f467a67763cf085cbf548af"},{"fileReference":"bfdfe7dc352907fc980b868725387e98521a0cbc97fe403ba6dec7b4711eca98","guid":"bfdfe7dc352907fc980b868725387e980a470861055f808b55678e192587ba69"},{"fileReference":"bfdfe7dc352907fc980b868725387e98ef7fab25f1a8201b14fddc3cd791dc85","guid":"bfdfe7dc352907fc980b868725387e98fe1bc38033b4a880e7f645b78bb9d3b6"},{"fileReference":"bfdfe7dc352907fc980b868725387e98e866ceff35444fd643a35a36698bec12","guid":"bfdfe7dc352907fc980b868725387e98e0ab939fbafbd88342244f2b4a6d7f2e"},{"fileReference":"bfdfe7dc352907fc980b868725387e987c91f08e2ecb120f461660eb187051f2","guid":"bfdfe7dc352907fc980b868725387e986537e55fb8c991ac3f2f365064e71ccf"}],"guid":"bfdfe7dc352907fc980b868725387e98ce67561b68c83c2e24888c94da999914","type":"com.apple.buildphase.sources"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e9867aa7825f197b5c58a57320b4ccdc64e","guid":"bfdfe7dc352907fc980b868725387e98cf1fddec899afc6c9825c5eb5ec44493"}],"guid":"bfdfe7dc352907fc980b868725387e98434353ef3b38c3699582ee30b73fe6a8","type":"com.apple.buildphase.frameworks"},{"buildFiles":[{"guid":"bfdfe7dc352907fc980b868725387e9872ef11792920b3966776ea469c5092df","targetReference":"bfdfe7dc352907fc980b868725387e98082dc85da1fc941e5234c7cc1f11b27d"}],"guid":"bfdfe7dc352907fc980b868725387e9811e9e8a5f23273fd9234f4740a75ccb9","type":"com.apple.buildphase.resources"}],"buildRules":[],"dependencies":[{"guid":"bfdfe7dc352907fc980b868725387e989da425bb6d6d5d8dbb95e4afffb82217","name":"Flutter"},{"guid":"bfdfe7dc352907fc980b868725387e98082dc85da1fc941e5234c7cc1f11b27d","name":"image_picker_ios-image_picker_ios_privacy"}],"guid":"bfdfe7dc352907fc980b868725387e981f000f066404b97b12e9c4ca84d38d0f","name":"image_picker_ios","predominantSourceCodeLanguage":"Xcode.SourceCodeLanguage.Objective-C-Plus-Plus","productReference":{"guid":"bfdfe7dc352907fc980b868725387e988e06e8c3685b7c12032d8059f412f4cb","name":"image_picker_ios.framework","type":"product"},"productTypeIdentifier":"com.apple.product-type.framework","provisioningSourceData":[{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Debug","provisioningStyle":1},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Profile","provisioningStyle":1},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Release","provisioningStyle":1}],"type":"standard"} |
| 1 | -{"buildConfigurations":[{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98a6093e67eab0c95af9b59197c88a1b6b","buildSettings":{"CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DEFINES_MODULE":"YES","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","GCC_PREFIX_HEADER":"Target Support Files/permission_handler_apple/permission_handler_apple-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/permission_handler_apple/permission_handler_apple-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks @loader_path/Frameworks","MACH_O_TYPE":"staticlib","MODULEMAP_FILE":"Target Support Files/permission_handler_apple/permission_handler_apple.modulemap","ONLY_ACTIVE_ARCH":"NO","OTHER_LDFLAGS":"$(inherited) -framework Flutter","PRODUCT_MODULE_NAME":"permission_handler_apple","PRODUCT_NAME":"permission_handler_apple","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e98519c0f1aabf34eacbf6f755b813f1496","name":"Debug"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e982824e3afc8d872da1d8489aeef65d25c","buildSettings":{"CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DEFINES_MODULE":"YES","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","GCC_PREFIX_HEADER":"Target Support Files/permission_handler_apple/permission_handler_apple-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/permission_handler_apple/permission_handler_apple-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks @loader_path/Frameworks","MACH_O_TYPE":"staticlib","MODULEMAP_FILE":"Target Support Files/permission_handler_apple/permission_handler_apple.modulemap","OTHER_LDFLAGS":"$(inherited) -framework Flutter","PRODUCT_MODULE_NAME":"permission_handler_apple","PRODUCT_NAME":"permission_handler_apple","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e98ec62137b0ee28ca4265944856877be27","name":"Profile"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e982824e3afc8d872da1d8489aeef65d25c","buildSettings":{"CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DEFINES_MODULE":"YES","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","GCC_PREFIX_HEADER":"Target Support Files/permission_handler_apple/permission_handler_apple-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/permission_handler_apple/permission_handler_apple-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks @loader_path/Frameworks","MACH_O_TYPE":"staticlib","MODULEMAP_FILE":"Target Support Files/permission_handler_apple/permission_handler_apple.modulemap","OTHER_LDFLAGS":"$(inherited) -framework Flutter","PRODUCT_MODULE_NAME":"permission_handler_apple","PRODUCT_NAME":"permission_handler_apple","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e9832ebf16d88dad2729e444c32094aa46a","name":"Release"}],"buildPhases":[{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e9884851084d3b02b53cd983fcffd818235","guid":"bfdfe7dc352907fc980b868725387e98a27a49b00be0ac981e5db9f07832563d","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e982e7f7319c09f94850a0723a216d70e04","guid":"bfdfe7dc352907fc980b868725387e989878394bd4334bc5b75f69e19db62bf2","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e9821ebb0fa53d0eb7ec2ab6f8ca9190624","guid":"bfdfe7dc352907fc980b868725387e98e1897e3d8aed17671192779f85b1c248","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e98b5b86aa005335961c358b4a960923948","guid":"bfdfe7dc352907fc980b868725387e981450c482f5c290824d437888a184ae8b","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e98cfa5ce0ae7f41afbcf0e57cd6de30680","guid":"bfdfe7dc352907fc980b868725387e98e4fc9878a7a9815c87ead588b617e13f","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e9822c393317084e36f2969d86ba20820cc","guid":"bfdfe7dc352907fc980b868725387e98931f24651cd163bcb560cc6bf0293fcb","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e98265b9bc576c6643489aa71c5ef563885","guid":"bfdfe7dc352907fc980b868725387e98d4cfb9e1cbc63fdd6505f85f9a29df9c","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e98fb31e35e6ff05f31942d41934fa09494","guid":"bfdfe7dc352907fc980b868725387e98603cbbc70b8dca9ffe9ae016055178e4","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e983e6c10cd55ac7b546e8a27a6d73df979","guid":"bfdfe7dc352907fc980b868725387e98de1a9153e9195626d2e18082bddd5583","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e986788e4d437e57069fdd7150780f6a1e9","guid":"bfdfe7dc352907fc980b868725387e984a1d400fa247fcd1999957a8682fc831","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e98cafca9abb3305da2511ec18f0b22ed2c","guid":"bfdfe7dc352907fc980b868725387e98d32709d3a2dbac2c57e7e408179d0866","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e9828dae6c5d961eab3e33766ec57e27821","guid":"bfdfe7dc352907fc980b868725387e9852b51035de7e7268120a09bd61060c70","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e985dfbcd518b00fd83791970f4f841e5e7","guid":"bfdfe7dc352907fc980b868725387e98dc02dbc796bf1a1c79ffc8c7bf3cea72","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e981329cadec5aeca914b5940565930d22a","guid":"bfdfe7dc352907fc980b868725387e9851124daa4e024f512c6435eeca4166ec","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e98447580bc8b8f4827c81d8360464b9e24","guid":"bfdfe7dc352907fc980b868725387e98d1ab4894e5f8a787a882b5c8c35da9b2","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e98c2f59fe53444d666df46ecacb137334b","guid":"bfdfe7dc352907fc980b868725387e980ef321af2f9f2c834e49de13f3d55bc9","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e986dd8eb61c9df9c78ba64a9f79cace01e","guid":"bfdfe7dc352907fc980b868725387e98f0d31a429c82f17b0991aeffa8fa048c","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e988e29d9b40690bda08dda415d92f70602","guid":"bfdfe7dc352907fc980b868725387e98a42a0c118dedfd60efdbb4d16492c7b8","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e98e2ab3143400e3260e6623fe39e99f7c0","guid":"bfdfe7dc352907fc980b868725387e98b3c686ca16e5bce7ed24eabc68c27a7a","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e98af3e4ebd26dd264bce4333eb0e99f67c","guid":"bfdfe7dc352907fc980b868725387e9828accb1b5b23f919db719dddafe64428","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e98a5999d69392be533721c9529b09cb914","guid":"bfdfe7dc352907fc980b868725387e981a70531bb3ca84e549cc6ba35f299fa1","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e9830ffd03b288e713d891e12ce5bfa5613","guid":"bfdfe7dc352907fc980b868725387e98d126da608ed212ace70c11a62a0c1637","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e98e3b68579772f0ba934ed8c3c170aa0ab","guid":"bfdfe7dc352907fc980b868725387e98482d9659cbec4dc229fbd14d4097dbb0","headerVisibility":"public"}],"guid":"bfdfe7dc352907fc980b868725387e986a03fa67e33d48dce94be2b8eb2259ec","type":"com.apple.buildphase.headers"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e9859b1324343dc055e161d6c6533ee5e14","guid":"bfdfe7dc352907fc980b868725387e989514a07acecd425069c301ac83629a34"},{"fileReference":"bfdfe7dc352907fc980b868725387e9842f3aba82f1bbbea45fa42860b997a9c","guid":"bfdfe7dc352907fc980b868725387e98c5aeecdf68b8bd150764901c66af09a9"},{"fileReference":"bfdfe7dc352907fc980b868725387e98620c548ad1ca45b7c591297d1eb7c747","guid":"bfdfe7dc352907fc980b868725387e98845832e3ddc9b9efa82804181189873f"},{"fileReference":"bfdfe7dc352907fc980b868725387e988137c4db801b52960f300f0a7f9e66e1","guid":"bfdfe7dc352907fc980b868725387e988422c8d41a531ab0e5feda22b2729450"},{"fileReference":"bfdfe7dc352907fc980b868725387e9818cfd9e6e71005df38ef34975cee4bb2","guid":"bfdfe7dc352907fc980b868725387e98032b5432a99c409a15c23484d253896f"},{"fileReference":"bfdfe7dc352907fc980b868725387e9873b0ea1c477453db2a27395853e19d20","guid":"bfdfe7dc352907fc980b868725387e988ec79e04b31e29a98b9058ffe01c7fe6"},{"fileReference":"bfdfe7dc352907fc980b868725387e98c2f696a6e8c1b05591d7b9da435e3493","guid":"bfdfe7dc352907fc980b868725387e980e70a7a6c573b1b7ccd3e1d5ab5d07e2"},{"fileReference":"bfdfe7dc352907fc980b868725387e9800e74c7905cd82d36f9960af8978f5aa","guid":"bfdfe7dc352907fc980b868725387e983e056fb9529e4b65a3f33b7e5569c272"},{"fileReference":"bfdfe7dc352907fc980b868725387e98de774a6e299c64442144c3fcc58fa595","guid":"bfdfe7dc352907fc980b868725387e980341c01be7632895d55fd1dac2b54ca7"},{"fileReference":"bfdfe7dc352907fc980b868725387e985b42d8660810a9d6b3aad933347d7e57","guid":"bfdfe7dc352907fc980b868725387e9844b9c045f1529acb68ed027e0a48b614"},{"fileReference":"bfdfe7dc352907fc980b868725387e98520970053fda1908c06476b52a119a95","guid":"bfdfe7dc352907fc980b868725387e98b1b6e64bf6811487ed4c6e1384077226"},{"fileReference":"bfdfe7dc352907fc980b868725387e985b14ce293cf32a01a773054612405a14","guid":"bfdfe7dc352907fc980b868725387e981f2b6f47d148085767ead35147c92b0a"},{"fileReference":"bfdfe7dc352907fc980b868725387e98697a7d246f5ecbb804645bdff1bd0889","guid":"bfdfe7dc352907fc980b868725387e981850b344567e5543096f0a83d9eac0b2"},{"fileReference":"bfdfe7dc352907fc980b868725387e98b232dd09cbb7ffdcc50b18242e2d54aa","guid":"bfdfe7dc352907fc980b868725387e98ff4558f6e06cbb7fbd15dd015ca18854"},{"fileReference":"bfdfe7dc352907fc980b868725387e981311af37bc86faf3e55d86273af1dd44","guid":"bfdfe7dc352907fc980b868725387e983caf79b54a68f27872c153b09b7289cb"},{"fileReference":"bfdfe7dc352907fc980b868725387e989fc0bac42f87fe89196550b76dd1d39a","guid":"bfdfe7dc352907fc980b868725387e983550715f5dc47c6e779aeb12290a7c48"},{"fileReference":"bfdfe7dc352907fc980b868725387e984e9f512a43dc9832164c7190e5793552","guid":"bfdfe7dc352907fc980b868725387e985800f051739a623bb35f7cc22cbdb0d1"},{"fileReference":"bfdfe7dc352907fc980b868725387e98f4a28458a09df40a41a85843a00b9764","guid":"bfdfe7dc352907fc980b868725387e98f2203b5d78f4ac52bb8f2618d0202e75"},{"fileReference":"bfdfe7dc352907fc980b868725387e988c71f7c3f79685b588b764a4722910f6","guid":"bfdfe7dc352907fc980b868725387e983616275298339a71f8ce5405edcae646"},{"fileReference":"bfdfe7dc352907fc980b868725387e98629dea7138da4a42b26f2cec5f668c3e","guid":"bfdfe7dc352907fc980b868725387e9859b2f7d951b31f73b36500eee5613569"},{"fileReference":"bfdfe7dc352907fc980b868725387e98572e0d97aec70a0d968a775514e32a66","guid":"bfdfe7dc352907fc980b868725387e9800fc7c6d98b1d598e7f0946391c24f5b"}],"guid":"bfdfe7dc352907fc980b868725387e98be6229230a4715433df2f8e74fbafc5b","type":"com.apple.buildphase.sources"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e9867aa7825f197b5c58a57320b4ccdc64e","guid":"bfdfe7dc352907fc980b868725387e980797c2152e50219ee4196549bb34f857"}],"guid":"bfdfe7dc352907fc980b868725387e984d290968aff9eafa4ed5b85c80a8c610","type":"com.apple.buildphase.frameworks"},{"buildFiles":[],"guid":"bfdfe7dc352907fc980b868725387e98fa0d11ed0b4e1a85c13d68e37d1547e0","type":"com.apple.buildphase.resources"}],"buildRules":[],"dependencies":[{"guid":"bfdfe7dc352907fc980b868725387e989da425bb6d6d5d8dbb95e4afffb82217","name":"Flutter"},{"guid":"bfdfe7dc352907fc980b868725387e9802f35ab680609a626ebd2ddd692a3822","name":"permission_handler_apple-permission_handler_apple_privacy"}],"guid":"bfdfe7dc352907fc980b868725387e98ef10255b706f98e1e88fae00855b0968","name":"permission_handler_apple","predominantSourceCodeLanguage":"Xcode.SourceCodeLanguage.Objective-C-Plus-Plus","productReference":{"guid":"bfdfe7dc352907fc980b868725387e98f8f53f8ba4165e76c7481b24262177ed","name":"permission_handler_apple.framework","type":"product"},"productTypeIdentifier":"com.apple.product-type.framework","provisioningSourceData":[{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Debug","provisioningStyle":1},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Profile","provisioningStyle":1},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Release","provisioningStyle":1}],"type":"standard"} |
| 1 | -{"buildConfigurations":[{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e9868b77f49da4a0e0608aac8b3f5ae3e8e","buildSettings":{"CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","GCC_PREFIX_HEADER":"Target Support Files/fluttertoast/fluttertoast-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/fluttertoast/fluttertoast-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks @loader_path/Frameworks","MODULEMAP_FILE":"Target Support Files/fluttertoast/fluttertoast.modulemap","ONLY_ACTIVE_ARCH":"NO","OTHER_LDFLAGS":"$(inherited) -framework Flutter","PRODUCT_MODULE_NAME":"fluttertoast","PRODUCT_NAME":"fluttertoast","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e980d95e38372e14dc164189e9e39c87ca3","name":"Debug"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e9852dc1371d317d5fc535ff1f306279566","buildSettings":{"CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","GCC_PREFIX_HEADER":"Target Support Files/fluttertoast/fluttertoast-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/fluttertoast/fluttertoast-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks @loader_path/Frameworks","MODULEMAP_FILE":"Target Support Files/fluttertoast/fluttertoast.modulemap","OTHER_LDFLAGS":"$(inherited) -framework Flutter","PRODUCT_MODULE_NAME":"fluttertoast","PRODUCT_NAME":"fluttertoast","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e980c7da66ee29e77cf7e3c5ef7b6bf0505","name":"Profile"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e9852dc1371d317d5fc535ff1f306279566","buildSettings":{"CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","GCC_PREFIX_HEADER":"Target Support Files/fluttertoast/fluttertoast-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/fluttertoast/fluttertoast-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks @loader_path/Frameworks","MODULEMAP_FILE":"Target Support Files/fluttertoast/fluttertoast.modulemap","OTHER_LDFLAGS":"$(inherited) -framework Flutter","PRODUCT_MODULE_NAME":"fluttertoast","PRODUCT_NAME":"fluttertoast","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e985d57597ee80c97031b19d4dd216a0459","name":"Release"}],"buildPhases":[{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e983b44f07e560c85607ecdbac390035690","guid":"bfdfe7dc352907fc980b868725387e9898803a5367f403dbe7d7f50e52e158e4","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e988ce5db321c5b1a0e8d45a2033460a820","guid":"bfdfe7dc352907fc980b868725387e983964f34a71d25e0ad408764637201730","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e9810650f30d25a1a650eb486810140f78d","guid":"bfdfe7dc352907fc980b868725387e98c8914a1d8083fd11e9c788dbae30cb4b","headerVisibility":"public"}],"guid":"bfdfe7dc352907fc980b868725387e98f9f50c0f44c9e9758a8d2da9a3e9486d","type":"com.apple.buildphase.headers"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e98000117cc7e8ad3178027b29f7f6baa74","guid":"bfdfe7dc352907fc980b868725387e98732bda2bf98a69eea7b638ba5caac9e6"},{"additionalCompilerOptions":"-DOS_OBJECT_USE_OBJC=0","fileReference":"bfdfe7dc352907fc980b868725387e987d8dc557a1cefbf1c0572194540f3c91","guid":"bfdfe7dc352907fc980b868725387e98c7baa8b56c358501b9b804d1da3607c1"},{"additionalCompilerOptions":"-DOS_OBJECT_USE_OBJC=0","fileReference":"bfdfe7dc352907fc980b868725387e98be100717715b68a43bd8a6ee4411da5c","guid":"bfdfe7dc352907fc980b868725387e98434a59f227df2853d124b2eb53dbd773"}],"guid":"bfdfe7dc352907fc980b868725387e98ff7ff0914d28d25b338598e51f35a22a","type":"com.apple.buildphase.sources"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e9867aa7825f197b5c58a57320b4ccdc64e","guid":"bfdfe7dc352907fc980b868725387e98fb70b41caaa71fca49a76d4a80225131"}],"guid":"bfdfe7dc352907fc980b868725387e9893a2f3917413e2f83f099b893b6202f2","type":"com.apple.buildphase.frameworks"},{"buildFiles":[{"guid":"bfdfe7dc352907fc980b868725387e982a8b2134dd93f2cb1d1331e935303944","targetReference":"bfdfe7dc352907fc980b868725387e985739272bce418ef50bd06c859612bad5"}],"guid":"bfdfe7dc352907fc980b868725387e98678ff57194ccd7dfda00aac2582b17b5","type":"com.apple.buildphase.resources"}],"buildRules":[],"dependencies":[{"guid":"bfdfe7dc352907fc980b868725387e989da425bb6d6d5d8dbb95e4afffb82217","name":"Flutter"},{"guid":"bfdfe7dc352907fc980b868725387e985739272bce418ef50bd06c859612bad5","name":"fluttertoast-fluttertoast_privacy"}],"guid":"bfdfe7dc352907fc980b868725387e98839a1650b1f10605b2db52456c9e6468","name":"fluttertoast","predominantSourceCodeLanguage":"Xcode.SourceCodeLanguage.Objective-C","productReference":{"guid":"bfdfe7dc352907fc980b868725387e98f09a1b962c84d31b7bec2ec6176b5c98","name":"fluttertoast.framework","type":"product"},"productTypeIdentifier":"com.apple.product-type.framework","provisioningSourceData":[{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Debug","provisioningStyle":1},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Profile","provisioningStyle":1},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Release","provisioningStyle":1}],"type":"standard"} |
| 1 | -{"buildConfigurations":[{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98bef0d6c2c15f5aafe14dc460dedf3fd4","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DEFINES_MODULE":"YES","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","GCC_PREFIX_HEADER":"Target Support Files/image_cropper/image_cropper-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/image_cropper/image_cropper-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks @loader_path/Frameworks","MODULEMAP_FILE":"Target Support Files/image_cropper/image_cropper.modulemap","ONLY_ACTIVE_ARCH":"NO","OTHER_LDFLAGS":"$(inherited) -framework Flutter","PRODUCT_MODULE_NAME":"image_cropper","PRODUCT_NAME":"image_cropper","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e98f7b93ecdbd3c8b9ea5616aa7b0d3fe18","name":"Debug"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e980fd9e2f1733e15caec480c108e2f6d49","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DEFINES_MODULE":"YES","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","GCC_PREFIX_HEADER":"Target Support Files/image_cropper/image_cropper-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/image_cropper/image_cropper-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks @loader_path/Frameworks","MODULEMAP_FILE":"Target Support Files/image_cropper/image_cropper.modulemap","OTHER_LDFLAGS":"$(inherited) -framework Flutter","PRODUCT_MODULE_NAME":"image_cropper","PRODUCT_NAME":"image_cropper","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e98f138ec1c7959dd17e955c99842ba6cd3","name":"Profile"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e980fd9e2f1733e15caec480c108e2f6d49","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DEFINES_MODULE":"YES","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","GCC_PREFIX_HEADER":"Target Support Files/image_cropper/image_cropper-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/image_cropper/image_cropper-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks @loader_path/Frameworks","MODULEMAP_FILE":"Target Support Files/image_cropper/image_cropper.modulemap","OTHER_LDFLAGS":"$(inherited) -framework Flutter","PRODUCT_MODULE_NAME":"image_cropper","PRODUCT_NAME":"image_cropper","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e9822ce90530c07c0eef19816e1846a2b47","name":"Release"}],"buildPhases":[{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e988def5567fb619d818c0e4033c07da2d0","guid":"bfdfe7dc352907fc980b868725387e983cd18188c90960b70b25f90c0640bc2e","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e9849e25a833cb0bed5dc8ebb5539d6cb74","guid":"bfdfe7dc352907fc980b868725387e9835ba3c68c353baaaaf8a192693f9fb9c","headerVisibility":"public"}],"guid":"bfdfe7dc352907fc980b868725387e9872651d2c8d8c194c061f4d51d19b24d0","type":"com.apple.buildphase.headers"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e98e9ae364119d4ded1be782c9c2027f874","guid":"bfdfe7dc352907fc980b868725387e98fac879e253c603d20d42aefd7eba7e0c"},{"fileReference":"bfdfe7dc352907fc980b868725387e98c3a32b479b99fa0be5118bc2e4f2256c","guid":"bfdfe7dc352907fc980b868725387e98a90eca3f4ef58020f17eff6ee8594ca3"}],"guid":"bfdfe7dc352907fc980b868725387e984a515ab06e514badd4270f0dcfa41726","type":"com.apple.buildphase.sources"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e9867aa7825f197b5c58a57320b4ccdc64e","guid":"bfdfe7dc352907fc980b868725387e983584573e884ecb062bea67dd4b38239e"}],"guid":"bfdfe7dc352907fc980b868725387e988219f27d7669f834e95ed895d1bd8d70","type":"com.apple.buildphase.frameworks"},{"buildFiles":[],"guid":"bfdfe7dc352907fc980b868725387e9811cdaaa9080a7ba3ac6ee0e4bf44298c","type":"com.apple.buildphase.resources"}],"buildRules":[],"dependencies":[{"guid":"bfdfe7dc352907fc980b868725387e989da425bb6d6d5d8dbb95e4afffb82217","name":"Flutter"},{"guid":"bfdfe7dc352907fc980b868725387e987a4af56e2729cecfad7b13d62a9a5fa4","name":"TOCropViewController"}],"guid":"bfdfe7dc352907fc980b868725387e98c7508c3a173f39338f076ef697b954c4","name":"image_cropper","predominantSourceCodeLanguage":"Xcode.SourceCodeLanguage.Objective-C","productReference":{"guid":"bfdfe7dc352907fc980b868725387e98af2ff58bf1e7988af36cadaf5bb3d25c","name":"image_cropper.framework","type":"product"},"productTypeIdentifier":"com.apple.product-type.framework","provisioningSourceData":[{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Debug","provisioningStyle":1},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Profile","provisioningStyle":1},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Release","provisioningStyle":1}],"type":"standard"} |
| 1 | -{"buildConfigurations":[{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98c9406d4edd5339dd20198a367c290d36","buildSettings":{"CODE_SIGNING_ALLOWED":"NO","CODE_SIGNING_IDENTITY":"-","CODE_SIGNING_REQUIRED":"NO","CONFIGURATION_BUILD_DIR":"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/path_provider_foundation","EXPANDED_CODE_SIGN_IDENTITY":"-","IBSC_MODULE":"path_provider_foundation","INFOPLIST_FILE":"Target Support Files/path_provider_foundation/ResourceBundle-path_provider_foundation_privacy-path_provider_foundation-Info.plist","IPHONEOS_DEPLOYMENT_TARGET":"12.0","ONLY_ACTIVE_ARCH":"NO","PRODUCT_NAME":"path_provider_foundation_privacy","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","WRAPPER_EXTENSION":"bundle"},"guid":"bfdfe7dc352907fc980b868725387e9815d476a49b818ac83bb7d89c034bc7b7","name":"Debug"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e988fe63e2e9f76f78e56562689e1d41ffe","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CODE_SIGNING_ALLOWED":"NO","CODE_SIGNING_IDENTITY":"-","CODE_SIGNING_REQUIRED":"NO","CONFIGURATION_BUILD_DIR":"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/path_provider_foundation","EXPANDED_CODE_SIGN_IDENTITY":"-","IBSC_MODULE":"path_provider_foundation","INFOPLIST_FILE":"Target Support Files/path_provider_foundation/ResourceBundle-path_provider_foundation_privacy-path_provider_foundation-Info.plist","IPHONEOS_DEPLOYMENT_TARGET":"12.0","PRODUCT_NAME":"path_provider_foundation_privacy","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","WRAPPER_EXTENSION":"bundle"},"guid":"bfdfe7dc352907fc980b868725387e981373391d97c6cdcaed1361cf95934c31","name":"Profile"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e988fe63e2e9f76f78e56562689e1d41ffe","buildSettings":{"CODE_SIGNING_ALLOWED":"NO","CODE_SIGNING_IDENTITY":"-","CODE_SIGNING_REQUIRED":"NO","CONFIGURATION_BUILD_DIR":"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/path_provider_foundation","EXPANDED_CODE_SIGN_IDENTITY":"-","IBSC_MODULE":"path_provider_foundation","INFOPLIST_FILE":"Target Support Files/path_provider_foundation/ResourceBundle-path_provider_foundation_privacy-path_provider_foundation-Info.plist","IPHONEOS_DEPLOYMENT_TARGET":"12.0","PRODUCT_NAME":"path_provider_foundation_privacy","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","WRAPPER_EXTENSION":"bundle"},"guid":"bfdfe7dc352907fc980b868725387e98652057b55c4e6ac5e390712a0bc77086","name":"Release"}],"buildPhases":[{"buildFiles":[],"guid":"bfdfe7dc352907fc980b868725387e984823a2e0fba92681cf7792666b9bb36b","type":"com.apple.buildphase.sources"},{"buildFiles":[],"guid":"bfdfe7dc352907fc980b868725387e980148385ae73e13be88316704eda5e242","type":"com.apple.buildphase.frameworks"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e9889c48add7124ee091ff93ea9433486b7","guid":"bfdfe7dc352907fc980b868725387e98a792d5c00bccb4df88a188da3006a680"}],"guid":"bfdfe7dc352907fc980b868725387e98ebb3c9b4117be3b1e5bd198bd9957243","type":"com.apple.buildphase.resources"}],"buildRules":[],"dependencies":[],"guid":"bfdfe7dc352907fc980b868725387e987ea64ee8d53085bf9edd1a57aaf8cbb5","name":"path_provider_foundation-path_provider_foundation_privacy","productReference":{"guid":"bfdfe7dc352907fc980b868725387e986e649604f74c414a7c2dbe5ef4cc4e75","name":"path_provider_foundation_privacy.bundle","type":"product"},"productTypeIdentifier":"com.apple.product-type.bundle","provisioningSourceData":[{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Debug","provisioningStyle":0},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Profile","provisioningStyle":0},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Release","provisioningStyle":0}],"type":"standard"} |
| 1 | -{"buildConfigurations":[{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e9850cc7fc2d23136fb4fac488d6c47df20","buildSettings":{"ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES":"NO","CLANG_ENABLE_OBJC_WEAK":"NO","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DEFINES_MODULE":"YES","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","INFOPLIST_FILE":"Target Support Files/Pods-Runner/Pods-Runner-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","IPHONEOS_DEPLOYMENT_TARGET":"13.0","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks @loader_path/Frameworks","MACH_O_TYPE":"staticlib","MODULEMAP_FILE":"Target Support Files/Pods-Runner/Pods-Runner.modulemap","ONLY_ACTIVE_ARCH":"NO","OTHER_LDFLAGS":"$(inherited) -framework Flutter","OTHER_LIBTOOLFLAGS":"","PODS_ROOT":"$(SRCROOT)","PRODUCT_BUNDLE_IDENTIFIER":"org.cocoapods.${PRODUCT_NAME:rfc1034identifier}","PRODUCT_NAME":"$(TARGET_NAME:c99extidentifier)","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e98a2829366523dfaca9b75e5f7a72f6254","name":"Debug"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98dc1a21852c7a085c7dd8f65cf0fa9907","buildSettings":{"ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES":"NO","CLANG_ENABLE_OBJC_WEAK":"NO","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DEFINES_MODULE":"YES","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","INFOPLIST_FILE":"Target Support Files/Pods-Runner/Pods-Runner-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","IPHONEOS_DEPLOYMENT_TARGET":"13.0","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks @loader_path/Frameworks","MACH_O_TYPE":"staticlib","MODULEMAP_FILE":"Target Support Files/Pods-Runner/Pods-Runner.modulemap","OTHER_LDFLAGS":"$(inherited) -framework Flutter","OTHER_LIBTOOLFLAGS":"","PODS_ROOT":"$(SRCROOT)","PRODUCT_BUNDLE_IDENTIFIER":"org.cocoapods.${PRODUCT_NAME:rfc1034identifier}","PRODUCT_NAME":"$(TARGET_NAME:c99extidentifier)","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e98e4a12d91909beabfb3ea44357e371c3a","name":"Profile"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98f17abf55d75f35efcaf45a1185b085b6","buildSettings":{"ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES":"NO","CLANG_ENABLE_OBJC_WEAK":"NO","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DEFINES_MODULE":"YES","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","INFOPLIST_FILE":"Target Support Files/Pods-Runner/Pods-Runner-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","IPHONEOS_DEPLOYMENT_TARGET":"13.0","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks @loader_path/Frameworks","MACH_O_TYPE":"staticlib","MODULEMAP_FILE":"Target Support Files/Pods-Runner/Pods-Runner.modulemap","OTHER_LDFLAGS":"$(inherited) -framework Flutter","OTHER_LIBTOOLFLAGS":"","PODS_ROOT":"$(SRCROOT)","PRODUCT_BUNDLE_IDENTIFIER":"org.cocoapods.${PRODUCT_NAME:rfc1034identifier}","PRODUCT_NAME":"$(TARGET_NAME:c99extidentifier)","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e98cafdf8e5f53f5249fdb699a2c3732041","name":"Release"}],"buildPhases":[{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e980ec6fbca7264a6936f2adfc48dd7f5bc","guid":"bfdfe7dc352907fc980b868725387e98c18583e54caccb23480e9ad821fcc5d4","headerVisibility":"public"}],"guid":"bfdfe7dc352907fc980b868725387e982b12e1a9514e59d182b3bdc6513b57e9","type":"com.apple.buildphase.headers"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e98fed0970e703d355c9aff813f280aafdc","guid":"bfdfe7dc352907fc980b868725387e98d7195027851f4e0b20d9fe9fe9ba555f"}],"guid":"bfdfe7dc352907fc980b868725387e98913f65d029169dd43df72f5d47591332","type":"com.apple.buildphase.sources"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e9867aa7825f197b5c58a57320b4ccdc64e","guid":"bfdfe7dc352907fc980b868725387e981d396859bc3fe6a005f0edb170134b5c"}],"guid":"bfdfe7dc352907fc980b868725387e9826f150e187da19c7d1e421741141421b","type":"com.apple.buildphase.frameworks"},{"buildFiles":[],"guid":"bfdfe7dc352907fc980b868725387e98be276f630606ffe5c387e92a6079d1a6","type":"com.apple.buildphase.resources"}],"buildRules":[],"dependencies":[{"guid":"bfdfe7dc352907fc980b868725387e989da425bb6d6d5d8dbb95e4afffb82217","name":"Flutter"},{"guid":"bfdfe7dc352907fc980b868725387e987a4af56e2729cecfad7b13d62a9a5fa4","name":"TOCropViewController"},{"guid":"bfdfe7dc352907fc980b868725387e98839a1650b1f10605b2db52456c9e6468","name":"fluttertoast"},{"guid":"bfdfe7dc352907fc980b868725387e98c7508c3a173f39338f076ef697b954c4","name":"image_cropper"},{"guid":"bfdfe7dc352907fc980b868725387e981f000f066404b97b12e9c4ca84d38d0f","name":"image_picker_ios"},{"guid":"bfdfe7dc352907fc980b868725387e9830037b09fee48cfce1f8562d753688c8","name":"path_provider_foundation"},{"guid":"bfdfe7dc352907fc980b868725387e98ef10255b706f98e1e88fae00855b0968","name":"permission_handler_apple"},{"guid":"bfdfe7dc352907fc980b868725387e9828cab1f188854e0a973e6ff6905c5ffe","name":"shared_preferences_foundation"},{"guid":"bfdfe7dc352907fc980b868725387e981304d3d2169071b3ca365b19f5340b7c","name":"sqflite_darwin"},{"guid":"bfdfe7dc352907fc980b868725387e988efdc4dd0ac29b43123295eca853f4ed","name":"webview_flutter_wkwebview"}],"guid":"bfdfe7dc352907fc980b868725387e98312b4bc59bbbe2c06c205bf4da6737f5","name":"Pods-Runner","predominantSourceCodeLanguage":"Xcode.SourceCodeLanguage.Objective-C","productReference":{"guid":"bfdfe7dc352907fc980b868725387e98699846e06e93b50cafdb00290784c775","name":"Pods_Runner.framework","type":"product"},"productTypeIdentifier":"com.apple.product-type.framework","provisioningSourceData":[{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Debug","provisioningStyle":1},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Profile","provisioningStyle":1},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Release","provisioningStyle":1}],"type":"standard"} |
| 1 | -{"buildConfigurations":[{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98144cd18850e477837c238075d5256ffe","buildSettings":{"ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES":"NO","CLANG_ENABLE_OBJC_WEAK":"NO","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DEFINES_MODULE":"YES","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","INFOPLIST_FILE":"Target Support Files/Pods-RunnerTests/Pods-RunnerTests-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","IPHONEOS_DEPLOYMENT_TARGET":"13.0","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks @loader_path/Frameworks","MACH_O_TYPE":"staticlib","MODULEMAP_FILE":"Target Support Files/Pods-RunnerTests/Pods-RunnerTests.modulemap","ONLY_ACTIVE_ARCH":"NO","OTHER_LDFLAGS":"$(inherited) -framework Flutter","OTHER_LIBTOOLFLAGS":"","PODS_ROOT":"$(SRCROOT)","PRODUCT_BUNDLE_IDENTIFIER":"org.cocoapods.${PRODUCT_NAME:rfc1034identifier}","PRODUCT_NAME":"$(TARGET_NAME:c99extidentifier)","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e980029bc42f3dda39154e9cd2e8ce213da","name":"Debug"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e981b663a2c82f0220040296818ba53477e","buildSettings":{"ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES":"NO","CLANG_ENABLE_OBJC_WEAK":"NO","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DEFINES_MODULE":"YES","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","INFOPLIST_FILE":"Target Support Files/Pods-RunnerTests/Pods-RunnerTests-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","IPHONEOS_DEPLOYMENT_TARGET":"13.0","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks @loader_path/Frameworks","MACH_O_TYPE":"staticlib","MODULEMAP_FILE":"Target Support Files/Pods-RunnerTests/Pods-RunnerTests.modulemap","OTHER_LDFLAGS":"$(inherited) -framework Flutter","OTHER_LIBTOOLFLAGS":"","PODS_ROOT":"$(SRCROOT)","PRODUCT_BUNDLE_IDENTIFIER":"org.cocoapods.${PRODUCT_NAME:rfc1034identifier}","PRODUCT_NAME":"$(TARGET_NAME:c99extidentifier)","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e98d15fe13ffb666b223bfd6ee9f0a6138e","name":"Profile"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98965b92d39d30a7872295adc2841cd1b1","buildSettings":{"ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES":"NO","CLANG_ENABLE_OBJC_WEAK":"NO","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DEFINES_MODULE":"YES","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","INFOPLIST_FILE":"Target Support Files/Pods-RunnerTests/Pods-RunnerTests-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","IPHONEOS_DEPLOYMENT_TARGET":"13.0","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks @loader_path/Frameworks","MACH_O_TYPE":"staticlib","MODULEMAP_FILE":"Target Support Files/Pods-RunnerTests/Pods-RunnerTests.modulemap","OTHER_LDFLAGS":"$(inherited) -framework Flutter","OTHER_LIBTOOLFLAGS":"","PODS_ROOT":"$(SRCROOT)","PRODUCT_BUNDLE_IDENTIFIER":"org.cocoapods.${PRODUCT_NAME:rfc1034identifier}","PRODUCT_NAME":"$(TARGET_NAME:c99extidentifier)","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e98d4a36e40c823f266ea5f9d0711ed6df0","name":"Release"}],"buildPhases":[{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e98e5e8bcdff29e5f8321be18f7989b4bc7","guid":"bfdfe7dc352907fc980b868725387e98ca9af5e2c54f437f9ebb0c203883ccae","headerVisibility":"public"}],"guid":"bfdfe7dc352907fc980b868725387e986e6b8bd91d07f2fb082ccd84c7dcacb1","type":"com.apple.buildphase.headers"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e98022654f1ff78dd844d694dba2439dab2","guid":"bfdfe7dc352907fc980b868725387e9881f185e1672aa83b98d6e30b47f8f468"}],"guid":"bfdfe7dc352907fc980b868725387e98de09b1176c796343f1f9bcd422c73402","type":"com.apple.buildphase.sources"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e9867aa7825f197b5c58a57320b4ccdc64e","guid":"bfdfe7dc352907fc980b868725387e98e7ac2b91ee49764a75561cf994247683"}],"guid":"bfdfe7dc352907fc980b868725387e983bb5c38e7891bdb262f8e050f7d97030","type":"com.apple.buildphase.frameworks"},{"buildFiles":[],"guid":"bfdfe7dc352907fc980b868725387e987fddc24c35656402341de288e0688015","type":"com.apple.buildphase.resources"}],"buildRules":[],"dependencies":[{"guid":"bfdfe7dc352907fc980b868725387e98312b4bc59bbbe2c06c205bf4da6737f5","name":"Pods-Runner"}],"guid":"bfdfe7dc352907fc980b868725387e98483832d3c820398e9d40e1a6904b03fe","name":"Pods-RunnerTests","predominantSourceCodeLanguage":"Xcode.SourceCodeLanguage.Objective-C","productReference":{"guid":"bfdfe7dc352907fc980b868725387e984f9f39caeddf64cc331db2b69d62aa63","name":"Pods_RunnerTests.framework","type":"product"},"productTypeIdentifier":"com.apple.product-type.framework","provisioningSourceData":[{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Debug","provisioningStyle":1},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Profile","provisioningStyle":1},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Release","provisioningStyle":1}],"type":"standard"} |
| 1 | -{"buildConfigurations":[{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98890adb2b2b98fc70f20de16996216002","buildSettings":{"CODE_SIGNING_ALLOWED":"NO","CODE_SIGNING_IDENTITY":"-","CODE_SIGNING_REQUIRED":"NO","CONFIGURATION_BUILD_DIR":"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/image_picker_ios","EXPANDED_CODE_SIGN_IDENTITY":"-","IBSC_MODULE":"image_picker_ios","INFOPLIST_FILE":"Target Support Files/image_picker_ios/ResourceBundle-image_picker_ios_privacy-image_picker_ios-Info.plist","IPHONEOS_DEPLOYMENT_TARGET":"12.0","ONLY_ACTIVE_ARCH":"NO","PRODUCT_NAME":"image_picker_ios_privacy","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","WRAPPER_EXTENSION":"bundle"},"guid":"bfdfe7dc352907fc980b868725387e98c0a44ddda8105286060bf5c6003d30d7","name":"Debug"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98221c7220f1a43556e154d0e84bf28676","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CODE_SIGNING_ALLOWED":"NO","CODE_SIGNING_IDENTITY":"-","CODE_SIGNING_REQUIRED":"NO","CONFIGURATION_BUILD_DIR":"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/image_picker_ios","EXPANDED_CODE_SIGN_IDENTITY":"-","IBSC_MODULE":"image_picker_ios","INFOPLIST_FILE":"Target Support Files/image_picker_ios/ResourceBundle-image_picker_ios_privacy-image_picker_ios-Info.plist","IPHONEOS_DEPLOYMENT_TARGET":"12.0","PRODUCT_NAME":"image_picker_ios_privacy","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","WRAPPER_EXTENSION":"bundle"},"guid":"bfdfe7dc352907fc980b868725387e986afd31b25e8bdbe54b555b3e819bed9a","name":"Profile"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98221c7220f1a43556e154d0e84bf28676","buildSettings":{"CODE_SIGNING_ALLOWED":"NO","CODE_SIGNING_IDENTITY":"-","CODE_SIGNING_REQUIRED":"NO","CONFIGURATION_BUILD_DIR":"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/image_picker_ios","EXPANDED_CODE_SIGN_IDENTITY":"-","IBSC_MODULE":"image_picker_ios","INFOPLIST_FILE":"Target Support Files/image_picker_ios/ResourceBundle-image_picker_ios_privacy-image_picker_ios-Info.plist","IPHONEOS_DEPLOYMENT_TARGET":"12.0","PRODUCT_NAME":"image_picker_ios_privacy","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","WRAPPER_EXTENSION":"bundle"},"guid":"bfdfe7dc352907fc980b868725387e984409b961219dd5170e42cf8af9d37b85","name":"Release"}],"buildPhases":[{"buildFiles":[],"guid":"bfdfe7dc352907fc980b868725387e98da3c51c4c853664165580f13a6f0fc5c","type":"com.apple.buildphase.sources"},{"buildFiles":[],"guid":"bfdfe7dc352907fc980b868725387e98cf2603f400108d5d689d69e49210f9ea","type":"com.apple.buildphase.frameworks"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e98d81fa81818b111128bdfcbfe37fe99cc","guid":"bfdfe7dc352907fc980b868725387e986f2cd484effb79da397fd484ac7cefd7"}],"guid":"bfdfe7dc352907fc980b868725387e9848f72b017e196244386c0f13d723c588","type":"com.apple.buildphase.resources"}],"buildRules":[],"dependencies":[],"guid":"bfdfe7dc352907fc980b868725387e98082dc85da1fc941e5234c7cc1f11b27d","name":"image_picker_ios-image_picker_ios_privacy","productReference":{"guid":"bfdfe7dc352907fc980b868725387e98cba567c8a049008de84f093e54e3191c","name":"image_picker_ios_privacy.bundle","type":"product"},"productTypeIdentifier":"com.apple.product-type.bundle","provisioningSourceData":[{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Debug","provisioningStyle":0},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Profile","provisioningStyle":0},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Release","provisioningStyle":0}],"type":"standard"} |
| 1 | -{"buildConfigurations":[{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e989b8b5e6c332d83d94a43e8bb36f14a8b","buildSettings":{"ASSETCATALOG_COMPILER_APPICON_NAME":"AppIcon","ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME":"AccentColor","CLANG_ENABLE_OBJC_WEAK":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","IPHONEOS_DEPLOYMENT_TARGET":"12.0","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks","ONLY_ACTIVE_ARCH":"NO","SDKROOT":"iphoneos","TARGETED_DEVICE_FAMILY":"1,2"},"guid":"bfdfe7dc352907fc980b868725387e982cf0da236cf10d087750aa1434da9227","name":"Debug"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98765d02cbdfd89645a23d200ad656f8af","buildSettings":{"ASSETCATALOG_COMPILER_APPICON_NAME":"AppIcon","ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME":"AccentColor","CLANG_ENABLE_OBJC_WEAK":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","IPHONEOS_DEPLOYMENT_TARGET":"12.0","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks","SDKROOT":"iphoneos","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES"},"guid":"bfdfe7dc352907fc980b868725387e98cc28f154213fd8181aa70d4c188a8335","name":"Profile"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98765d02cbdfd89645a23d200ad656f8af","buildSettings":{"ASSETCATALOG_COMPILER_APPICON_NAME":"AppIcon","ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME":"AccentColor","CLANG_ENABLE_OBJC_WEAK":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","IPHONEOS_DEPLOYMENT_TARGET":"12.0","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks","SDKROOT":"iphoneos","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES"},"guid":"bfdfe7dc352907fc980b868725387e981f19fefc6e52ad9e4e005a2248234387","name":"Release"}],"buildPhases":[],"buildRules":[],"dependencies":[],"guid":"bfdfe7dc352907fc980b868725387e989da425bb6d6d5d8dbb95e4afffb82217","name":"Flutter","provisioningSourceData":[{"bundleIdentifierFromInfoPlist":"","configurationName":"Debug","provisioningStyle":0},{"bundleIdentifierFromInfoPlist":"","configurationName":"Profile","provisioningStyle":0},{"bundleIdentifierFromInfoPlist":"","configurationName":"Release","provisioningStyle":0}],"type":"aggregate"} |
| 1 | -{"buildConfigurations":[{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98c9406d4edd5339dd20198a367c290d36","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DEFINES_MODULE":"YES","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","GCC_PREFIX_HEADER":"Target Support Files/path_provider_foundation/path_provider_foundation-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/path_provider_foundation/path_provider_foundation-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","IPHONEOS_DEPLOYMENT_TARGET":"12.0","MODULEMAP_FILE":"Target Support Files/path_provider_foundation/path_provider_foundation.modulemap","ONLY_ACTIVE_ARCH":"NO","OTHER_LDFLAGS":"$(inherited) -framework Flutter","PRODUCT_MODULE_NAME":"path_provider_foundation","PRODUCT_NAME":"path_provider_foundation","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e98ab88586633079f928287f370e8b6f07b","name":"Debug"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e988fe63e2e9f76f78e56562689e1d41ffe","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DEFINES_MODULE":"YES","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","GCC_PREFIX_HEADER":"Target Support Files/path_provider_foundation/path_provider_foundation-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/path_provider_foundation/path_provider_foundation-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","IPHONEOS_DEPLOYMENT_TARGET":"12.0","MODULEMAP_FILE":"Target Support Files/path_provider_foundation/path_provider_foundation.modulemap","OTHER_LDFLAGS":"$(inherited) -framework Flutter","PRODUCT_MODULE_NAME":"path_provider_foundation","PRODUCT_NAME":"path_provider_foundation","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e9880f884b2537bd891ed54ff6e3ab7d0ee","name":"Profile"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e988fe63e2e9f76f78e56562689e1d41ffe","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DEFINES_MODULE":"YES","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","GCC_PREFIX_HEADER":"Target Support Files/path_provider_foundation/path_provider_foundation-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/path_provider_foundation/path_provider_foundation-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","IPHONEOS_DEPLOYMENT_TARGET":"12.0","MODULEMAP_FILE":"Target Support Files/path_provider_foundation/path_provider_foundation.modulemap","OTHER_LDFLAGS":"$(inherited) -framework Flutter","PRODUCT_MODULE_NAME":"path_provider_foundation","PRODUCT_NAME":"path_provider_foundation","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e9858b9d941e76db42d349048c14af0e16e","name":"Release"}],"buildPhases":[{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e986ad90640c2a87ccd2504ab539e324d34","guid":"bfdfe7dc352907fc980b868725387e98e40234757d04478dc54a213f59e845fa","headerVisibility":"public"}],"guid":"bfdfe7dc352907fc980b868725387e98450b40315711083d32b0ed949174ff28","type":"com.apple.buildphase.headers"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e989f750ed9e20f950ada8937f306c31206","guid":"bfdfe7dc352907fc980b868725387e98c025a6cef40a0e1fbdb6dfdf276171ba"},{"fileReference":"bfdfe7dc352907fc980b868725387e980e2570f90a57a95b7bd48b9bbed2305f","guid":"bfdfe7dc352907fc980b868725387e986dfc1b5ca512f6383be32a7124385963"},{"fileReference":"bfdfe7dc352907fc980b868725387e98616486747c74cf21aec11f2de8c74a26","guid":"bfdfe7dc352907fc980b868725387e98651a9c0b966fd508c01d94f4cad81677"}],"guid":"bfdfe7dc352907fc980b868725387e98f5d455158bacea210fd45e1a8f3245fc","type":"com.apple.buildphase.sources"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e9867aa7825f197b5c58a57320b4ccdc64e","guid":"bfdfe7dc352907fc980b868725387e9829f34398048903731961241124ac546e"}],"guid":"bfdfe7dc352907fc980b868725387e987ebedde198dc993f3ca38aec4ed08768","type":"com.apple.buildphase.frameworks"},{"buildFiles":[{"guid":"bfdfe7dc352907fc980b868725387e98234997a2811e55e2dfc23faf0b9d3093","targetReference":"bfdfe7dc352907fc980b868725387e987ea64ee8d53085bf9edd1a57aaf8cbb5"}],"guid":"bfdfe7dc352907fc980b868725387e98ac45f7d09c5ae0c1d8f7eb8e8ff004ab","type":"com.apple.buildphase.resources"}],"buildRules":[],"dependencies":[{"guid":"bfdfe7dc352907fc980b868725387e989da425bb6d6d5d8dbb95e4afffb82217","name":"Flutter"},{"guid":"bfdfe7dc352907fc980b868725387e987ea64ee8d53085bf9edd1a57aaf8cbb5","name":"path_provider_foundation-path_provider_foundation_privacy"}],"guid":"bfdfe7dc352907fc980b868725387e9830037b09fee48cfce1f8562d753688c8","name":"path_provider_foundation","predominantSourceCodeLanguage":"Xcode.SourceCodeLanguage.Swift","productReference":{"guid":"bfdfe7dc352907fc980b868725387e98177b75fe6f519d73b22b382cca137f1c","name":"path_provider_foundation.framework","type":"product"},"productTypeIdentifier":"com.apple.product-type.framework","provisioningSourceData":[{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Debug","provisioningStyle":1},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Profile","provisioningStyle":1},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Release","provisioningStyle":1}],"type":"standard"} |
| 1 | -{"buildConfigurations":[{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e987429100136c7cabdb1c53ff7846a606f","buildSettings":{"CODE_SIGNING_ALLOWED":"NO","CODE_SIGNING_IDENTITY":"-","CODE_SIGNING_REQUIRED":"NO","CONFIGURATION_BUILD_DIR":"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/shared_preferences_foundation","EXPANDED_CODE_SIGN_IDENTITY":"-","IBSC_MODULE":"shared_preferences_foundation","INFOPLIST_FILE":"Target Support Files/shared_preferences_foundation/ResourceBundle-shared_preferences_foundation_privacy-shared_preferences_foundation-Info.plist","IPHONEOS_DEPLOYMENT_TARGET":"12.0","ONLY_ACTIVE_ARCH":"NO","PRODUCT_NAME":"shared_preferences_foundation_privacy","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","WRAPPER_EXTENSION":"bundle"},"guid":"bfdfe7dc352907fc980b868725387e984b0ac60cfbf01a73090074fdbdad02ca","name":"Debug"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e9852aeec4c055e14df0bbd35d7207bef7b","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CODE_SIGNING_ALLOWED":"NO","CODE_SIGNING_IDENTITY":"-","CODE_SIGNING_REQUIRED":"NO","CONFIGURATION_BUILD_DIR":"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/shared_preferences_foundation","EXPANDED_CODE_SIGN_IDENTITY":"-","IBSC_MODULE":"shared_preferences_foundation","INFOPLIST_FILE":"Target Support Files/shared_preferences_foundation/ResourceBundle-shared_preferences_foundation_privacy-shared_preferences_foundation-Info.plist","IPHONEOS_DEPLOYMENT_TARGET":"12.0","PRODUCT_NAME":"shared_preferences_foundation_privacy","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","WRAPPER_EXTENSION":"bundle"},"guid":"bfdfe7dc352907fc980b868725387e98497877ccb057c0b998adcc0f530ed6ce","name":"Profile"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e9852aeec4c055e14df0bbd35d7207bef7b","buildSettings":{"CODE_SIGNING_ALLOWED":"NO","CODE_SIGNING_IDENTITY":"-","CODE_SIGNING_REQUIRED":"NO","CONFIGURATION_BUILD_DIR":"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/shared_preferences_foundation","EXPANDED_CODE_SIGN_IDENTITY":"-","IBSC_MODULE":"shared_preferences_foundation","INFOPLIST_FILE":"Target Support Files/shared_preferences_foundation/ResourceBundle-shared_preferences_foundation_privacy-shared_preferences_foundation-Info.plist","IPHONEOS_DEPLOYMENT_TARGET":"12.0","PRODUCT_NAME":"shared_preferences_foundation_privacy","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","WRAPPER_EXTENSION":"bundle"},"guid":"bfdfe7dc352907fc980b868725387e98e4724ec3436be7e4ac6868968ad6eabe","name":"Release"}],"buildPhases":[{"buildFiles":[],"guid":"bfdfe7dc352907fc980b868725387e983798725b834d0f662406381ca7bde3f2","type":"com.apple.buildphase.sources"},{"buildFiles":[],"guid":"bfdfe7dc352907fc980b868725387e98b91701758ca9639610804a99b5398a8d","type":"com.apple.buildphase.frameworks"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e98a7593e8608d9acecd27fab8927976749","guid":"bfdfe7dc352907fc980b868725387e98ac058063c77726eb1f0b10fdd8be15b5"}],"guid":"bfdfe7dc352907fc980b868725387e9837f959b90bb31b5e696f9a85cb667226","type":"com.apple.buildphase.resources"}],"buildRules":[],"dependencies":[],"guid":"bfdfe7dc352907fc980b868725387e98e0be3b0d5ad56f1985578b1f97431765","name":"shared_preferences_foundation-shared_preferences_foundation_privacy","productReference":{"guid":"bfdfe7dc352907fc980b868725387e98ad625504a4c1e61077bbfd33bd1d1785","name":"shared_preferences_foundation_privacy.bundle","type":"product"},"productTypeIdentifier":"com.apple.product-type.bundle","provisioningSourceData":[{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Debug","provisioningStyle":0},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Profile","provisioningStyle":0},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Release","provisioningStyle":0}],"type":"standard"} |
| 1 | -{"buildConfigurations":[{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98a6093e67eab0c95af9b59197c88a1b6b","buildSettings":{"CODE_SIGNING_ALLOWED":"NO","CODE_SIGNING_IDENTITY":"-","CODE_SIGNING_REQUIRED":"NO","CONFIGURATION_BUILD_DIR":"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/permission_handler_apple","EXPANDED_CODE_SIGN_IDENTITY":"-","IBSC_MODULE":"permission_handler_apple","INFOPLIST_FILE":"Target Support Files/permission_handler_apple/ResourceBundle-permission_handler_apple_privacy-permission_handler_apple-Info.plist","IPHONEOS_DEPLOYMENT_TARGET":"9.0","ONLY_ACTIVE_ARCH":"NO","PRODUCT_NAME":"permission_handler_apple_privacy","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","WRAPPER_EXTENSION":"bundle"},"guid":"bfdfe7dc352907fc980b868725387e98ab85269e3c32dd7d46dc5b2d71a85e10","name":"Debug"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e982824e3afc8d872da1d8489aeef65d25c","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CODE_SIGNING_ALLOWED":"NO","CODE_SIGNING_IDENTITY":"-","CODE_SIGNING_REQUIRED":"NO","CONFIGURATION_BUILD_DIR":"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/permission_handler_apple","EXPANDED_CODE_SIGN_IDENTITY":"-","IBSC_MODULE":"permission_handler_apple","INFOPLIST_FILE":"Target Support Files/permission_handler_apple/ResourceBundle-permission_handler_apple_privacy-permission_handler_apple-Info.plist","IPHONEOS_DEPLOYMENT_TARGET":"9.0","PRODUCT_NAME":"permission_handler_apple_privacy","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","WRAPPER_EXTENSION":"bundle"},"guid":"bfdfe7dc352907fc980b868725387e98339217bba3aa34a14079ebf1ecbb02d9","name":"Profile"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e982824e3afc8d872da1d8489aeef65d25c","buildSettings":{"CODE_SIGNING_ALLOWED":"NO","CODE_SIGNING_IDENTITY":"-","CODE_SIGNING_REQUIRED":"NO","CONFIGURATION_BUILD_DIR":"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/permission_handler_apple","EXPANDED_CODE_SIGN_IDENTITY":"-","IBSC_MODULE":"permission_handler_apple","INFOPLIST_FILE":"Target Support Files/permission_handler_apple/ResourceBundle-permission_handler_apple_privacy-permission_handler_apple-Info.plist","IPHONEOS_DEPLOYMENT_TARGET":"9.0","PRODUCT_NAME":"permission_handler_apple_privacy","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","WRAPPER_EXTENSION":"bundle"},"guid":"bfdfe7dc352907fc980b868725387e9848aceabd21d4bd82521339f4bb06e5ef","name":"Release"}],"buildPhases":[{"buildFiles":[],"guid":"bfdfe7dc352907fc980b868725387e9809610ca16e4ec0b8b4d2801aac5247b5","type":"com.apple.buildphase.sources"},{"buildFiles":[],"guid":"bfdfe7dc352907fc980b868725387e98ec495162e4d17b980435a432aa070d1b","type":"com.apple.buildphase.frameworks"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e983baa8e01f584c554d49e2b2bb7cde084","guid":"bfdfe7dc352907fc980b868725387e98a0381af2bb4814d9d0a8e921781d06fe"}],"guid":"bfdfe7dc352907fc980b868725387e981f36d8e95984def1a3e1adcf78688e0d","type":"com.apple.buildphase.resources"}],"buildRules":[],"dependencies":[],"guid":"bfdfe7dc352907fc980b868725387e9802f35ab680609a626ebd2ddd692a3822","name":"permission_handler_apple-permission_handler_apple_privacy","productReference":{"guid":"bfdfe7dc352907fc980b868725387e983e9a904e8a35cb34b69458780be142b3","name":"permission_handler_apple_privacy.bundle","type":"product"},"productTypeIdentifier":"com.apple.product-type.bundle","provisioningSourceData":[{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Debug","provisioningStyle":0},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Profile","provisioningStyle":0},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Release","provisioningStyle":0}],"type":"standard"} |
| 1 | -{"buildConfigurations":[{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98e8fb97e4d8fc6b27d73383f393c88ecd","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DEFINES_MODULE":"YES","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","GCC_PREFIX_HEADER":"Target Support Files/TOCropViewController/TOCropViewController-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/TOCropViewController/TOCropViewController-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","IPHONEOS_DEPLOYMENT_TARGET":"11.0","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks @loader_path/Frameworks","MODULEMAP_FILE":"Target Support Files/TOCropViewController/TOCropViewController.modulemap","ONLY_ACTIVE_ARCH":"NO","PRODUCT_MODULE_NAME":"TOCropViewController","PRODUCT_NAME":"TOCropViewController","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e98605c35452afc21c9738f0c6828c71d26","name":"Debug"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98646a08d73cb1b05edde4b2af727d2773","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DEFINES_MODULE":"YES","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","GCC_PREFIX_HEADER":"Target Support Files/TOCropViewController/TOCropViewController-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/TOCropViewController/TOCropViewController-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","IPHONEOS_DEPLOYMENT_TARGET":"11.0","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks @loader_path/Frameworks","MODULEMAP_FILE":"Target Support Files/TOCropViewController/TOCropViewController.modulemap","PRODUCT_MODULE_NAME":"TOCropViewController","PRODUCT_NAME":"TOCropViewController","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e988c614f91f9b72052667099c32cbe6637","name":"Profile"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98646a08d73cb1b05edde4b2af727d2773","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DEFINES_MODULE":"YES","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","GCC_PREFIX_HEADER":"Target Support Files/TOCropViewController/TOCropViewController-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/TOCropViewController/TOCropViewController-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","IPHONEOS_DEPLOYMENT_TARGET":"11.0","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks @loader_path/Frameworks","MODULEMAP_FILE":"Target Support Files/TOCropViewController/TOCropViewController.modulemap","PRODUCT_MODULE_NAME":"TOCropViewController","PRODUCT_NAME":"TOCropViewController","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e98a86be8a3d3fc9de309d9529a5e706f38","name":"Release"}],"buildPhases":[{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e98cb086915a5d7868551987b7c937b1a48","guid":"bfdfe7dc352907fc980b868725387e980a4dfaa604a5c973bd4cb5802c9855ab","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e980f947dccb1ab29394d270a9013b4bf6e","guid":"bfdfe7dc352907fc980b868725387e9863499551374c1c9ce8b5b4772d95cd02","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e984d282bd26faa86397beb095b7a3d376d","guid":"bfdfe7dc352907fc980b868725387e98729b04462a09cdeae6d1711d1cca87d8","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e98797068857240200848cd110455f52a9a","guid":"bfdfe7dc352907fc980b868725387e98d7cc822e6ff7acf3dda3e6f4e7a05fe2","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e982bff5d3fdbb96b21480273a5f509d057","guid":"bfdfe7dc352907fc980b868725387e9869aa9e264a1406d02e21ec0e12782b90","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e98a10957f0a1eeea88e6ac565e960e0f93","guid":"bfdfe7dc352907fc980b868725387e988c61a099ce5bda3b8c423bda9a71d3fe","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e98f0694873f1892e94bb75f8e59dedc233","guid":"bfdfe7dc352907fc980b868725387e98969de66fdf5f5ba640fa0b4fd2b1983d","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e9856f118f2d46d97111b2a52e08ff662a3","guid":"bfdfe7dc352907fc980b868725387e9892aac1b38c1207e05c8972ff02b5577d","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e98486b24fc8a92b9608b42c206e389e292","guid":"bfdfe7dc352907fc980b868725387e98a105d635d93b75cace7bbd27b86b8e73","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e98c2614aa70647dc91f2899ce09cd44b2e","guid":"bfdfe7dc352907fc980b868725387e98fe2d3380e9a9e674f15a2ea4eea9bba1","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e98822752306ed24d8224a31589482a6a19","guid":"bfdfe7dc352907fc980b868725387e98311b7b43caef2eb83b6f84d293bffd52","headerVisibility":"public"}],"guid":"bfdfe7dc352907fc980b868725387e989823e64e9e00d6500db27cd56bb64766","type":"com.apple.buildphase.headers"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e9820af03ccb5b9dba937543436fde51ebc","guid":"bfdfe7dc352907fc980b868725387e98f2854e2e6c4b0efdadd510b4c7df08e9"},{"fileReference":"bfdfe7dc352907fc980b868725387e98589c232ca66350220c5b2b60af419691","guid":"bfdfe7dc352907fc980b868725387e98862f204c2217ec1fd0f6c2541cb1d7d2"},{"fileReference":"bfdfe7dc352907fc980b868725387e98967f8f000fb83e0cd182b51d0c53cdd4","guid":"bfdfe7dc352907fc980b868725387e98c4192ed237fd06694e27828b8f6eaa20"},{"fileReference":"bfdfe7dc352907fc980b868725387e98e6de2b9bdc36646e78a3b258eff7e562","guid":"bfdfe7dc352907fc980b868725387e98314f445b68467d09bc944f6977d6a1e5"},{"fileReference":"bfdfe7dc352907fc980b868725387e98817bdacacaa9c1fdca41497b32908163","guid":"bfdfe7dc352907fc980b868725387e9899e94270ee0a9fc2990fe6311d9e930c"},{"fileReference":"bfdfe7dc352907fc980b868725387e9816660fd5daec56af3b08e2cb899287bc","guid":"bfdfe7dc352907fc980b868725387e9874865e6e3e9d40fd69956302ea6a16b1"},{"fileReference":"bfdfe7dc352907fc980b868725387e9846c6e6f5c86b917d65fbec9b4dd09550","guid":"bfdfe7dc352907fc980b868725387e9830df155f1ccf9cf1ff06feda787776e8"},{"fileReference":"bfdfe7dc352907fc980b868725387e98af82120dd778704d0b071247289500e0","guid":"bfdfe7dc352907fc980b868725387e98a9f944ecbfbd6276045c1acd677fdc87"},{"fileReference":"bfdfe7dc352907fc980b868725387e98ce28fe5a13c196a6e1dc960861f6321d","guid":"bfdfe7dc352907fc980b868725387e9834639a5bdb4f50681ddcc792d8385313"},{"fileReference":"bfdfe7dc352907fc980b868725387e983daa75eb91fc04928c0658f9216185f7","guid":"bfdfe7dc352907fc980b868725387e983da8a6c162fb34e1a67bb7ab1d4b8cc7"}],"guid":"bfdfe7dc352907fc980b868725387e98b973c71a5c8d9944a2711af778a2c3b6","type":"com.apple.buildphase.sources"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e9867aa7825f197b5c58a57320b4ccdc64e","guid":"bfdfe7dc352907fc980b868725387e98269f2208e6af9ec57440cdb8e9a1fdb7"}],"guid":"bfdfe7dc352907fc980b868725387e988710db881c4cd7a5ab2e4ff5e3783e93","type":"com.apple.buildphase.frameworks"},{"buildFiles":[{"guid":"bfdfe7dc352907fc980b868725387e981ebf3826f47dbb7fd582b4ad06af832b","targetReference":"bfdfe7dc352907fc980b868725387e986dbfa2df59ddcae0f992dedaee8f3553"}],"guid":"bfdfe7dc352907fc980b868725387e98561889aae53f203fe8d20bd552b4212b","type":"com.apple.buildphase.resources"}],"buildRules":[],"dependencies":[{"guid":"bfdfe7dc352907fc980b868725387e986dbfa2df59ddcae0f992dedaee8f3553","name":"TOCropViewController-TOCropViewControllerBundle"}],"guid":"bfdfe7dc352907fc980b868725387e987a4af56e2729cecfad7b13d62a9a5fa4","name":"TOCropViewController","predominantSourceCodeLanguage":"Xcode.SourceCodeLanguage.Objective-C-Plus-Plus","productReference":{"guid":"bfdfe7dc352907fc980b868725387e9874cf314b58ac20a75c1512ceb8885e00","name":"TOCropViewController.framework","type":"product"},"productTypeIdentifier":"com.apple.product-type.framework","provisioningSourceData":[{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Debug","provisioningStyle":1},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Profile","provisioningStyle":1},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Release","provisioningStyle":1}],"type":"standard"} |
| 1 | -{"buildConfigurations":[{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98493610a1174f3f4f738833de56d1328f","buildSettings":{"CODE_SIGNING_ALLOWED":"NO","CODE_SIGNING_IDENTITY":"-","CODE_SIGNING_REQUIRED":"NO","CONFIGURATION_BUILD_DIR":"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/webview_flutter_wkwebview","EXPANDED_CODE_SIGN_IDENTITY":"-","IBSC_MODULE":"webview_flutter_wkwebview","INFOPLIST_FILE":"Target Support Files/webview_flutter_wkwebview/ResourceBundle-webview_flutter_wkwebview_privacy-webview_flutter_wkwebview-Info.plist","IPHONEOS_DEPLOYMENT_TARGET":"12.0","ONLY_ACTIVE_ARCH":"NO","PRODUCT_NAME":"webview_flutter_wkwebview_privacy","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","WRAPPER_EXTENSION":"bundle"},"guid":"bfdfe7dc352907fc980b868725387e98d80b51d8a17903a5a76f87e19f34a1cd","name":"Debug"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e986361f96ad5d0d9cdb22ee6aa8a152702","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CODE_SIGNING_ALLOWED":"NO","CODE_SIGNING_IDENTITY":"-","CODE_SIGNING_REQUIRED":"NO","CONFIGURATION_BUILD_DIR":"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/webview_flutter_wkwebview","EXPANDED_CODE_SIGN_IDENTITY":"-","IBSC_MODULE":"webview_flutter_wkwebview","INFOPLIST_FILE":"Target Support Files/webview_flutter_wkwebview/ResourceBundle-webview_flutter_wkwebview_privacy-webview_flutter_wkwebview-Info.plist","IPHONEOS_DEPLOYMENT_TARGET":"12.0","PRODUCT_NAME":"webview_flutter_wkwebview_privacy","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","WRAPPER_EXTENSION":"bundle"},"guid":"bfdfe7dc352907fc980b868725387e98a8eb84ead72688d86e0b3cc3812ea2c5","name":"Profile"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e986361f96ad5d0d9cdb22ee6aa8a152702","buildSettings":{"CODE_SIGNING_ALLOWED":"NO","CODE_SIGNING_IDENTITY":"-","CODE_SIGNING_REQUIRED":"NO","CONFIGURATION_BUILD_DIR":"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/webview_flutter_wkwebview","EXPANDED_CODE_SIGN_IDENTITY":"-","IBSC_MODULE":"webview_flutter_wkwebview","INFOPLIST_FILE":"Target Support Files/webview_flutter_wkwebview/ResourceBundle-webview_flutter_wkwebview_privacy-webview_flutter_wkwebview-Info.plist","IPHONEOS_DEPLOYMENT_TARGET":"12.0","PRODUCT_NAME":"webview_flutter_wkwebview_privacy","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","WRAPPER_EXTENSION":"bundle"},"guid":"bfdfe7dc352907fc980b868725387e9866813aeb060709759078e48411f62903","name":"Release"}],"buildPhases":[{"buildFiles":[],"guid":"bfdfe7dc352907fc980b868725387e98be0c772f785cd7505ee510c2c1540ffc","type":"com.apple.buildphase.sources"},{"buildFiles":[],"guid":"bfdfe7dc352907fc980b868725387e9803c584675c731ccc36f8caaf4cf90695","type":"com.apple.buildphase.frameworks"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e982297aecfd80c36c1baf114e37d793b48","guid":"bfdfe7dc352907fc980b868725387e98da6537bf6fc10be793f792c58fe8ceb7"}],"guid":"bfdfe7dc352907fc980b868725387e988311469bb57dac0be242a449c4d5a54c","type":"com.apple.buildphase.resources"}],"buildRules":[],"dependencies":[],"guid":"bfdfe7dc352907fc980b868725387e987c93e943aa0a38b5f6684beaf6b4a3a1","name":"webview_flutter_wkwebview-webview_flutter_wkwebview_privacy","productReference":{"guid":"bfdfe7dc352907fc980b868725387e98a0c2ea56ea4c64a4495566659e5fdb93","name":"webview_flutter_wkwebview_privacy.bundle","type":"product"},"productTypeIdentifier":"com.apple.product-type.bundle","provisioningSourceData":[{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Debug","provisioningStyle":0},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Profile","provisioningStyle":0},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Release","provisioningStyle":0}],"type":"standard"} |
| 1 | -{"buildConfigurations":[{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e987429100136c7cabdb1c53ff7846a606f","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","GCC_PREFIX_HEADER":"Target Support Files/shared_preferences_foundation/shared_preferences_foundation-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/shared_preferences_foundation/shared_preferences_foundation-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","IPHONEOS_DEPLOYMENT_TARGET":"12.0","MODULEMAP_FILE":"Target Support Files/shared_preferences_foundation/shared_preferences_foundation.modulemap","ONLY_ACTIVE_ARCH":"NO","OTHER_LDFLAGS":"$(inherited) -framework Flutter","PRODUCT_MODULE_NAME":"shared_preferences_foundation","PRODUCT_NAME":"shared_preferences_foundation","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e986f83bf1d86816a7afe713389f3b0794c","name":"Debug"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e9852aeec4c055e14df0bbd35d7207bef7b","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","GCC_PREFIX_HEADER":"Target Support Files/shared_preferences_foundation/shared_preferences_foundation-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/shared_preferences_foundation/shared_preferences_foundation-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","IPHONEOS_DEPLOYMENT_TARGET":"12.0","MODULEMAP_FILE":"Target Support Files/shared_preferences_foundation/shared_preferences_foundation.modulemap","OTHER_LDFLAGS":"$(inherited) -framework Flutter","PRODUCT_MODULE_NAME":"shared_preferences_foundation","PRODUCT_NAME":"shared_preferences_foundation","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e98c6ce66678a98cae8c935e06602a448e0","name":"Profile"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e9852aeec4c055e14df0bbd35d7207bef7b","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","GCC_PREFIX_HEADER":"Target Support Files/shared_preferences_foundation/shared_preferences_foundation-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/shared_preferences_foundation/shared_preferences_foundation-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","IPHONEOS_DEPLOYMENT_TARGET":"12.0","MODULEMAP_FILE":"Target Support Files/shared_preferences_foundation/shared_preferences_foundation.modulemap","OTHER_LDFLAGS":"$(inherited) -framework Flutter","PRODUCT_MODULE_NAME":"shared_preferences_foundation","PRODUCT_NAME":"shared_preferences_foundation","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e980f59bc0c0df185b08d92e2afa6f35dda","name":"Release"}],"buildPhases":[{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e983ec0af0b71e7c22c7e6379565b559d23","guid":"bfdfe7dc352907fc980b868725387e98e82259888cd400660e6ae15b115eb233","headerVisibility":"public"}],"guid":"bfdfe7dc352907fc980b868725387e9845bc282ec8aa7540f3a569c2631d21d5","type":"com.apple.buildphase.headers"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e98e7a271e245866241ed9115730062d18c","guid":"bfdfe7dc352907fc980b868725387e983f910a660af8b1be8c5c9c125d4adb96"},{"fileReference":"bfdfe7dc352907fc980b868725387e987877bde4a16b804bce84a03009cd4a3f","guid":"bfdfe7dc352907fc980b868725387e98489a95f2019f3ec6e0acd5ba6de8991a"},{"fileReference":"bfdfe7dc352907fc980b868725387e98c00b68f3aa242f9059f2aa57ccc6a50b","guid":"bfdfe7dc352907fc980b868725387e989af2be92c6af9962a52aabe6c4751285"}],"guid":"bfdfe7dc352907fc980b868725387e98f14b5d6b6d6b0c465e2f1e0eaa6bc1cd","type":"com.apple.buildphase.sources"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e9867aa7825f197b5c58a57320b4ccdc64e","guid":"bfdfe7dc352907fc980b868725387e98475ba5d87573359032e9b06fd466a003"}],"guid":"bfdfe7dc352907fc980b868725387e9859badffc37928e123e98be61f8d11d71","type":"com.apple.buildphase.frameworks"},{"buildFiles":[{"guid":"bfdfe7dc352907fc980b868725387e9872e4e537a8c9a8da179493daa4c54b77","targetReference":"bfdfe7dc352907fc980b868725387e98e0be3b0d5ad56f1985578b1f97431765"}],"guid":"bfdfe7dc352907fc980b868725387e9876fd72010a5b056ae41fa1936cd39334","type":"com.apple.buildphase.resources"}],"buildRules":[],"dependencies":[{"guid":"bfdfe7dc352907fc980b868725387e989da425bb6d6d5d8dbb95e4afffb82217","name":"Flutter"},{"guid":"bfdfe7dc352907fc980b868725387e98e0be3b0d5ad56f1985578b1f97431765","name":"shared_preferences_foundation-shared_preferences_foundation_privacy"}],"guid":"bfdfe7dc352907fc980b868725387e9828cab1f188854e0a973e6ff6905c5ffe","name":"shared_preferences_foundation","predominantSourceCodeLanguage":"Xcode.SourceCodeLanguage.Swift","productReference":{"guid":"bfdfe7dc352907fc980b868725387e9815af7ba71ce93f789a463577fc360420","name":"shared_preferences_foundation.framework","type":"product"},"productTypeIdentifier":"com.apple.product-type.framework","provisioningSourceData":[{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Debug","provisioningStyle":1},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Profile","provisioningStyle":1},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Release","provisioningStyle":1}],"type":"standard"} |
| 1 | -{"buildConfigurations":[{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e9872c3a102c2f55c6f0eee5833107fac63","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","GCC_PREFIX_HEADER":"Target Support Files/sqflite_darwin/sqflite_darwin-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/sqflite_darwin/sqflite_darwin-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","IPHONEOS_DEPLOYMENT_TARGET":"12.0","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks @loader_path/Frameworks","MODULEMAP_FILE":"Target Support Files/sqflite_darwin/sqflite_darwin.modulemap","ONLY_ACTIVE_ARCH":"NO","OTHER_LDFLAGS":"$(inherited) -framework Flutter","PRODUCT_MODULE_NAME":"sqflite_darwin","PRODUCT_NAME":"sqflite_darwin","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e98ee49f65d26d8a0c930cc03d16e0ffcc8","name":"Debug"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98d1a455bf7417c3aeb645db18ef108a94","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","GCC_PREFIX_HEADER":"Target Support Files/sqflite_darwin/sqflite_darwin-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/sqflite_darwin/sqflite_darwin-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","IPHONEOS_DEPLOYMENT_TARGET":"12.0","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks @loader_path/Frameworks","MODULEMAP_FILE":"Target Support Files/sqflite_darwin/sqflite_darwin.modulemap","OTHER_LDFLAGS":"$(inherited) -framework Flutter","PRODUCT_MODULE_NAME":"sqflite_darwin","PRODUCT_NAME":"sqflite_darwin","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e987d3fcd01a34fff9e18dec0764bcc371e","name":"Profile"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e98d1a455bf7417c3aeb645db18ef108a94","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"NO","CODE_SIGN_IDENTITY[sdk=appletvos*]":"","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"","CODE_SIGN_IDENTITY[sdk=watchos*]":"","CURRENT_PROJECT_VERSION":"1","DYLIB_COMPATIBILITY_VERSION":"1","DYLIB_CURRENT_VERSION":"1","DYLIB_INSTALL_NAME_BASE":"@rpath","ENABLE_BITCODE":"NO","ENABLE_MODULE_VERIFIER":"NO","ENABLE_USER_SCRIPT_SANDBOXING":"NO","EXCLUDED_ARCHS[sdk=iphoneos*]":"$(inherited) armv7","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"$(inherited) i386","FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64\" $(inherited)","FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]":"\"/Users/changshouda/Documents/flutter_flutter/bin/cache/artifacts/engine/ios-release/Flutter.xcframework/ios-arm64_x86_64-simulator\" $(inherited)","GCC_PREFIX_HEADER":"Target Support Files/sqflite_darwin/sqflite_darwin-prefix.pch","GENERATE_INFOPLIST_FILE":"NO","INFOPLIST_FILE":"Target Support Files/sqflite_darwin/sqflite_darwin-Info.plist","INSTALL_PATH":"$(LOCAL_LIBRARY_DIR)/Frameworks","IPHONEOS_DEPLOYMENT_TARGET":"12.0","LD_RUNPATH_SEARCH_PATHS":"$(inherited) @executable_path/Frameworks @loader_path/Frameworks","MODULEMAP_FILE":"Target Support Files/sqflite_darwin/sqflite_darwin.modulemap","OTHER_LDFLAGS":"$(inherited) -framework Flutter","PRODUCT_MODULE_NAME":"sqflite_darwin","PRODUCT_NAME":"sqflite_darwin","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) ","SWIFT_INSTALL_OBJC_HEADER":"YES","SWIFT_VERSION":"5.0","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","VALID_ARCHS[sdk=iphonesimulator*]":"$(ARCHS_STANDARD)","VERSIONING_SYSTEM":"apple-generic","VERSION_INFO_PREFIX":""},"guid":"bfdfe7dc352907fc980b868725387e984def481d54810d0de6d3335b228b2a5d","name":"Release"}],"buildPhases":[{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e986fb22dd51fa46a134940fe6eaca4c33f","guid":"bfdfe7dc352907fc980b868725387e98f74bfe561cdc142d140be02f934f1dd5","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e9884ed5320049c5deacc8bdde324892932","guid":"bfdfe7dc352907fc980b868725387e98ee11ff009cad5d58df14f6726361e05e"},{"fileReference":"bfdfe7dc352907fc980b868725387e986111af286522da9c91d0a23bb43d5789","guid":"bfdfe7dc352907fc980b868725387e985b56be77fd90c95178875fbdc9759ded"},{"fileReference":"bfdfe7dc352907fc980b868725387e98f522c2c362e0cb505e091d40b601b659","guid":"bfdfe7dc352907fc980b868725387e989fb03240d74c5b16d4df2090487dd870"},{"fileReference":"bfdfe7dc352907fc980b868725387e98fb3a48ecd51a149ca81f8964ffc2a633","guid":"bfdfe7dc352907fc980b868725387e98b0a98399ea089bec6015924fca214fd0"},{"fileReference":"bfdfe7dc352907fc980b868725387e98a6073c90465ff96a450936e03863b005","guid":"bfdfe7dc352907fc980b868725387e98a561fea4089e8e9aaa7d41e004a74551"},{"fileReference":"bfdfe7dc352907fc980b868725387e984af2acc58d476c23807b931ebdc6fcea","guid":"bfdfe7dc352907fc980b868725387e98e58eb8cea732f8da9eabee06f4760c04"},{"fileReference":"bfdfe7dc352907fc980b868725387e989792f90254ab248f54bb28aad5d10b96","guid":"bfdfe7dc352907fc980b868725387e989f7af140018ee6c1e6b210b20014ceb1"},{"fileReference":"bfdfe7dc352907fc980b868725387e980a6fc4f6d4839e6b447146ca7c886bce","guid":"bfdfe7dc352907fc980b868725387e98f23c703fb2f8f96105b7661f651d76b4"},{"fileReference":"bfdfe7dc352907fc980b868725387e989592a62ecd847bb19b5cd1307f338495","guid":"bfdfe7dc352907fc980b868725387e98b842866f43a58c7f2cd4903a46020f66"},{"fileReference":"bfdfe7dc352907fc980b868725387e980af65b71df82ad9c499004407ea24610","guid":"bfdfe7dc352907fc980b868725387e986769c43d062e13d97bdb26435517a0a9","headerVisibility":"public"},{"fileReference":"bfdfe7dc352907fc980b868725387e9888ff0ca83bb270d92e95e5a3a94f5b66","guid":"bfdfe7dc352907fc980b868725387e98dcd1d6833c3ba22cff192e9c729f402d"},{"fileReference":"bfdfe7dc352907fc980b868725387e98d5ab4adf06bad8786975c988a7c1aefc","guid":"bfdfe7dc352907fc980b868725387e98a8b10f256eab576dca45253c4e5eb01a"},{"fileReference":"bfdfe7dc352907fc980b868725387e9870b0a201c6540c238e46ebd2a3e3f6e2","guid":"bfdfe7dc352907fc980b868725387e984db51d20d4b10739c36207080362307d","headerVisibility":"public"}],"guid":"bfdfe7dc352907fc980b868725387e98cc465b98567e5be1dff8b7284a07e4e3","type":"com.apple.buildphase.headers"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e98a3e0e5f6b6128baf5a0636ba98dd1bf7","guid":"bfdfe7dc352907fc980b868725387e985adaa6c4e40f33315ac0cec984200988"},{"fileReference":"bfdfe7dc352907fc980b868725387e98b5a595be9be31b486c76ff2f88085da9","guid":"bfdfe7dc352907fc980b868725387e9887c26a4ce919cd8e1da7ba74bc37f5e3"},{"fileReference":"bfdfe7dc352907fc980b868725387e98a0dd514539473bc748477a243ceff41d","guid":"bfdfe7dc352907fc980b868725387e98d14ae3cc83358b6ee88a1f9a4d5c6d3e"},{"fileReference":"bfdfe7dc352907fc980b868725387e9879bf3158d99ad90816a50571a9897a1b","guid":"bfdfe7dc352907fc980b868725387e983dbbed738f1be0937cd9105c2a700a04"},{"fileReference":"bfdfe7dc352907fc980b868725387e986fd10043962de1416443951e6f9a1223","guid":"bfdfe7dc352907fc980b868725387e98ea0fba00024403dbd3e85cac557df8e5"},{"fileReference":"bfdfe7dc352907fc980b868725387e9809c004b58d16a21de9d0533f0896ef75","guid":"bfdfe7dc352907fc980b868725387e9823662ee0e62201af4b56a8af77ae26e6"},{"fileReference":"bfdfe7dc352907fc980b868725387e98caf6cab5efa0d88fed7a44cb8c5eb823","guid":"bfdfe7dc352907fc980b868725387e98fed9191d2666c9cd3507583cd694c1a5"},{"fileReference":"bfdfe7dc352907fc980b868725387e98c83761c941d8fe8c3cf108bf0d3e3aa5","guid":"bfdfe7dc352907fc980b868725387e98af1eb69ffb28b93e0b654f9fa9aeec11"},{"fileReference":"bfdfe7dc352907fc980b868725387e98407416797503baf87cf9e40adefc896e","guid":"bfdfe7dc352907fc980b868725387e98e6b9ebef247a9cb276932d07fdf18195"}],"guid":"bfdfe7dc352907fc980b868725387e9837c2f0a37c50e959478519168227e455","type":"com.apple.buildphase.sources"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e9867aa7825f197b5c58a57320b4ccdc64e","guid":"bfdfe7dc352907fc980b868725387e982f461d51c284a55b8f869fc9092ae5dc"}],"guid":"bfdfe7dc352907fc980b868725387e98dd47f73652ff7b522b7942f6a87afd23","type":"com.apple.buildphase.frameworks"},{"buildFiles":[{"guid":"bfdfe7dc352907fc980b868725387e98d17544c34b81de618417de5f9c91b4ec","targetReference":"bfdfe7dc352907fc980b868725387e9883134bb5f399cb37a1eb075d4fea30d8"}],"guid":"bfdfe7dc352907fc980b868725387e98e60a652c76bfee084293e97b00176921","type":"com.apple.buildphase.resources"}],"buildRules":[],"dependencies":[{"guid":"bfdfe7dc352907fc980b868725387e989da425bb6d6d5d8dbb95e4afffb82217","name":"Flutter"},{"guid":"bfdfe7dc352907fc980b868725387e9883134bb5f399cb37a1eb075d4fea30d8","name":"sqflite_darwin-sqflite_darwin_privacy"}],"guid":"bfdfe7dc352907fc980b868725387e981304d3d2169071b3ca365b19f5340b7c","name":"sqflite_darwin","predominantSourceCodeLanguage":"Xcode.SourceCodeLanguage.Objective-C-Plus-Plus","productReference":{"guid":"bfdfe7dc352907fc980b868725387e98dbbec3eebed26c79cc653713be723aba","name":"sqflite_darwin.framework","type":"product"},"productTypeIdentifier":"com.apple.product-type.framework","provisioningSourceData":[{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Debug","provisioningStyle":1},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Profile","provisioningStyle":1},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Release","provisioningStyle":1}],"type":"standard"} |
| 1 | -{"buildConfigurations":[{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e9868b77f49da4a0e0608aac8b3f5ae3e8e","buildSettings":{"CODE_SIGNING_ALLOWED":"NO","CODE_SIGNING_IDENTITY":"-","CODE_SIGNING_REQUIRED":"NO","CONFIGURATION_BUILD_DIR":"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/fluttertoast","EXPANDED_CODE_SIGN_IDENTITY":"-","IBSC_MODULE":"fluttertoast","INFOPLIST_FILE":"Target Support Files/fluttertoast/ResourceBundle-fluttertoast_privacy-fluttertoast-Info.plist","IPHONEOS_DEPLOYMENT_TARGET":"9.0","ONLY_ACTIVE_ARCH":"NO","PRODUCT_NAME":"fluttertoast_privacy","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","WRAPPER_EXTENSION":"bundle"},"guid":"bfdfe7dc352907fc980b868725387e98e953ed566e91deecfd81b8d86775557e","name":"Debug"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e9852dc1371d317d5fc535ff1f306279566","buildSettings":{"CLANG_ENABLE_OBJC_WEAK":"NO","CODE_SIGNING_ALLOWED":"NO","CODE_SIGNING_IDENTITY":"-","CODE_SIGNING_REQUIRED":"NO","CONFIGURATION_BUILD_DIR":"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/fluttertoast","EXPANDED_CODE_SIGN_IDENTITY":"-","IBSC_MODULE":"fluttertoast","INFOPLIST_FILE":"Target Support Files/fluttertoast/ResourceBundle-fluttertoast_privacy-fluttertoast-Info.plist","IPHONEOS_DEPLOYMENT_TARGET":"9.0","PRODUCT_NAME":"fluttertoast_privacy","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","VALIDATE_PRODUCT":"YES","WRAPPER_EXTENSION":"bundle"},"guid":"bfdfe7dc352907fc980b868725387e98aaf3428a047eceae78d5066e966725e2","name":"Profile"},{"baseConfigurationFileReference":"bfdfe7dc352907fc980b868725387e9852dc1371d317d5fc535ff1f306279566","buildSettings":{"CODE_SIGNING_ALLOWED":"NO","CODE_SIGNING_IDENTITY":"-","CODE_SIGNING_REQUIRED":"NO","CONFIGURATION_BUILD_DIR":"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/fluttertoast","EXPANDED_CODE_SIGN_IDENTITY":"-","IBSC_MODULE":"fluttertoast","INFOPLIST_FILE":"Target Support Files/fluttertoast/ResourceBundle-fluttertoast_privacy-fluttertoast-Info.plist","IPHONEOS_DEPLOYMENT_TARGET":"9.0","PRODUCT_NAME":"fluttertoast_privacy","SDKROOT":"iphoneos","SKIP_INSTALL":"YES","TARGETED_DEVICE_FAMILY":"1,2","WRAPPER_EXTENSION":"bundle"},"guid":"bfdfe7dc352907fc980b868725387e98f579fcd0430145ff5c26ebb561295b61","name":"Release"}],"buildPhases":[{"buildFiles":[],"guid":"bfdfe7dc352907fc980b868725387e9841303d8c9e72995b61c742fccb2bd21c","type":"com.apple.buildphase.sources"},{"buildFiles":[],"guid":"bfdfe7dc352907fc980b868725387e984d28eac7bb4cfd0b6e33b38e608751b1","type":"com.apple.buildphase.frameworks"},{"buildFiles":[{"fileReference":"bfdfe7dc352907fc980b868725387e98929a0306e3d8a8e4d4e29ee5e8aefe33","guid":"bfdfe7dc352907fc980b868725387e98eed99eb74e9521e86d82963f3fcafaa0"}],"guid":"bfdfe7dc352907fc980b868725387e98b5f9c12d7a063695e354f05b3e8b7e8e","type":"com.apple.buildphase.resources"}],"buildRules":[],"dependencies":[],"guid":"bfdfe7dc352907fc980b868725387e985739272bce418ef50bd06c859612bad5","name":"fluttertoast-fluttertoast_privacy","productReference":{"guid":"bfdfe7dc352907fc980b868725387e989d4bb598ca0a92e1d0f3a4ef0157bf7e","name":"fluttertoast_privacy.bundle","type":"product"},"productTypeIdentifier":"com.apple.product-type.bundle","provisioningSourceData":[{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Debug","provisioningStyle":0},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Profile","provisioningStyle":0},{"bundleIdentifierFromInfoPlist":"${PRODUCT_BUNDLE_IDENTIFIER}","configurationName":"Release","provisioningStyle":0}],"type":"standard"} |
| 1 | -{"guid":"dc4b70c03e8043e50e38f2068887b1d4","name":"Pods","path":"/Users/changshouda/Documents/FlutterProject/doublefeel_flutter/ios/Pods/Pods.xcodeproj/project.xcworkspace","projects":["PROJECT@v11_mod=0c3f0943d4c7c3601816831d5458ddcd_hash=bfdfe7dc352907fc980b868725387e98plugins=1OJSG6M1FOV3XYQCBH7Z29RZ0FPR9XDE1"]} |
| @@ -7,6 +7,7 @@ import '../../core/network/api/health_api.dart'; | @@ -7,6 +7,7 @@ import '../../core/network/api/health_api.dart'; | ||
| 7 | import '../../core/network/api/interaction_api.dart'; | 7 | import '../../core/network/api/interaction_api.dart'; |
| 8 | import '../../core/network/api/obs_api.dart'; | 8 | import '../../core/network/api/obs_api.dart'; |
| 9 | import '../../core/network/api/pay_api.dart'; | 9 | import '../../core/network/api/pay_api.dart'; |
| 10 | +import '../../core/network/api/theme_api.dart'; | ||
| 10 | import '../../core/network/api/user_api.dart'; | 11 | import '../../core/network/api/user_api.dart'; |
| 11 | import '../../core/network/api/vip_api.dart'; | 12 | import '../../core/network/api/vip_api.dart'; |
| 12 | import '../../core/network/dio_client.dart'; | 13 | import '../../core/network/dio_client.dart'; |
| @@ -86,6 +87,11 @@ void registerConfigDeps(DioClient dioClient) { | @@ -86,6 +87,11 @@ void registerConfigDeps(DioClient dioClient) { | ||
| 86 | Get.lazyPut(() => ConfigApi(dioClient), fenix: true); | 87 | Get.lazyPut(() => ConfigApi(dioClient), fenix: true); |
| 87 | } | 88 | } |
| 88 | 89 | ||
| 90 | +/// Watch theme API. | ||
| 91 | +void registerThemeDeps(DioClient dioClient) { | ||
| 92 | + Get.lazyPut(() => ThemeApi(dioClient), fenix: true); | ||
| 93 | +} | ||
| 94 | + | ||
| 89 | /// Partner interaction API and wear engine. | 95 | /// Partner interaction API and wear engine. |
| 90 | void registerInteractionDeps( | 96 | void registerInteractionDeps( |
| 91 | DioClient dioClient, | 97 | DioClient dioClient, |
| @@ -37,6 +37,7 @@ class InitialBinding extends Bindings { | @@ -37,6 +37,7 @@ class InitialBinding extends Bindings { | ||
| 37 | registerHealthDeps(dioClient); | 37 | registerHealthDeps(dioClient); |
| 38 | registerPayDeps(dioClient); | 38 | registerPayDeps(dioClient); |
| 39 | registerConfigDeps(dioClient); | 39 | registerConfigDeps(dioClient); |
| 40 | + registerThemeDeps(dioClient); | ||
| 40 | registerInteractionDeps(dioClient, userPrefs); | 41 | registerInteractionDeps(dioClient, userPrefs); |
| 41 | registerObsDeps(dioClient); | 42 | registerObsDeps(dioClient); |
| 42 | } | 43 | } |
| 1 | import 'package:get/get.dart'; | 1 | import 'package:get/get.dart'; |
| 2 | 2 | ||
| 3 | +import '../../../../core/network/api/theme_api.dart'; | ||
| 3 | import '../controllers/create_watch_theme_controller.dart'; | 4 | import '../controllers/create_watch_theme_controller.dart'; |
| 4 | import '../controllers/custom_watch_theme_preview_controller.dart'; | 5 | import '../controllers/custom_watch_theme_preview_controller.dart'; |
| 5 | import '../controllers/watch_theme_controller.dart'; | 6 | import '../controllers/watch_theme_controller.dart'; |
| @@ -8,7 +9,9 @@ import '../controllers/watch_theme_preview_controller.dart'; | @@ -8,7 +9,9 @@ import '../controllers/watch_theme_preview_controller.dart'; | ||
| 8 | class WatchThemeBinding extends Bindings { | 9 | class WatchThemeBinding extends Bindings { |
| 9 | @override | 10 | @override |
| 10 | void dependencies() { | 11 | void dependencies() { |
| 11 | - Get.lazyPut<WatchThemeController>(() => WatchThemeController()); | 12 | + Get.lazyPut<WatchThemeController>( |
| 13 | + () => WatchThemeController(Get.find<ThemeApi>()), | ||
| 14 | + ); | ||
| 12 | } | 15 | } |
| 13 | } | 16 | } |
| 14 | 17 | ||
| @@ -16,7 +19,7 @@ class WatchThemePreviewBinding extends Bindings { | @@ -16,7 +19,7 @@ class WatchThemePreviewBinding extends Bindings { | ||
| 16 | @override | 19 | @override |
| 17 | void dependencies() { | 20 | void dependencies() { |
| 18 | Get.lazyPut<WatchThemePreviewController>( | 21 | Get.lazyPut<WatchThemePreviewController>( |
| 19 | - () => WatchThemePreviewController(), | 22 | + () => WatchThemePreviewController(Get.find<ThemeApi>()), |
| 20 | ); | 23 | ); |
| 21 | } | 24 | } |
| 22 | } | 25 | } |
| @@ -25,7 +28,7 @@ class CreateWatchThemeBinding extends Bindings { | @@ -25,7 +28,7 @@ class CreateWatchThemeBinding extends Bindings { | ||
| 25 | @override | 28 | @override |
| 26 | void dependencies() { | 29 | void dependencies() { |
| 27 | Get.lazyPut<CreateWatchThemeController>( | 30 | Get.lazyPut<CreateWatchThemeController>( |
| 28 | - () => CreateWatchThemeController(), | 31 | + () => CreateWatchThemeController(Get.find<ThemeApi>()), |
| 29 | ); | 32 | ); |
| 30 | } | 33 | } |
| 31 | } | 34 | } |
| @@ -34,7 +37,7 @@ class CustomWatchThemePreviewBinding extends Bindings { | @@ -34,7 +37,7 @@ class CustomWatchThemePreviewBinding extends Bindings { | ||
| 34 | @override | 37 | @override |
| 35 | void dependencies() { | 38 | void dependencies() { |
| 36 | Get.lazyPut<CustomWatchThemePreviewController>( | 39 | Get.lazyPut<CustomWatchThemePreviewController>( |
| 37 | - () => CustomWatchThemePreviewController(), | 40 | + () => CustomWatchThemePreviewController(Get.find<ThemeApi>()), |
| 38 | ); | 41 | ); |
| 39 | } | 42 | } |
| 40 | } | 43 | } |
| 1 | import 'package:doublefeel_flutter/app/routes/app_pages.dart'; | 1 | import 'package:doublefeel_flutter/app/routes/app_pages.dart'; |
| 2 | +import 'package:doublefeel_flutter/core/network/api/theme_api.dart'; | ||
| 3 | +import 'package:doublefeel_flutter/core/result/app_result.dart'; | ||
| 2 | import 'package:doublefeel_flutter/core/util/app_toast.dart'; | 4 | import 'package:doublefeel_flutter/core/util/app_toast.dart'; |
| 3 | import 'package:doublefeel_flutter/pigeon/wear_engine_api.g.dart'; | 5 | import 'package:doublefeel_flutter/pigeon/wear_engine_api.g.dart'; |
| 4 | import 'package:flutter/material.dart'; | 6 | import 'package:flutter/material.dart'; |
| @@ -8,10 +10,15 @@ import '../models/watch_theme_models.dart'; | @@ -8,10 +10,15 @@ import '../models/watch_theme_models.dart'; | ||
| 8 | import '../widgets/watch_theme_dialogs.dart'; | 10 | import '../widgets/watch_theme_dialogs.dart'; |
| 9 | 11 | ||
| 10 | class CreateWatchThemeController extends GetxController { | 12 | class CreateWatchThemeController extends GetxController { |
| 13 | + CreateWatchThemeController(this._themeApi); | ||
| 14 | + | ||
| 15 | + final ThemeApi _themeApi; | ||
| 16 | + | ||
| 11 | final customThemeName = ''.obs; | 17 | final customThemeName = ''.obs; |
| 12 | final agreedToSubmission = true.obs; | 18 | final agreedToSubmission = true.obs; |
| 13 | final customStatusNames = ['状态优秀', '状态正常', '注意压力', '压力过载'].obs; | 19 | final customStatusNames = ['状态优秀', '状态正常', '注意压力', '压力过载'].obs; |
| 14 | final customImagePaths = RxList<String?>.filled(4, null); | 20 | final customImagePaths = RxList<String?>.filled(4, null); |
| 21 | + final isSaving = false.obs; | ||
| 15 | late final TextEditingController themeNameController; | 22 | late final TextEditingController themeNameController; |
| 16 | 23 | ||
| 17 | @override | 24 | @override |
| @@ -30,9 +37,10 @@ class CreateWatchThemeController extends GetxController { | @@ -30,9 +37,10 @@ class CreateWatchThemeController extends GetxController { | ||
| 30 | } | 37 | } |
| 31 | 38 | ||
| 32 | bool get canSaveCustomTheme => | 39 | bool get canSaveCustomTheme => |
| 40 | + !isSaving.value && | ||
| 33 | customThemeName.value.isNotEmpty && | 41 | customThemeName.value.isNotEmpty && |
| 34 | agreedToSubmission.value && | 42 | agreedToSubmission.value && |
| 35 | - customImagePaths.any((path) => path != null && path.isNotEmpty); | 43 | + customImagePaths.every((path) => path != null && path.isNotEmpty); |
| 36 | 44 | ||
| 37 | Future<void> pickCustomImage(int index) async { | 45 | Future<void> pickCustomImage(int index) async { |
| 38 | try { | 46 | try { |
| @@ -81,12 +89,31 @@ class CreateWatchThemeController extends GetxController { | @@ -81,12 +89,31 @@ class CreateWatchThemeController extends GetxController { | ||
| 81 | agreedToSubmission.toggle(); | 89 | agreedToSubmission.toggle(); |
| 82 | } | 90 | } |
| 83 | 91 | ||
| 84 | - void saveCustomTheme() { | 92 | + Future<void> saveCustomTheme() async { |
| 85 | if (!canSaveCustomTheme) { | 93 | if (!canSaveCustomTheme) { |
| 86 | return; | 94 | return; |
| 87 | } | 95 | } |
| 96 | + | ||
| 97 | + isSaving.value = true; | ||
| 98 | + final result = await _themeApi.createTheme( | ||
| 99 | + name: customThemeName.value, | ||
| 100 | + fullOfEnergyName: customStatusNames[0], | ||
| 101 | + normalName: customStatusNames[1], | ||
| 102 | + overpressureName: customStatusNames[3], | ||
| 103 | + fullOfEnergyImage: customImagePaths[0] ?? '', | ||
| 104 | + normalImage: customImagePaths[1] ?? '', | ||
| 105 | + overpressureImage: customImagePaths[3] ?? customImagePaths[2] ?? '', | ||
| 106 | + ); | ||
| 107 | + isSaving.value = false; | ||
| 108 | + | ||
| 109 | + if (result is! AppSuccess<void>) { | ||
| 110 | + AppToast.show('主题保存失败'); | ||
| 111 | + return; | ||
| 112 | + } | ||
| 113 | + | ||
| 114 | + final createdTheme = await _loadCreatedTheme(); | ||
| 88 | Get.toNamed(Routes.WATCH_THEME_CUSTOM_PREVIEW, arguments: { | 115 | Get.toNamed(Routes.WATCH_THEME_CUSTOM_PREVIEW, arguments: { |
| 89 | - 'theme': _buildThemeItem(), | 116 | + 'theme': createdTheme ?? _buildThemeItem(), |
| 90 | }); | 117 | }); |
| 91 | } | 118 | } |
| 92 | 119 | ||
| @@ -103,4 +130,16 @@ class CreateWatchThemeController extends GetxController { | @@ -103,4 +130,16 @@ class CreateWatchThemeController extends GetxController { | ||
| 103 | ], | 130 | ], |
| 104 | ); | 131 | ); |
| 105 | } | 132 | } |
| 133 | + | ||
| 134 | + Future<WatchThemeItem?> _loadCreatedTheme() async { | ||
| 135 | + final result = await _themeApi.getThemeList(errorHandlingPolicy: null); | ||
| 136 | + if (result is! AppSuccess<WatchThemeResponse>) { | ||
| 137 | + return null; | ||
| 138 | + } | ||
| 139 | + final matchedThemes = result.data.themes | ||
| 140 | + .where((theme) => theme.title == customThemeName.value) | ||
| 141 | + .toList() | ||
| 142 | + ..sort((a, b) => (b.createTime ?? 0).compareTo(a.createTime ?? 0)); | ||
| 143 | + return matchedThemes.firstOrNull; | ||
| 144 | + } | ||
| 106 | } | 145 | } |
| 1 | +import 'dart:convert'; | ||
| 2 | + | ||
| 3 | +import 'package:doublefeel_flutter/core/network/api/theme_api.dart'; | ||
| 4 | +import 'package:doublefeel_flutter/core/result/app_result.dart'; | ||
| 5 | +import 'package:doublefeel_flutter/core/util/app_toast.dart'; | ||
| 6 | +import 'package:doublefeel_flutter/pigeon/wear_engine_api.g.dart'; | ||
| 1 | import 'package:get/get.dart'; | 7 | import 'package:get/get.dart'; |
| 2 | 8 | ||
| 3 | import '../models/watch_theme_models.dart'; | 9 | import '../models/watch_theme_models.dart'; |
| 4 | import '../widgets/watch_theme_dialogs.dart'; | 10 | import '../widgets/watch_theme_dialogs.dart'; |
| 5 | 11 | ||
| 6 | class CustomWatchThemePreviewController extends GetxController { | 12 | class CustomWatchThemePreviewController extends GetxController { |
| 13 | + CustomWatchThemePreviewController(this._themeApi); | ||
| 14 | + | ||
| 15 | + final ThemeApi _themeApi; | ||
| 16 | + | ||
| 7 | late final WatchThemeItem themeItem; | 17 | late final WatchThemeItem themeItem; |
| 18 | + final isApplying = false.obs; | ||
| 19 | + final isDeleting = false.obs; | ||
| 8 | 20 | ||
| 9 | @override | 21 | @override |
| 10 | void onInit() { | 22 | void onInit() { |
| @@ -40,11 +52,46 @@ class CustomWatchThemePreviewController extends GetxController { | @@ -40,11 +52,46 @@ class CustomWatchThemePreviewController extends GetxController { | ||
| 40 | barrierDismissible: true, | 52 | barrierDismissible: true, |
| 41 | ); | 53 | ); |
| 42 | if (shouldDelete == true) { | 54 | if (shouldDelete == true) { |
| 55 | + final themeId = themeItem.id; | ||
| 56 | + if (themeId == null) { | ||
| 57 | + Get.back(); | ||
| 58 | + return; | ||
| 59 | + } | ||
| 60 | + isDeleting.value = true; | ||
| 61 | + final result = await _themeApi.deleteTheme(themeId); | ||
| 62 | + isDeleting.value = false; | ||
| 63 | + if (result is! AppSuccess<void>) { | ||
| 64 | + AppToast.show('删除主题失败'); | ||
| 65 | + return; | ||
| 66 | + } | ||
| 67 | + AppToast.show('已删除主题'); | ||
| 43 | Get.back(); | 68 | Get.back(); |
| 44 | } | 69 | } |
| 45 | } | 70 | } |
| 46 | 71 | ||
| 47 | - void addWatchFace() { | ||
| 48 | - // Hook to NativeHostApiStubs when the native watch-face install flow is ready. | 72 | + Future<void> addWatchFace() async { |
| 73 | + final themeId = themeItem.id; | ||
| 74 | + if (themeId == null) { | ||
| 75 | + AppToast.show('主题信息不完整'); | ||
| 76 | + return; | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + isApplying.value = true; | ||
| 80 | + final result = await _themeApi.applyTheme(themeId); | ||
| 81 | + if (result is! AppSuccess<void>) { | ||
| 82 | + isApplying.value = false; | ||
| 83 | + AppToast.show('应用主题失败'); | ||
| 84 | + return; | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + try { | ||
| 88 | + await WearEngineHostApi().sendWatchSyncPayload( | ||
| 89 | + jsonEncode(themeItem.toJson()), | ||
| 90 | + ); | ||
| 91 | + } catch (_) { | ||
| 92 | + // The active theme is already saved on the server. | ||
| 93 | + } | ||
| 94 | + isApplying.value = false; | ||
| 95 | + AppToast.show('已应用主题'); | ||
| 49 | } | 96 | } |
| 50 | } | 97 | } |
| 1 | import 'package:doublefeel_flutter/app/routes/app_pages.dart'; | 1 | import 'package:doublefeel_flutter/app/routes/app_pages.dart'; |
| 2 | +import 'package:doublefeel_flutter/core/network/api/theme_api.dart'; | ||
| 3 | +import 'package:doublefeel_flutter/core/result/app_result.dart'; | ||
| 2 | import 'package:get/get.dart'; | 4 | import 'package:get/get.dart'; |
| 3 | 5 | ||
| 4 | import '../models/watch_theme_models.dart'; | 6 | import '../models/watch_theme_models.dart'; |
| 5 | 7 | ||
| 6 | class WatchThemeController extends GetxController { | 8 | class WatchThemeController extends GetxController { |
| 9 | + WatchThemeController(this._themeApi); | ||
| 10 | + | ||
| 11 | + final ThemeApi _themeApi; | ||
| 12 | + | ||
| 7 | final selectedOfficialIndex = 0.obs; | 13 | final selectedOfficialIndex = 0.obs; |
| 8 | final hasCustomThemes = false.obs; | 14 | final hasCustomThemes = false.obs; |
| 9 | final isPremium = false.obs; | 15 | final isPremium = false.obs; |
| 16 | + final isLoadingThemes = false.obs; | ||
| 17 | + final officialThemeItems = <WatchThemeItem>[...officialThemes].obs; | ||
| 18 | + final customThemeItems = <WatchThemeItem>[...customThemes].obs; | ||
| 10 | 19 | ||
| 11 | @override | 20 | @override |
| 12 | void onInit() { | 21 | void onInit() { |
| @@ -16,6 +25,35 @@ class WatchThemeController extends GetxController { | @@ -16,6 +25,35 @@ class WatchThemeController extends GetxController { | ||
| 16 | hasCustomThemes.value = args['hasCustomThemes'] == true; | 25 | hasCustomThemes.value = args['hasCustomThemes'] == true; |
| 17 | isPremium.value = args['isPremium'] == true || hasCustomThemes.value; | 26 | isPremium.value = args['isPremium'] == true || hasCustomThemes.value; |
| 18 | } | 27 | } |
| 28 | + loadThemeList(); | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + Future<void> loadThemeList() async { | ||
| 32 | + isLoadingThemes.value = true; | ||
| 33 | + final result = await _themeApi.getThemeList(errorHandlingPolicy: null); | ||
| 34 | + isLoadingThemes.value = false; | ||
| 35 | + | ||
| 36 | + if (result is! AppSuccess<WatchThemeResponse>) { | ||
| 37 | + hasCustomThemes.value = customThemeItems.isNotEmpty; | ||
| 38 | + isPremium.value = isPremium.value || hasCustomThemes.value; | ||
| 39 | + return; | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + final themes = result.data.themes; | ||
| 43 | + if (themes.isEmpty) { | ||
| 44 | + hasCustomThemes.value = customThemeItems.isNotEmpty; | ||
| 45 | + isPremium.value = isPremium.value || hasCustomThemes.value; | ||
| 46 | + return; | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + final official = themes.where((theme) => !theme.isCustomTheme).toList(); | ||
| 50 | + final custom = themes.where((theme) => theme.isCustomTheme).toList(); | ||
| 51 | + if (official.isNotEmpty) { | ||
| 52 | + officialThemeItems.assignAll(official); | ||
| 53 | + } | ||
| 54 | + customThemeItems.assignAll(custom); | ||
| 55 | + hasCustomThemes.value = custom.isNotEmpty; | ||
| 56 | + isPremium.value = isPremium.value || hasCustomThemes.value; | ||
| 19 | } | 57 | } |
| 20 | 58 | ||
| 21 | void executeBackLogic() { | 59 | void executeBackLogic() { |
| @@ -25,7 +63,7 @@ class WatchThemeController extends GetxController { | @@ -25,7 +63,7 @@ class WatchThemeController extends GetxController { | ||
| 25 | void selectOfficialTheme(int index) { | 63 | void selectOfficialTheme(int index) { |
| 26 | selectedOfficialIndex.value = index; | 64 | selectedOfficialIndex.value = index; |
| 27 | Get.toNamed(Routes.WATCH_THEME_PREVIEW, arguments: { | 65 | Get.toNamed(Routes.WATCH_THEME_PREVIEW, arguments: { |
| 28 | - 'theme': officialThemes[index], | 66 | + 'theme': officialThemeItems[index], |
| 29 | }); | 67 | }); |
| 30 | } | 68 | } |
| 31 | 69 |
| 1 | +import 'dart:convert'; | ||
| 2 | + | ||
| 3 | +import 'package:doublefeel_flutter/core/network/api/theme_api.dart'; | ||
| 4 | +import 'package:doublefeel_flutter/core/result/app_result.dart'; | ||
| 5 | +import 'package:doublefeel_flutter/core/util/app_toast.dart'; | ||
| 6 | +import 'package:doublefeel_flutter/pigeon/wear_engine_api.g.dart'; | ||
| 1 | import 'package:get/get.dart'; | 7 | import 'package:get/get.dart'; |
| 2 | 8 | ||
| 3 | import '../models/watch_theme_models.dart'; | 9 | import '../models/watch_theme_models.dart'; |
| 4 | 10 | ||
| 5 | class WatchThemePreviewController extends GetxController { | 11 | class WatchThemePreviewController extends GetxController { |
| 12 | + WatchThemePreviewController(this._themeApi); | ||
| 13 | + | ||
| 14 | + final ThemeApi _themeApi; | ||
| 15 | + | ||
| 6 | late final WatchThemeItem themeItem; | 16 | late final WatchThemeItem themeItem; |
| 17 | + final isApplying = false.obs; | ||
| 7 | 18 | ||
| 8 | @override | 19 | @override |
| 9 | void onInit() { | 20 | void onInit() { |
| @@ -26,7 +37,29 @@ class WatchThemePreviewController extends GetxController { | @@ -26,7 +37,29 @@ class WatchThemePreviewController extends GetxController { | ||
| 26 | Get.back(); | 37 | Get.back(); |
| 27 | } | 38 | } |
| 28 | 39 | ||
| 29 | - void addWatchFace() { | ||
| 30 | - // Hook to NativeHostApiStubs when the native watch-face install flow is ready. | 40 | + Future<void> addWatchFace() async { |
| 41 | + final themeId = themeItem.id; | ||
| 42 | + if (themeId == null) { | ||
| 43 | + AppToast.show('主题信息不完整'); | ||
| 44 | + return; | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + isApplying.value = true; | ||
| 48 | + final result = await _themeApi.applyTheme(themeId); | ||
| 49 | + if (result is! AppSuccess<void>) { | ||
| 50 | + isApplying.value = false; | ||
| 51 | + AppToast.show('应用主题失败'); | ||
| 52 | + return; | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + try { | ||
| 56 | + await WearEngineHostApi().sendWatchSyncPayload( | ||
| 57 | + jsonEncode(themeItem.toJson()), | ||
| 58 | + ); | ||
| 59 | + } catch (_) { | ||
| 60 | + // The server state has been updated; watch sync can be retried later. | ||
| 61 | + } | ||
| 62 | + isApplying.value = false; | ||
| 63 | + AppToast.show('已应用主题'); | ||
| 31 | } | 64 | } |
| 32 | } | 65 | } |
| 1 | +import 'dart:ui'; | ||
| 2 | + | ||
| 3 | +import 'package:doublefeel_flutter/app/modules/watch_theme/widgets/watch_theme_colors.dart'; | ||
| 4 | + | ||
| 1 | class WatchThemeItem { | 5 | class WatchThemeItem { |
| 2 | const WatchThemeItem({ | 6 | const WatchThemeItem({ |
| 3 | this.id, | 7 | this.id, |
| 8 | + this.createTime, | ||
| 9 | + this.updateTime, | ||
| 10 | + this.userId, | ||
| 11 | + this.status, | ||
| 4 | this.isCustomTheme = false, | 12 | this.isCustomTheme = false, |
| 5 | this.isDefaultCharacter = false, | 13 | this.isDefaultCharacter = false, |
| 6 | required this.title, | 14 | required this.title, |
| @@ -8,11 +16,76 @@ class WatchThemeItem { | @@ -8,11 +16,76 @@ class WatchThemeItem { | ||
| 8 | }); | 16 | }); |
| 9 | 17 | ||
| 10 | final int? id; | 18 | final int? id; |
| 19 | + final int? createTime; | ||
| 20 | + final int? updateTime; | ||
| 21 | + final int? userId; | ||
| 22 | + final int? status; | ||
| 11 | final bool isDefaultCharacter; | 23 | final bool isDefaultCharacter; |
| 12 | final bool isCustomTheme; | 24 | final bool isCustomTheme; |
| 13 | final String title; | 25 | final String title; |
| 14 | final List<WatchThemeItemInfo> infoList; | 26 | final List<WatchThemeItemInfo> infoList; |
| 15 | 27 | ||
| 28 | + factory WatchThemeItem.fromJson(Map<String, dynamic> json) { | ||
| 29 | + final isDefaultTheme = json['is_default_theme'] == true; | ||
| 30 | + final userId = json['user_id'] as int?; | ||
| 31 | + final positiveDescription = json['positive_description'] as String?; | ||
| 32 | + final normalDescription = json['normal_description'] as String?; | ||
| 33 | + final negativeDescription = json['negative_description'] as String?; | ||
| 34 | + final positiveImage = json['positive_image'] as String?; | ||
| 35 | + final normalImage = json['normal_image'] as String?; | ||
| 36 | + final negativeImage = json['negative_image'] as String?; | ||
| 37 | + | ||
| 38 | + return WatchThemeItem( | ||
| 39 | + id: json['id'] as int?, | ||
| 40 | + createTime: json['create_time'] as int?, | ||
| 41 | + updateTime: json['update_time'] as int?, | ||
| 42 | + userId: userId, | ||
| 43 | + status: json['status'] as int?, | ||
| 44 | + isDefaultCharacter: isDefaultTheme, | ||
| 45 | + isCustomTheme: userId != null, | ||
| 46 | + title: (json['theme_name'] as String?) ?? '自定义主题', | ||
| 47 | + infoList: [ | ||
| 48 | + WatchThemeItemInfo( | ||
| 49 | + title: positiveDescription ?? '状态优秀', | ||
| 50 | + imgUrl: positiveImage, | ||
| 51 | + ), | ||
| 52 | + WatchThemeItemInfo( | ||
| 53 | + title: normalDescription ?? '状态正常', | ||
| 54 | + imgUrl: normalImage, | ||
| 55 | + ), | ||
| 56 | + WatchThemeItemInfo( | ||
| 57 | + title: negativeDescription ?? '注意压力', | ||
| 58 | + imgUrl: negativeImage, | ||
| 59 | + ), | ||
| 60 | + WatchThemeItemInfo( | ||
| 61 | + title: negativeDescription ?? '压力过载', | ||
| 62 | + imgUrl: negativeImage, | ||
| 63 | + ), | ||
| 64 | + ], | ||
| 65 | + ); | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + Map<String, dynamic> toJson() { | ||
| 69 | + final excellent = infoList.elementAtOrNull(0); | ||
| 70 | + final normal = infoList.elementAtOrNull(1); | ||
| 71 | + final overload = infoList.elementAtOrNull(3) ?? infoList.elementAtOrNull(2); | ||
| 72 | + final json = <String, dynamic>{}; | ||
| 73 | + if (id != null) json['id'] = id; | ||
| 74 | + if (createTime != null) json['create_time'] = createTime; | ||
| 75 | + if (updateTime != null) json['update_time'] = updateTime; | ||
| 76 | + if (userId != null) json['user_id'] = userId; | ||
| 77 | + if (status != null) json['status'] = status; | ||
| 78 | + json['theme_name'] = title; | ||
| 79 | + json['is_default_theme'] = isDefaultCharacter; | ||
| 80 | + json['positive_description'] = excellent?.title; | ||
| 81 | + json['positive_image'] = excellent?.image; | ||
| 82 | + json['normal_description'] = normal?.title; | ||
| 83 | + json['normal_image'] = normal?.image; | ||
| 84 | + json['negative_description'] = overload?.title; | ||
| 85 | + json['negative_image'] = overload?.image; | ||
| 86 | + return json; | ||
| 87 | + } | ||
| 88 | + | ||
| 16 | static WatchThemeItem empty() { | 89 | static WatchThemeItem empty() { |
| 17 | return WatchThemeItem( | 90 | return WatchThemeItem( |
| 18 | title: '自定义主题', | 91 | title: '自定义主题', |
| @@ -27,6 +100,63 @@ class WatchThemeItemInfo { | @@ -27,6 +100,63 @@ class WatchThemeItemInfo { | ||
| 27 | final String? assetPath; | 100 | final String? assetPath; |
| 28 | 101 | ||
| 29 | WatchThemeItemInfo({required this.title, this.imgUrl, this.assetPath}); | 102 | WatchThemeItemInfo({required this.title, this.imgUrl, this.assetPath}); |
| 103 | + | ||
| 104 | + String? get image => imgUrl ?? assetPath; | ||
| 105 | +} | ||
| 106 | + | ||
| 107 | +class WatchThemeResponse { | ||
| 108 | + const WatchThemeResponse({this.themes = const []}); | ||
| 109 | + | ||
| 110 | + final List<WatchThemeItem> themes; | ||
| 111 | + | ||
| 112 | + factory WatchThemeResponse.fromJson(Map<String, dynamic> json) { | ||
| 113 | + final rawThemes = json['themes']; | ||
| 114 | + return WatchThemeResponse( | ||
| 115 | + themes: rawThemes is List | ||
| 116 | + ? rawThemes | ||
| 117 | + .whereType<Map>() | ||
| 118 | + .map((theme) => WatchThemeItem.fromJson( | ||
| 119 | + Map<String, dynamic>.from(theme), | ||
| 120 | + )) | ||
| 121 | + .toList() | ||
| 122 | + : const [], | ||
| 123 | + ); | ||
| 124 | + } | ||
| 125 | +} | ||
| 126 | + | ||
| 127 | +class WatchThemeUpsertRequest { | ||
| 128 | + const WatchThemeUpsertRequest({ | ||
| 129 | + this.themeId, | ||
| 130 | + required this.name, | ||
| 131 | + required this.fullOfEnergyName, | ||
| 132 | + required this.normalName, | ||
| 133 | + required this.overpressureName, | ||
| 134 | + required this.fullOfEnergyImage, | ||
| 135 | + required this.normalImage, | ||
| 136 | + required this.overpressureImage, | ||
| 137 | + }); | ||
| 138 | + | ||
| 139 | + final int? themeId; | ||
| 140 | + final String name; | ||
| 141 | + final String fullOfEnergyName; | ||
| 142 | + final String normalName; | ||
| 143 | + final String overpressureName; | ||
| 144 | + final String fullOfEnergyImage; | ||
| 145 | + final String normalImage; | ||
| 146 | + final String overpressureImage; | ||
| 147 | + | ||
| 148 | + Map<String, dynamic> toJson() { | ||
| 149 | + return { | ||
| 150 | + if (themeId != null) 'theme_id': themeId, | ||
| 151 | + 'theme_name': name, | ||
| 152 | + 'positive_description': fullOfEnergyName, | ||
| 153 | + 'positive_image': fullOfEnergyImage, | ||
| 154 | + 'normal_description': normalName, | ||
| 155 | + 'normal_image': normalImage, | ||
| 156 | + 'negative_description': overpressureName, | ||
| 157 | + 'negative_image': overpressureImage, | ||
| 158 | + }; | ||
| 159 | + } | ||
| 30 | } | 160 | } |
| 31 | 161 | ||
| 32 | final officialThemes = <WatchThemeItem>[ | 162 | final officialThemes = <WatchThemeItem>[ |
| @@ -161,3 +291,17 @@ final customThemes = <WatchThemeItem>[ | @@ -161,3 +291,17 @@ final customThemes = <WatchThemeItem>[ | ||
| 161 | ], | 291 | ], |
| 162 | ), | 292 | ), |
| 163 | ]; | 293 | ]; |
| 294 | + | ||
| 295 | +final defaultThemeTemplates = [ | ||
| 296 | + WatchThemeTemplateItem(title: '状态优秀', color: WatchThemeColors.excellent), | ||
| 297 | + WatchThemeTemplateItem(title: '状态正常', color: WatchThemeColors.normal), | ||
| 298 | + WatchThemeTemplateItem(title: '注意压力', color: WatchThemeColors.stress), | ||
| 299 | + WatchThemeTemplateItem(title: '压力过载', color: WatchThemeColors.overload), | ||
| 300 | +]; | ||
| 301 | + | ||
| 302 | +class WatchThemeTemplateItem { | ||
| 303 | + final String title; | ||
| 304 | + final Color color; | ||
| 305 | + | ||
| 306 | + WatchThemeTemplateItem({required this.title, required this.color}); | ||
| 307 | +} |
| @@ -59,7 +59,7 @@ class CreateWatchThemeView extends GetView<CreateWatchThemeController> { | @@ -59,7 +59,7 @@ class CreateWatchThemeView extends GetView<CreateWatchThemeController> { | ||
| 59 | ), | 59 | ), |
| 60 | Expanded( | 60 | Expanded( |
| 61 | child: SingleChildScrollView( | 61 | child: SingleChildScrollView( |
| 62 | - physics: const ClampingScrollPhysics(), | 62 | + physics: const AlwaysScrollableScrollPhysics(), |
| 63 | padding: EdgeInsets.only(bottom: 28.dp), | 63 | padding: EdgeInsets.only(bottom: 28.dp), |
| 64 | child: Column( | 64 | child: Column( |
| 65 | children: [ | 65 | children: [ |
| @@ -130,8 +130,7 @@ class _EditorCard extends StatelessWidget { | @@ -130,8 +130,7 @@ class _EditorCard extends StatelessWidget { | ||
| 130 | ), | 130 | ), |
| 131 | ), | 131 | ), |
| 132 | SizedBox(height: 14.dp), | 132 | SizedBox(height: 14.dp), |
| 133 | - Obx( | ||
| 134 | - () => GridView.builder( | 133 | + GridView.builder( |
| 135 | shrinkWrap: true, | 134 | shrinkWrap: true, |
| 136 | padding: EdgeInsets.zero, | 135 | padding: EdgeInsets.zero, |
| 137 | physics: const NeverScrollableScrollPhysics(), | 136 | physics: const NeverScrollableScrollPhysics(), |
| @@ -143,16 +142,17 @@ class _EditorCard extends StatelessWidget { | @@ -143,16 +142,17 @@ class _EditorCard extends StatelessWidget { | ||
| 143 | mainAxisExtent: 170.dp, | 142 | mainAxisExtent: 170.dp, |
| 144 | ), | 143 | ), |
| 145 | itemBuilder: (context, index) { | 144 | itemBuilder: (context, index) { |
| 146 | - return _UploadTile( | 145 | + return Obx( |
| 146 | + () => _UploadTile( | ||
| 147 | index: index, | 147 | index: index, |
| 148 | color: _statusColors[index], | 148 | color: _statusColors[index], |
| 149 | label: controller.customStatusNames[index], | 149 | label: controller.customStatusNames[index], |
| 150 | path: controller.customImagePaths[index], | 150 | path: controller.customImagePaths[index], |
| 151 | onPick: () => controller.pickCustomImage(index), | 151 | onPick: () => controller.pickCustomImage(index), |
| 152 | onRename: () => controller.showRenameStatusDialog(index), | 152 | onRename: () => controller.showRenameStatusDialog(index), |
| 153 | + ), | ||
| 153 | ); | 154 | ); |
| 154 | - }, | ||
| 155 | - ), | 155 | + }, |
| 156 | ), | 156 | ), |
| 157 | SizedBox(height: 14.dp), | 157 | SizedBox(height: 14.dp), |
| 158 | Text( | 158 | Text( |
| @@ -235,6 +235,9 @@ class _UploadTile extends StatelessWidget { | @@ -235,6 +235,9 @@ class _UploadTile extends StatelessWidget { | ||
| 235 | ), | 235 | ), |
| 236 | ), | 236 | ), |
| 237 | SizedBox(height: 10.dp), | 237 | SizedBox(height: 10.dp), |
| 238 | + GestureDetector( | ||
| 239 | + onTap: onRename, | ||
| 240 | + child: | ||
| 238 | Row( | 241 | Row( |
| 239 | mainAxisAlignment: MainAxisAlignment.center, | 242 | mainAxisAlignment: MainAxisAlignment.center, |
| 240 | children: [ | 243 | children: [ |
| @@ -246,16 +249,14 @@ class _UploadTile extends StatelessWidget { | @@ -246,16 +249,14 @@ class _UploadTile extends StatelessWidget { | ||
| 246 | ), | 249 | ), |
| 247 | ), | 250 | ), |
| 248 | SizedBox(width: 4.dp), | 251 | SizedBox(width: 4.dp), |
| 249 | - GestureDetector( | ||
| 250 | - onTap: onRename, | ||
| 251 | - child: Icon( | 252 | + Icon( |
| 252 | Icons.edit_outlined, | 253 | Icons.edit_outlined, |
| 253 | color: const Color(0xFFA084EF), | 254 | color: const Color(0xFFA084EF), |
| 254 | size: 14.dp, | 255 | size: 14.dp, |
| 255 | ), | 256 | ), |
| 256 | - ), | ||
| 257 | ], | 257 | ], |
| 258 | ), | 258 | ), |
| 259 | + ), | ||
| 259 | ], | 260 | ], |
| 260 | ); | 261 | ); |
| 261 | } | 262 | } |
| @@ -317,7 +318,7 @@ class _SaveButton extends StatelessWidget { | @@ -317,7 +318,7 @@ class _SaveButton extends StatelessWidget { | ||
| 317 | return Opacity( | 318 | return Opacity( |
| 318 | opacity: enabled ? 1 : 0.4, | 319 | opacity: enabled ? 1 : 0.4, |
| 319 | child: GestureDetector( | 320 | child: GestureDetector( |
| 320 | - onTap: enabled ? controller.saveCustomTheme : null, | 321 | + onTap: enabled ? () => controller.saveCustomTheme() : null, |
| 321 | child: Container( | 322 | child: Container( |
| 322 | width: 280.dp, | 323 | width: 280.dp, |
| 323 | height: 48.dp, | 324 | height: 48.dp, |
| @@ -327,7 +328,7 @@ class _SaveButton extends StatelessWidget { | @@ -327,7 +328,7 @@ class _SaveButton extends StatelessWidget { | ||
| 327 | borderRadius: BorderRadius.circular(24.dp), | 328 | borderRadius: BorderRadius.circular(24.dp), |
| 328 | ), | 329 | ), |
| 329 | child: Text( | 330 | child: Text( |
| 330 | - '保存主题', | 331 | + controller.isSaving.value ? '保存中' : '保存主题', |
| 331 | style: TextStyle( | 332 | style: TextStyle( |
| 332 | color: Colors.white, | 333 | color: Colors.white, |
| 333 | fontSize: 16.dp, | 334 | fontSize: 16.dp, |
| 1 | +import 'package:doublefeel_flutter/app/modules/watch_theme/models/watch_theme_models.dart'; | ||
| 1 | import 'package:doublefeel_flutter/core/util/size_extensions.dart'; | 2 | import 'package:doublefeel_flutter/core/util/size_extensions.dart'; |
| 2 | import 'package:doublefeel_flutter/r.dart'; | 3 | import 'package:doublefeel_flutter/r.dart'; |
| 3 | import 'package:flutter/material.dart'; | 4 | import 'package:flutter/material.dart'; |
| @@ -114,12 +115,6 @@ class _CustomStatusPreviewCard extends StatelessWidget { | @@ -114,12 +115,6 @@ class _CustomStatusPreviewCard extends StatelessWidget { | ||
| 114 | R.assetsImagesWatchThemeCustomStatusOverload, | 115 | R.assetsImagesWatchThemeCustomStatusOverload, |
| 115 | ]; | 116 | ]; |
| 116 | 117 | ||
| 117 | - static const _colors = [ | ||
| 118 | - WatchThemeColors.excellent, | ||
| 119 | - WatchThemeColors.normal, | ||
| 120 | - WatchThemeColors.stress, | ||
| 121 | - WatchThemeColors.overload, | ||
| 122 | - ]; | ||
| 123 | 118 | ||
| 124 | @override | 119 | @override |
| 125 | Widget build(BuildContext context) { | 120 | Widget build(BuildContext context) { |
| @@ -150,7 +145,7 @@ class _CustomStatusPreviewCard extends StatelessWidget { | @@ -150,7 +145,7 @@ class _CustomStatusPreviewCard extends StatelessWidget { | ||
| 150 | Text( | 145 | Text( |
| 151 | i < infoList.length ? infoList[i].title : '', | 146 | i < infoList.length ? infoList[i].title : '', |
| 152 | style: TextStyle( | 147 | style: TextStyle( |
| 153 | - color: _colors[i], | 148 | + color: defaultThemeTemplates[i].color, |
| 154 | fontSize: 12.dp, | 149 | fontSize: 12.dp, |
| 155 | ), | 150 | ), |
| 156 | ), | 151 | ), |
| 1 | -import 'package:doublefeel_flutter/app/modules/watch_theme/models/watch_theme_models.dart'; | ||
| 2 | import 'package:flutter/material.dart'; | 1 | import 'package:flutter/material.dart'; |
| 3 | import 'package:flutter/services.dart'; | 2 | import 'package:flutter/services.dart'; |
| 4 | import 'package:get/get.dart'; | 3 | import 'package:get/get.dart'; |
| @@ -63,7 +62,7 @@ class WatchThemeView extends GetView<WatchThemeController> { | @@ -63,7 +62,7 @@ class WatchThemeView extends GetView<WatchThemeController> { | ||
| 63 | const WatchThemeHeader(), | 62 | const WatchThemeHeader(), |
| 64 | const SizedBox(height: 26), | 63 | const SizedBox(height: 26), |
| 65 | OfficialThemeGrid( | 64 | OfficialThemeGrid( |
| 66 | - themes: officialThemes, | 65 | + themes: controller.officialThemeItems, |
| 67 | selectedIndex: | 66 | selectedIndex: |
| 68 | controller.selectedOfficialIndex.value, | 67 | controller.selectedOfficialIndex.value, |
| 69 | onThemeTap: controller.selectOfficialTheme, | 68 | onThemeTap: controller.selectOfficialTheme, |
| @@ -72,7 +71,7 @@ class WatchThemeView extends GetView<WatchThemeController> { | @@ -72,7 +71,7 @@ class WatchThemeView extends GetView<WatchThemeController> { | ||
| 72 | CustomThemeCard( | 71 | CustomThemeCard( |
| 73 | isPremium: controller.isPremium.value, | 72 | isPremium: controller.isPremium.value, |
| 74 | hasCustomThemes: controller.hasCustomThemes.value, | 73 | hasCustomThemes: controller.hasCustomThemes.value, |
| 75 | - customThemes: customThemes, | 74 | + customThemes: controller.customThemeItems, |
| 76 | onCreateTap: controller.createCustomTheme, | 75 | onCreateTap: controller.createCustomTheme, |
| 77 | onThemeTap: controller.previewCustomTheme, | 76 | onThemeTap: controller.previewCustomTheme, |
| 78 | ), | 77 | ), |
| @@ -20,29 +20,34 @@ class StatusPreviewCard extends StatelessWidget { | @@ -20,29 +20,34 @@ class StatusPreviewCard extends StatelessWidget { | ||
| 20 | SizedBox(height: 24.dp), | 20 | SizedBox(height: 24.dp), |
| 21 | Row( | 21 | Row( |
| 22 | mainAxisAlignment: MainAxisAlignment.spaceBetween, | 22 | mainAxisAlignment: MainAxisAlignment.spaceBetween, |
| 23 | - children: [ | ||
| 24 | - for (final item in themeItem.infoList) | ||
| 25 | - Column( | ||
| 26 | - children: [ | ||
| 27 | - WatchThemeCharacterAvatar( | ||
| 28 | - size: 60, | ||
| 29 | - assetPath: item.assetPath, | ||
| 30 | - ), | ||
| 31 | - SizedBox(height: 4.dp), | ||
| 32 | - Text( | ||
| 33 | - item.title, | ||
| 34 | - style: TextStyle( | ||
| 35 | - fontSize: 12.dp, | ||
| 36 | - fontWeight: FontWeight.w400, | ||
| 37 | - height: 1.25, | ||
| 38 | - ), | ||
| 39 | - ), | ||
| 40 | - ], | ||
| 41 | - ), | ||
| 42 | - ], | 23 | + children: _previewItemView(), |
| 43 | ), | 24 | ), |
| 44 | ], | 25 | ], |
| 45 | ), | 26 | ), |
| 46 | ); | 27 | ); |
| 47 | } | 28 | } |
| 29 | + | ||
| 30 | + List<Widget> _previewItemView() { | ||
| 31 | + final infoList = themeItem.infoList; | ||
| 32 | + return [ | ||
| 33 | + for (var i = 0; i < infoList.length; i++) | ||
| 34 | + Column( | ||
| 35 | + children: [ | ||
| 36 | + WatchThemeCharacterAvatar( | ||
| 37 | + size: 60, | ||
| 38 | + assetPath: infoList[i].assetPath, | ||
| 39 | + ), | ||
| 40 | + SizedBox(height: 4.dp), | ||
| 41 | + Text( | ||
| 42 | + i < infoList.length ? infoList[i].title : '', | ||
| 43 | + style: TextStyle( | ||
| 44 | + color: defaultThemeTemplates[i].color, | ||
| 45 | + fontSize: 12.dp, | ||
| 46 | + fontWeight: FontWeight.w400, | ||
| 47 | + ), | ||
| 48 | + ), | ||
| 49 | + ], | ||
| 50 | + ), | ||
| 51 | + ]; | ||
| 52 | + } | ||
| 48 | } | 53 | } |
| @@ -47,7 +47,7 @@ class WatchThemeNavBar extends StatelessWidget { | @@ -47,7 +47,7 @@ class WatchThemeNavBar extends StatelessWidget { | ||
| 47 | ), | 47 | ), |
| 48 | trailing == null | 48 | trailing == null |
| 49 | ? SizedBox( | 49 | ? SizedBox( |
| 50 | - width: 8.dp, | 50 | + width: 28.dp, |
| 51 | ) | 51 | ) |
| 52 | : Center(child: trailing) | 52 | : Center(child: trailing) |
| 53 | ], | 53 | ], |
lib/core/network/api/theme_api.dart
0 → 100644
| 1 | +import '../../../app/modules/watch_theme/models/watch_theme_models.dart'; | ||
| 2 | +import '../../error/http_error_handling_policy.dart'; | ||
| 3 | +import '../../result/app_result.dart'; | ||
| 4 | +import '../../result/safe_call.dart'; | ||
| 5 | +import '../api_paths.dart'; | ||
| 6 | +import '../dio_client.dart'; | ||
| 7 | + | ||
| 8 | +class ThemeApi { | ||
| 9 | + ThemeApi(this._dioClient); | ||
| 10 | + | ||
| 11 | + final DioClient _dioClient; | ||
| 12 | + | ||
| 13 | + Future<AppResult<void>> createTheme({ | ||
| 14 | + required String name, | ||
| 15 | + required String fullOfEnergyName, | ||
| 16 | + required String normalName, | ||
| 17 | + required String overpressureName, | ||
| 18 | + required String fullOfEnergyImage, | ||
| 19 | + required String normalImage, | ||
| 20 | + required String overpressureImage, | ||
| 21 | + }) { | ||
| 22 | + return safeCall( | ||
| 23 | + call: () async { | ||
| 24 | + await _dioClient.dio.post( | ||
| 25 | + ApiPaths.watchTheme, | ||
| 26 | + data: WatchThemeUpsertRequest( | ||
| 27 | + name: name, | ||
| 28 | + fullOfEnergyName: fullOfEnergyName, | ||
| 29 | + normalName: normalName, | ||
| 30 | + overpressureName: overpressureName, | ||
| 31 | + fullOfEnergyImage: fullOfEnergyImage, | ||
| 32 | + normalImage: normalImage, | ||
| 33 | + overpressureImage: overpressureImage, | ||
| 34 | + ).toJson(), | ||
| 35 | + ); | ||
| 36 | + }, | ||
| 37 | + ); | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + Future<AppResult<void>> editTheme({ | ||
| 41 | + required int themeId, | ||
| 42 | + required String name, | ||
| 43 | + required String fullOfEnergyName, | ||
| 44 | + required String normalName, | ||
| 45 | + required String overpressureName, | ||
| 46 | + required String fullOfEnergyImage, | ||
| 47 | + required String normalImage, | ||
| 48 | + required String overpressureImage, | ||
| 49 | + }) { | ||
| 50 | + return safeCall( | ||
| 51 | + call: () async { | ||
| 52 | + await _dioClient.dio.put( | ||
| 53 | + ApiPaths.watchTheme, | ||
| 54 | + data: WatchThemeUpsertRequest( | ||
| 55 | + themeId: themeId, | ||
| 56 | + name: name, | ||
| 57 | + fullOfEnergyName: fullOfEnergyName, | ||
| 58 | + normalName: normalName, | ||
| 59 | + overpressureName: overpressureName, | ||
| 60 | + fullOfEnergyImage: fullOfEnergyImage, | ||
| 61 | + normalImage: normalImage, | ||
| 62 | + overpressureImage: overpressureImage, | ||
| 63 | + ).toJson(), | ||
| 64 | + ); | ||
| 65 | + }, | ||
| 66 | + ); | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + Future<AppResult<WatchThemeItem>> getTheme({ | ||
| 70 | + required int themeId, | ||
| 71 | + HttpErrorHandlingPolicy? errorHandlingPolicy = | ||
| 72 | + HttpErrorHandlingPolicy.defaultPolicy, | ||
| 73 | + }) { | ||
| 74 | + return safeCall( | ||
| 75 | + call: () async { | ||
| 76 | + final response = await _dioClient.dio.get( | ||
| 77 | + ApiPaths.watchTheme, | ||
| 78 | + queryParameters: {'theme_id': themeId}, | ||
| 79 | + ); | ||
| 80 | + return WatchThemeItem.fromJson(response.data as Map<String, dynamic>); | ||
| 81 | + }, | ||
| 82 | + errorHandlingPolicy: errorHandlingPolicy, | ||
| 83 | + ); | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + Future<AppResult<WatchThemeResponse>> getThemeList({ | ||
| 87 | + HttpErrorHandlingPolicy? errorHandlingPolicy = | ||
| 88 | + HttpErrorHandlingPolicy.defaultPolicy, | ||
| 89 | + }) { | ||
| 90 | + return safeCall( | ||
| 91 | + call: () async { | ||
| 92 | + final response = await _dioClient.dio.get(ApiPaths.watchThemeList); | ||
| 93 | + return WatchThemeResponse.fromJson( | ||
| 94 | + response.data as Map<String, dynamic>, | ||
| 95 | + ); | ||
| 96 | + }, | ||
| 97 | + errorHandlingPolicy: errorHandlingPolicy, | ||
| 98 | + ); | ||
| 99 | + } | ||
| 100 | + | ||
| 101 | + Future<AppResult<void>> deleteTheme(int themeId) { | ||
| 102 | + return safeCall( | ||
| 103 | + call: () async { | ||
| 104 | + await _dioClient.dio.delete( | ||
| 105 | + ApiPaths.watchTheme, | ||
| 106 | + data: {'theme_id': themeId}, | ||
| 107 | + ); | ||
| 108 | + }, | ||
| 109 | + ); | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + Future<AppResult<WatchThemeItem>> getCurrentTheme({ | ||
| 113 | + bool? isOther, | ||
| 114 | + HttpErrorHandlingPolicy? errorHandlingPolicy = | ||
| 115 | + HttpErrorHandlingPolicy.defaultPolicy, | ||
| 116 | + }) { | ||
| 117 | + return safeCall( | ||
| 118 | + call: () async { | ||
| 119 | + final response = await _dioClient.dio.get( | ||
| 120 | + ApiPaths.watchThemeActive, | ||
| 121 | + queryParameters: { | ||
| 122 | + if (isOther != null) 'is_other': isOther ? 1 : 0, | ||
| 123 | + }, | ||
| 124 | + ); | ||
| 125 | + return WatchThemeItem.fromJson(response.data as Map<String, dynamic>); | ||
| 126 | + }, | ||
| 127 | + errorHandlingPolicy: errorHandlingPolicy, | ||
| 128 | + ); | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + Future<AppResult<void>> applyTheme(int themeId) { | ||
| 132 | + return safeCall( | ||
| 133 | + call: () async { | ||
| 134 | + await _dioClient.dio.post( | ||
| 135 | + ApiPaths.watchThemeActive, | ||
| 136 | + data: {'theme_id': themeId}, | ||
| 137 | + ); | ||
| 138 | + }, | ||
| 139 | + ); | ||
| 140 | + } | ||
| 141 | + | ||
| 142 | + // Keep compatibility with the original Swift ThemeEndpoint spelling. | ||
| 143 | + Future<AppResult<void>> appplyTheme(int themeId) => applyTheme(themeId); | ||
| 144 | +} |
| @@ -48,4 +48,10 @@ abstract final class ApiPaths { | @@ -48,4 +48,10 @@ abstract final class ApiPaths { | ||
| 48 | 48 | ||
| 49 | // VIP | 49 | // VIP |
| 50 | static const userVipInfo = '/client/doublefeel/user/vip/info/'; | 50 | static const userVipInfo = '/client/doublefeel/user/vip/info/'; |
| 51 | + | ||
| 52 | + // Watch theme | ||
| 53 | + static const watchTheme = '/client/doublefeel/theme/watch_theme/'; | ||
| 54 | + static const watchThemeList = '/client/doublefeel/theme/watch_theme/list/'; | ||
| 55 | + static const watchThemeActive = | ||
| 56 | + '/client/doublefeel/theme/watch_theme/active/'; | ||
| 51 | } | 57 | } |
| @@ -62,8 +62,7 @@ import 'app_localizations_zh.dart'; | @@ -62,8 +62,7 @@ import 'app_localizations_zh.dart'; | ||
| 62 | /// be consistent with the languages listed in the AppLocalizations.supportedLocales | 62 | /// be consistent with the languages listed in the AppLocalizations.supportedLocales |
| 63 | /// property. | 63 | /// property. |
| 64 | abstract class AppLocalizations { | 64 | abstract class AppLocalizations { |
| 65 | - AppLocalizations(String locale) | ||
| 66 | - : localeName = intl.Intl.canonicalizedLocale(locale.toString()); | 65 | + AppLocalizations(String locale) : localeName = intl.Intl.canonicalizedLocale(locale.toString()); |
| 67 | 66 | ||
| 68 | final String localeName; | 67 | final String localeName; |
| 69 | 68 | ||
| @@ -71,8 +70,7 @@ abstract class AppLocalizations { | @@ -71,8 +70,7 @@ abstract class AppLocalizations { | ||
| 71 | return Localizations.of<AppLocalizations>(context, AppLocalizations); | 70 | return Localizations.of<AppLocalizations>(context, AppLocalizations); |
| 72 | } | 71 | } |
| 73 | 72 | ||
| 74 | - static const LocalizationsDelegate<AppLocalizations> delegate = | ||
| 75 | - _AppLocalizationsDelegate(); | 73 | + static const LocalizationsDelegate<AppLocalizations> delegate = _AppLocalizationsDelegate(); |
| 76 | 74 | ||
| 77 | /// A list of this localizations delegate along with the default localizations | 75 | /// A list of this localizations delegate along with the default localizations |
| 78 | /// delegates. | 76 | /// delegates. |
| @@ -84,8 +82,7 @@ abstract class AppLocalizations { | @@ -84,8 +82,7 @@ abstract class AppLocalizations { | ||
| 84 | /// Additional delegates can be added by appending to this list in | 82 | /// Additional delegates can be added by appending to this list in |
| 85 | /// MaterialApp. This list does not have to be used at all if a custom list | 83 | /// MaterialApp. This list does not have to be used at all if a custom list |
| 86 | /// of delegates is preferred or required. | 84 | /// of delegates is preferred or required. |
| 87 | - static const List<LocalizationsDelegate<dynamic>> localizationsDelegates = | ||
| 88 | - <LocalizationsDelegate<dynamic>>[ | 85 | + static const List<LocalizationsDelegate<dynamic>> localizationsDelegates = <LocalizationsDelegate<dynamic>>[ |
| 89 | delegate, | 86 | delegate, |
| 90 | GlobalMaterialLocalizations.delegate, | 87 | GlobalMaterialLocalizations.delegate, |
| 91 | GlobalCupertinoLocalizations.delegate, | 88 | GlobalCupertinoLocalizations.delegate, |
| @@ -528,8 +525,7 @@ abstract class AppLocalizations { | @@ -528,8 +525,7 @@ abstract class AppLocalizations { | ||
| 528 | /// | 525 | /// |
| 529 | /// In zh, this message translates to: | 526 | /// In zh, this message translates to: |
| 530 | /// **'你们现在可以查看彼此的 HRV、压力与睡眠变化,并在对方疲惫时及时送上关心。'** | 527 | /// **'你们现在可以查看彼此的 HRV、压力与睡眠变化,并在对方疲惫时及时送上关心。'** |
| 531 | - String | ||
| 532 | - get youCanNowViewEachOtherSHrvStressLevelsAndSleepPatternsAndReachOutToCheckInWhenTheOtherPersonSeemsTired; | 528 | + String get youCanNowViewEachOtherSHrvStressLevelsAndSleepPatternsAndReachOutToCheckInWhenTheOtherPersonSeemsTired; |
| 533 | 529 | ||
| 534 | /// No description provided for @bindPartnerTitle. | 530 | /// No description provided for @bindPartnerTitle. |
| 535 | /// | 531 | /// |
| @@ -1738,8 +1734,7 @@ abstract class AppLocalizations { | @@ -1738,8 +1734,7 @@ abstract class AppLocalizations { | ||
| 1738 | String get dailyActions; | 1734 | String get dailyActions; |
| 1739 | } | 1735 | } |
| 1740 | 1736 | ||
| 1741 | -class _AppLocalizationsDelegate | ||
| 1742 | - extends LocalizationsDelegate<AppLocalizations> { | 1737 | +class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> { |
| 1743 | const _AppLocalizationsDelegate(); | 1738 | const _AppLocalizationsDelegate(); |
| 1744 | 1739 | ||
| 1745 | @override | 1740 | @override |
| @@ -1748,25 +1743,25 @@ class _AppLocalizationsDelegate | @@ -1748,25 +1743,25 @@ class _AppLocalizationsDelegate | ||
| 1748 | } | 1743 | } |
| 1749 | 1744 | ||
| 1750 | @override | 1745 | @override |
| 1751 | - bool isSupported(Locale locale) => | ||
| 1752 | - <String>['en', 'zh'].contains(locale.languageCode); | 1746 | + bool isSupported(Locale locale) => <String>['en', 'zh'].contains(locale.languageCode); |
| 1753 | 1747 | ||
| 1754 | @override | 1748 | @override |
| 1755 | bool shouldReload(_AppLocalizationsDelegate old) => false; | 1749 | bool shouldReload(_AppLocalizationsDelegate old) => false; |
| 1756 | } | 1750 | } |
| 1757 | 1751 | ||
| 1758 | AppLocalizations lookupAppLocalizations(Locale locale) { | 1752 | AppLocalizations lookupAppLocalizations(Locale locale) { |
| 1753 | + | ||
| 1754 | + | ||
| 1759 | // Lookup logic when only language code is specified. | 1755 | // Lookup logic when only language code is specified. |
| 1760 | switch (locale.languageCode) { | 1756 | switch (locale.languageCode) { |
| 1761 | - case 'en': | ||
| 1762 | - return AppLocalizationsEn(); | ||
| 1763 | - case 'zh': | ||
| 1764 | - return AppLocalizationsZh(); | 1757 | + case 'en': return AppLocalizationsEn(); |
| 1758 | + case 'zh': return AppLocalizationsZh(); | ||
| 1765 | } | 1759 | } |
| 1766 | 1760 | ||
| 1767 | throw FlutterError( | 1761 | throw FlutterError( |
| 1768 | - 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' | ||
| 1769 | - 'an issue with the localizations generation tool. Please file an issue ' | ||
| 1770 | - 'on GitHub with a reproducible sample app and the gen-l10n configuration ' | ||
| 1771 | - 'that was used.'); | 1762 | + 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' |
| 1763 | + 'an issue with the localizations generation tool. Please file an issue ' | ||
| 1764 | + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' | ||
| 1765 | + 'that was used.' | ||
| 1766 | + ); | ||
| 1772 | } | 1767 | } |
| 1 | -// ignore: unused_import | ||
| 2 | -import 'package:intl/intl.dart' as intl; | ||
| 3 | import 'app_localizations.dart'; | 1 | import 'app_localizations.dart'; |
| 4 | 2 | ||
| 5 | // ignore_for_file: type=lint | 3 | // ignore_for_file: type=lint |
| @@ -69,12 +67,10 @@ class AppLocalizationsEn extends AppLocalizations { | @@ -69,12 +67,10 @@ class AppLocalizationsEn extends AppLocalizations { | ||
| 69 | String get settings => 'Settings'; | 67 | String get settings => 'Settings'; |
| 70 | 68 | ||
| 71 | @override | 69 | @override |
| 72 | - String get onboardingIntroTitle => | ||
| 73 | - 'DoubleFeel is a health companion app built for Apple Watch'; | 70 | + String get onboardingIntroTitle => 'DoubleFeel is a health companion app built for Apple Watch'; |
| 74 | 71 | ||
| 75 | @override | 72 | @override |
| 76 | - String get onboardingIntroBody => | ||
| 77 | - 'We hope to help you\n<em>notice changes in your mind and body, and help the people who love you</em> see when you are <em>tired or need support</em>'; | 73 | + String get onboardingIntroBody => 'We hope to help you\n<em>notice changes in your mind and body, and help the people who love you</em> see when you are <em>tired or need support</em>'; |
| 78 | 74 | ||
| 79 | @override | 75 | @override |
| 80 | String get onboardingStateQuestion => 'Which of these often happens to you?'; | 76 | String get onboardingStateQuestion => 'Which of these often happens to you?'; |
| @@ -86,19 +82,16 @@ class AppLocalizationsEn extends AppLocalizations { | @@ -86,19 +82,16 @@ class AppLocalizationsEn extends AppLocalizations { | ||
| 86 | String get onboardingStateTired => 'I get tired easily'; | 82 | String get onboardingStateTired => 'I get tired easily'; |
| 87 | 83 | ||
| 88 | @override | 84 | @override |
| 89 | - String get onboardingStatePoorRest => | ||
| 90 | - 'I wake up but still do not feel rested'; | 85 | + String get onboardingStatePoorRest => 'I wake up but still do not feel rested'; |
| 91 | 86 | ||
| 92 | @override | 87 | @override |
| 93 | - String get onboardingStateNeedStimulants => | ||
| 94 | - 'I rely on cigarettes, alcohol, coffee, or other stimulants to stay alert'; | 88 | + String get onboardingStateNeedStimulants => 'I rely on cigarettes, alcohol, coffee, or other stimulants to stay alert'; |
| 95 | 89 | ||
| 96 | @override | 90 | @override |
| 97 | String get onboardingStateNone => 'None of the above'; | 91 | String get onboardingStateNone => 'None of the above'; |
| 98 | 92 | ||
| 99 | @override | 93 | @override |
| 100 | - String get onboardingStressGoalQuestion => | ||
| 101 | - 'What do you want to learn by understanding stress?'; | 94 | + String get onboardingStressGoalQuestion => 'What do you want to learn by understanding stress?'; |
| 102 | 95 | ||
| 103 | @override | 96 | @override |
| 104 | String get onboardingStressGoalSource => 'Understand where stress comes from'; | 97 | String get onboardingStressGoalSource => 'Understand where stress comes from'; |
| @@ -107,8 +100,7 @@ class AppLocalizationsEn extends AppLocalizations { | @@ -107,8 +100,7 @@ class AppLocalizationsEn extends AppLocalizations { | ||
| 107 | String get onboardingStressGoalReminder => 'Get reminded when stress appears'; | 100 | String get onboardingStressGoalReminder => 'Get reminded when stress appears'; |
| 108 | 101 | ||
| 109 | @override | 102 | @override |
| 110 | - String get onboardingStressGoalLovedOnes => | ||
| 111 | - 'Let people who care about me know my stress state'; | 103 | + String get onboardingStressGoalLovedOnes => 'Let people who care about me know my stress state'; |
| 112 | 104 | ||
| 113 | @override | 105 | @override |
| 114 | String get onboardingStressGoalRelax => 'Understand stress and feel lighter'; | 106 | String get onboardingStressGoalRelax => 'Understand stress and feel lighter'; |
| @@ -117,8 +109,7 @@ class AppLocalizationsEn extends AppLocalizations { | @@ -117,8 +109,7 @@ class AppLocalizationsEn extends AppLocalizations { | ||
| 117 | String get onboardingStressGoalBodyTalk => 'Communicate better with my body'; | 109 | String get onboardingStressGoalBodyTalk => 'Communicate better with my body'; |
| 118 | 110 | ||
| 119 | @override | 111 | @override |
| 120 | - String get onboardingReliefQuestion => | ||
| 121 | - 'Which methods do you think can ease stress?'; | 112 | + String get onboardingReliefQuestion => 'Which methods do you think can ease stress?'; |
| 122 | 113 | ||
| 123 | @override | 114 | @override |
| 124 | String get onboardingReliefSleep => 'Regular sleep'; | 115 | String get onboardingReliefSleep => 'Regular sleep'; |
| @@ -142,8 +133,7 @@ class AppLocalizationsEn extends AppLocalizations { | @@ -142,8 +133,7 @@ class AppLocalizationsEn extends AppLocalizations { | ||
| 142 | String get onboardingKeyDataTitle => 'Did you know?'; | 133 | String get onboardingKeyDataTitle => 'Did you know?'; |
| 143 | 134 | ||
| 144 | @override | 135 | @override |
| 145 | - String get onboardingKeyDataSubtitle => | ||
| 146 | - 'Everyone has a magical and important body metric that can help us:'; | 136 | + String get onboardingKeyDataSubtitle => 'Everyone has a magical and important body metric that can help us:'; |
| 147 | 137 | ||
| 148 | @override | 138 | @override |
| 149 | String get onboardingKeyDataStress => 'Monitor stress'; | 139 | String get onboardingKeyDataStress => 'Monitor stress'; |
| @@ -158,8 +148,7 @@ class AppLocalizationsEn extends AppLocalizations { | @@ -158,8 +148,7 @@ class AppLocalizationsEn extends AppLocalizations { | ||
| 158 | String get onboardingKeyDataHabits => 'Build healthy habits'; | 148 | String get onboardingKeyDataHabits => 'Build healthy habits'; |
| 159 | 149 | ||
| 160 | @override | 150 | @override |
| 161 | - String get onboardingKeyDataLovedOnes => | ||
| 162 | - 'Help important people care about your state in time'; | 151 | + String get onboardingKeyDataLovedOnes => 'Help important people care about your state in time'; |
| 163 | 152 | ||
| 164 | @override | 153 | @override |
| 165 | String get onboardingTellMeWhatItIs => 'Tell me what it is!'; | 154 | String get onboardingTellMeWhatItIs => 'Tell me what it is!'; |
| @@ -168,19 +157,16 @@ class AppLocalizationsEn extends AppLocalizations { | @@ -168,19 +157,16 @@ class AppLocalizationsEn extends AppLocalizations { | ||
| 168 | String get onboardingHrvTitle => 'It is HRV, heart rate variability'; | 157 | String get onboardingHrvTitle => 'It is HRV, heart rate variability'; |
| 169 | 158 | ||
| 170 | @override | 159 | @override |
| 171 | - String get onboardingHrvSubtitle => | ||
| 172 | - 'It helps us measure overall stress and health'; | 160 | + String get onboardingHrvSubtitle => 'It helps us measure overall stress and health'; |
| 173 | 161 | ||
| 174 | @override | 162 | @override |
| 175 | - String get onboardingHrvDescription => | ||
| 176 | - 'Heart rate variability (HRV) is the tiny variation in time between heartbeats. It reflects autonomic nervous system activity and how the body responds to stress.'; | 163 | + String get onboardingHrvDescription => 'Heart rate variability (HRV) is the tiny variation in time between heartbeats. It reflects autonomic nervous system activity and how the body responds to stress.'; |
| 177 | 164 | ||
| 178 | @override | 165 | @override |
| 179 | String get onboardingTellMeMore => 'Tell me more'; | 166 | String get onboardingTellMeMore => 'Tell me more'; |
| 180 | 167 | ||
| 181 | @override | 168 | @override |
| 182 | - String get onboardingResearchTitle => | ||
| 183 | - 'Many studies show that HRV changes are closely related to how our body and mind feel'; | 169 | + String get onboardingResearchTitle => 'Many studies show that HRV changes are closely related to how our body and mind feel'; |
| 184 | 170 | ||
| 185 | @override | 171 | @override |
| 186 | String get onboardingResearchFatigue => 'Physical fatigue'; | 172 | String get onboardingResearchFatigue => 'Physical fatigue'; |
| @@ -198,30 +184,25 @@ class AppLocalizationsEn extends AppLocalizations { | @@ -198,30 +184,25 @@ class AppLocalizationsEn extends AppLocalizations { | ||
| 198 | String get onboardingHealthPermissionTitle => 'Allow health data access'; | 184 | String get onboardingHealthPermissionTitle => 'Allow health data access'; |
| 199 | 185 | ||
| 200 | @override | 186 | @override |
| 201 | - String get onboardingHealthPermissionBody => | ||
| 202 | - 'DoubleFeel needs connected wearable health data to send reminders, count stress moments, and provide suggestions.'; | 187 | + String get onboardingHealthPermissionBody => 'DoubleFeel needs connected wearable health data to send reminders, count stress moments, and provide suggestions.'; |
| 203 | 188 | ||
| 204 | @override | 189 | @override |
| 205 | - String get onboardingHealthPermissionPrivacy => | ||
| 206 | - 'Your health data is stored locally. We do not upload any related data.'; | 190 | + String get onboardingHealthPermissionPrivacy => 'Your health data is stored locally. We do not upload any related data.'; |
| 207 | 191 | ||
| 208 | @override | 192 | @override |
| 209 | String get onboardingNotificationTitle => 'Turn on notifications'; | 193 | String get onboardingNotificationTitle => 'Turn on notifications'; |
| 210 | 194 | ||
| 211 | @override | 195 | @override |
| 212 | - String get onboardingNotificationSubtitle => | ||
| 213 | - 'Learn about every body change in time'; | 196 | + String get onboardingNotificationSubtitle => 'Learn about every body change in time'; |
| 214 | 197 | ||
| 215 | @override | 198 | @override |
| 216 | - String get onboardingNotificationBody => | ||
| 217 | - 'After Apple Watch data updates, we can remind you in time and help you act to improve your stress state.'; | 199 | + String get onboardingNotificationBody => 'After Apple Watch data updates, we can remind you in time and help you act to improve your stress state.'; |
| 218 | 200 | ||
| 219 | @override | 201 | @override |
| 220 | String get onboardingMemberTitle => 'Get an annual membership offer'; | 202 | String get onboardingMemberTitle => 'Get an annual membership offer'; |
| 221 | 203 | ||
| 222 | @override | 204 | @override |
| 223 | - String get onboardingMemberBody => | ||
| 224 | - 'Start your pressure alert and health companion journey, so love and care are always present.'; | 205 | + String get onboardingMemberBody => 'Start your pressure alert and health companion journey, so love and care are always present.'; |
| 225 | 206 | ||
| 226 | @override | 207 | @override |
| 227 | String get onboardingMemberOriginalPrice => 'Original ¥72.00/year'; | 208 | String get onboardingMemberOriginalPrice => 'Original ¥72.00/year'; |
| @@ -236,16 +217,13 @@ class AppLocalizationsEn extends AppLocalizations { | @@ -236,16 +217,13 @@ class AppLocalizationsEn extends AppLocalizations { | ||
| 236 | String get onboardingMemberAllOptions => 'View all purchase options'; | 217 | String get onboardingMemberAllOptions => 'View all purchase options'; |
| 237 | 218 | ||
| 238 | @override | 219 | @override |
| 239 | - String get healthCompanionIsNowAvailable => | ||
| 240 | - 'Health Companion is now available'; | 220 | + String get healthCompanionIsNowAvailable => 'Health Companion is now available'; |
| 241 | 221 | ||
| 242 | @override | 222 | @override |
| 243 | - String get youCanNowViewEachOtherSHrvStressLevelsAndSleepPatternsAndReachOutToCheckInWhenTheOtherPersonSeemsTired => | ||
| 244 | - 'You can now view each other\'s HRV, stress levels, and sleep patterns, and reach out to check in when the other person seems tired.'; | 223 | + String get youCanNowViewEachOtherSHrvStressLevelsAndSleepPatternsAndReachOutToCheckInWhenTheOtherPersonSeemsTired => 'You can now view each other\'s HRV, stress levels, and sleep patterns, and reach out to check in when the other person seems tired.'; |
| 245 | 224 | ||
| 246 | @override | 225 | @override |
| 247 | - String get bindPartnerTitle => | ||
| 248 | - 'Add a Close Contact\nOne more person to care about your health'; | 226 | + String get bindPartnerTitle => 'Add a Close Contact\nOne more person to care about your health'; |
| 249 | 227 | ||
| 250 | @override | 228 | @override |
| 251 | String get bindPartnerMyId => 'My ID'; | 229 | String get bindPartnerMyId => 'My ID'; |
| @@ -284,8 +262,7 @@ class AppLocalizationsEn extends AppLocalizations { | @@ -284,8 +262,7 @@ class AppLocalizationsEn extends AppLocalizations { | ||
| 284 | String get onboardingResearchGoodSleep => 'Good Sleep'; | 262 | String get onboardingResearchGoodSleep => 'Good Sleep'; |
| 285 | 263 | ||
| 286 | @override | 264 | @override |
| 287 | - String get loginSlogan => | ||
| 288 | - 'Start your pressure alert and health companion journey\nso love and care are always present'; | 265 | + String get loginSlogan => 'Start your pressure alert and health companion journey\nso love and care are always present'; |
| 289 | 266 | ||
| 290 | @override | 267 | @override |
| 291 | String get loginWithPhone => 'Sign in with Phone'; | 268 | String get loginWithPhone => 'Sign in with Phone'; |
| @@ -335,15 +312,13 @@ class AppLocalizationsEn extends AppLocalizations { | @@ -335,15 +312,13 @@ class AppLocalizationsEn extends AppLocalizations { | ||
| 335 | String get phoneLoginCodeHint => 'Enter verification code'; | 312 | String get phoneLoginCodeHint => 'Enter verification code'; |
| 336 | 313 | ||
| 337 | @override | 314 | @override |
| 338 | - String get phoneLoginAutoRegisterHint => | ||
| 339 | - 'Unregistered numbers will be registered automatically'; | 315 | + String get phoneLoginAutoRegisterHint => 'Unregistered numbers will be registered automatically'; |
| 340 | 316 | ||
| 341 | @override | 317 | @override |
| 342 | String get phoneLoginLoggingIn => 'Signing in...'; | 318 | String get phoneLoginLoggingIn => 'Signing in...'; |
| 343 | 319 | ||
| 344 | @override | 320 | @override |
| 345 | - String get loginAgreeToTermsToast => | ||
| 346 | - 'Please read and agree to the Terms of Service and Privacy Policy first'; | 321 | + String get loginAgreeToTermsToast => 'Please read and agree to the Terms of Service and Privacy Policy first'; |
| 347 | 322 | ||
| 348 | @override | 323 | @override |
| 349 | String get phoneLoginInvalidPhone => 'Invalid phone number'; | 324 | String get phoneLoginInvalidPhone => 'Invalid phone number'; |
| @@ -355,12 +330,10 @@ class AppLocalizationsEn extends AppLocalizations { | @@ -355,12 +330,10 @@ class AppLocalizationsEn extends AppLocalizations { | ||
| 355 | String get phoneLoginInvalidCode => 'Invalid verification code'; | 330 | String get phoneLoginInvalidCode => 'Invalid verification code'; |
| 356 | 331 | ||
| 357 | @override | 332 | @override |
| 358 | - String get todayHealthDataAuthTitle => | ||
| 359 | - 'Unable to access heart rate health data'; | 333 | + String get todayHealthDataAuthTitle => 'Unable to access heart rate health data'; |
| 360 | 334 | ||
| 361 | @override | 335 | @override |
| 362 | - String get todayHealthDataAuthDescription => | ||
| 363 | - 'DoubleFeel needs permission to access your health data to provide stress reminders, real-time stress statistics, and health suggestions. Otherwise, some app features may not work properly. Your health data is stored locally only and will not be uploaded to any server.'; | 336 | + String get todayHealthDataAuthDescription => 'DoubleFeel needs permission to access your health data to provide stress reminders, real-time stress statistics, and health suggestions. Otherwise, some app features may not work properly. Your health data is stored locally only and will not be uploaded to any server.'; |
| 364 | 337 | ||
| 365 | @override | 338 | @override |
| 366 | String get todayHealthDataAuthAction => 'Authorize health data access'; | 339 | String get todayHealthDataAuthAction => 'Authorize health data access'; |
| @@ -381,24 +354,19 @@ class AppLocalizationsEn extends AppLocalizations { | @@ -381,24 +354,19 @@ class AppLocalizationsEn extends AppLocalizations { | ||
| 381 | String get todayFaqLinkNoData => 'What if the app or watch face has no data?'; | 354 | String get todayFaqLinkNoData => 'What if the app or watch face has no data?'; |
| 382 | 355 | ||
| 383 | @override | 356 | @override |
| 384 | - String get todayFaqLinkHrvRealtimeUpdate => | ||
| 385 | - 'How can HRV data update in real time?'; | 357 | + String get todayFaqLinkHrvRealtimeUpdate => 'How can HRV data update in real time?'; |
| 386 | 358 | ||
| 387 | @override | 359 | @override |
| 388 | - String get todayFaqLinkWatchNoStatusNotification => | ||
| 389 | - 'Why can\'t my watch receive status notifications?'; | 360 | + String get todayFaqLinkWatchNoStatusNotification => 'Why can\'t my watch receive status notifications?'; |
| 390 | 361 | ||
| 391 | @override | 362 | @override |
| 392 | - String get todayFaqLinkWatchNoStatusAndInteractionNotification => | ||
| 393 | - 'Why can\'t my watch receive status and interaction notifications?'; | 363 | + String get todayFaqLinkWatchNoStatusAndInteractionNotification => 'Why can\'t my watch receive status and interaction notifications?'; |
| 394 | 364 | ||
| 395 | @override | 365 | @override |
| 396 | - String get todayFaqLinkWatchFaceDataDelay => | ||
| 397 | - 'Why is watch face data delayed or not updating?'; | 366 | + String get todayFaqLinkWatchFaceDataDelay => 'Why is watch face data delayed or not updating?'; |
| 398 | 367 | ||
| 399 | @override | 368 | @override |
| 400 | - String get todayFaqLinkWatchFaceBlackScreen => | ||
| 401 | - 'Why does the watch face turn black?'; | 369 | + String get todayFaqLinkWatchFaceBlackScreen => 'Why does the watch face turn black?'; |
| 402 | 370 | ||
| 403 | @override | 371 | @override |
| 404 | String get todayStressStatusTitle => 'Overall stress status'; | 372 | String get todayStressStatusTitle => 'Overall stress status'; |
| @@ -425,176 +393,136 @@ class AppLocalizationsEn extends AppLocalizations { | @@ -425,176 +393,136 @@ class AppLocalizationsEn extends AppLocalizations { | ||
| 425 | String get todayStressStatusInsufficientData => 'Insufficient data'; | 393 | String get todayStressStatusInsufficientData => 'Insufficient data'; |
| 426 | 394 | ||
| 427 | @override | 395 | @override |
| 428 | - String get todayStressStatusOverloadDescription => | ||
| 429 | - 'Your current HRV is much lower than your long-term average, which may indicate fatigue, high stress, or insufficient recovery. Rest is recommended.'; | 396 | + String get todayStressStatusOverloadDescription => 'Your current HRV is much lower than your long-term average, which may indicate fatigue, high stress, or insufficient recovery. Rest is recommended.'; |
| 430 | 397 | ||
| 431 | @override | 398 | @override |
| 432 | - String get todayStressStatusCautionDescription => | ||
| 433 | - 'Your current HRV is below the normal range, and your body may be accumulating stress. Pay attention to rest and recovery.'; | 399 | + String get todayStressStatusCautionDescription => 'Your current HRV is below the normal range, and your body may be accumulating stress. Pay attention to rest and recovery.'; |
| 434 | 400 | ||
| 435 | @override | 401 | @override |
| 436 | - String get todayStressStatusNormalDescription => | ||
| 437 | - 'Your current body state is within your normal fluctuation range.'; | 402 | + String get todayStressStatusNormalDescription => 'Your current body state is within your normal fluctuation range.'; |
| 438 | 403 | ||
| 439 | @override | 404 | @override |
| 440 | - String get todayStressStatusExcellentDescription => | ||
| 441 | - 'Your current HRV is higher than your recent average, indicating better recovery and overall state.'; | 405 | + String get todayStressStatusExcellentDescription => 'Your current HRV is higher than your recent average, indicating better recovery and overall state.'; |
| 442 | 406 | ||
| 443 | @override | 407 | @override |
| 444 | - String get todayStressStatusInsufficientDataDescription => | ||
| 445 | - 'There is not enough available data to accurately assess your stress state yet.'; | 408 | + String get todayStressStatusInsufficientDataDescription => 'There is not enough available data to accurately assess your stress state yet.'; |
| 446 | 409 | ||
| 447 | @override | 410 | @override |
| 448 | - String get todayHrvMeasurementIntro => | ||
| 449 | - 'Apple Watch measures HRV every 2-5 hours by default. If you want to measure it manually right now, follow these steps:'; | 411 | + String get todayHrvMeasurementIntro => 'Apple Watch measures HRV every 2-5 hours by default. If you want to measure it manually right now, follow these steps:'; |
| 450 | 412 | ||
| 451 | @override | 413 | @override |
| 452 | - String get todayHrvMeasurementStep1 => | ||
| 453 | - '1. Wear your Apple Watch snugly, sit down, and stay calm'; | 414 | + String get todayHrvMeasurementStep1 => '1. Wear your Apple Watch snugly, sit down, and stay calm'; |
| 454 | 415 | ||
| 455 | @override | 416 | @override |
| 456 | - String get todayHrvMeasurementStep2 => | ||
| 457 | - '2. Open Mindfulness on Apple Watch and start Breathe'; | 417 | + String get todayHrvMeasurementStep2 => '2. Open Mindfulness on Apple Watch and start Breathe'; |
| 458 | 418 | ||
| 459 | @override | 419 | @override |
| 460 | - String get todayHrvMeasurementStep3 => | ||
| 461 | - '3. Keep breathing steadily and wait 1-3 minutes'; | 420 | + String get todayHrvMeasurementStep3 => '3. Keep breathing steadily and wait 1-3 minutes'; |
| 462 | 421 | ||
| 463 | @override | 422 | @override |
| 464 | - String get todayHrvMeasurementStep4 => | ||
| 465 | - '4. After breathing is complete, lock and unlock your iPhone once'; | 423 | + String get todayHrvMeasurementStep4 => '4. After breathing is complete, lock and unlock your iPhone once'; |
| 466 | 424 | ||
| 467 | @override | 425 | @override |
| 468 | - String get todayHrvMeasurementStep5 => | ||
| 469 | - '5. Wait about one minute. StressWatch will receive and display your data'; | 426 | + String get todayHrvMeasurementStep5 => '5. Wait about one minute. StressWatch will receive and display your data'; |
| 470 | 427 | ||
| 471 | @override | 428 | @override |
| 472 | - String get todayHrvMeasurementHint => | ||
| 473 | - 'Tip: Data comes from Apple Watch. After measurement, there may be delays or data may not sync immediately. If this happens, measure again and wait for the data to be read.'; | 429 | + String get todayHrvMeasurementHint => 'Tip: Data comes from Apple Watch. After measurement, there may be delays or data may not sync immediately. If this happens, measure again and wait for the data to be read.'; |
| 474 | 430 | ||
| 475 | @override | 431 | @override |
| 476 | - String get todayHrvMeasurementWarning => | ||
| 477 | - 'Note: Health permissions must be enabled, and Low Power Mode must be turned off.'; | 432 | + String get todayHrvMeasurementWarning => 'Note: Health permissions must be enabled, and Low Power Mode must be turned off.'; |
| 478 | 433 | ||
| 479 | @override | 434 | @override |
| 480 | String get todayStressStatusWhatTitle => 'What is overall stress status?'; | 435 | String get todayStressStatusWhatTitle => 'What is overall stress status?'; |
| 481 | 436 | ||
| 482 | @override | 437 | @override |
| 483 | - String get todayStressStatusWhatDescription1 => | ||
| 484 | - 'DoubleFeel combines your HRV (heart rate variability), resting heart rate, and body-state changes from the past 30 days to assess your overall stress level.'; | 438 | + String get todayStressStatusWhatDescription1 => 'DoubleFeel combines your HRV (heart rate variability), resting heart rate, and body-state changes from the past 30 days to assess your overall stress level.'; |
| 485 | 439 | ||
| 486 | @override | 440 | @override |
| 487 | - String get todayStressStatusWhatDescription2 => | ||
| 488 | - 'Because HRV fluctuates with emotions, exercise, sleep, and fatigue, a single reading has limited value. We recommend focusing on your overall stress status across the day, which is more stable and useful. It helps you understand your body state and helps close contacts notice changes in time.'; | 441 | + String get todayStressStatusWhatDescription2 => 'Because HRV fluctuates with emotions, exercise, sleep, and fatigue, a single reading has limited value. We recommend focusing on your overall stress status across the day, which is more stable and useful. It helps you understand your body state and helps close contacts notice changes in time.'; |
| 489 | 442 | ||
| 490 | @override | 443 | @override |
| 491 | - String get todayStressStatusWhyHrvTitle => | ||
| 492 | - 'Why use HRV (heart rate variability)?'; | 444 | + String get todayStressStatusWhyHrvTitle => 'Why use HRV (heart rate variability)?'; |
| 493 | 445 | ||
| 494 | @override | 446 | @override |
| 495 | - String get todayStressStatusWhyHrvDescription => | ||
| 496 | - 'HRV is an important metric for measuring body stress and recovery capacity.'; | 447 | + String get todayStressStatusWhyHrvDescription => 'HRV is an important metric for measuring body stress and recovery capacity.'; |
| 497 | 448 | ||
| 498 | @override | 449 | @override |
| 499 | String get todayStressStatusUsually => 'In general:'; | 450 | String get todayStressStatusUsually => 'In general:'; |
| 500 | 451 | ||
| 501 | @override | 452 | @override |
| 502 | - String get todayStressStatusHrvHigher => | ||
| 503 | - '· Higher HRV usually means better recovery'; | 453 | + String get todayStressStatusHrvHigher => '· Higher HRV usually means better recovery'; |
| 504 | 454 | ||
| 505 | @override | 455 | @override |
| 506 | - String get todayStressStatusHrvLower => | ||
| 507 | - '· Lower HRV may indicate fatigue, stress, or insufficient sleep'; | 456 | + String get todayStressStatusHrvLower => '· Lower HRV may indicate fatigue, stress, or insufficient sleep'; |
| 508 | 457 | ||
| 509 | @override | 458 | @override |
| 510 | - String get todayStressStatusHrvChangesFast => | ||
| 511 | - '· HRV changes quickly, making it useful for short-term body-state changes.'; | 459 | + String get todayStressStatusHrvChangesFast => '· HRV changes quickly, making it useful for short-term body-state changes.'; |
| 512 | 460 | ||
| 513 | @override | 461 | @override |
| 514 | - String get todayStressStatusAppWatchDifferenceTitle => | ||
| 515 | - 'How are stress statuses on the phone app and Apple Watch different?'; | 462 | + String get todayStressStatusAppWatchDifferenceTitle => 'How are stress statuses on the phone app and Apple Watch different?'; |
| 516 | 463 | ||
| 517 | @override | 464 | @override |
| 518 | - String get todayStressStatusAppWatchDifferenceApp => | ||
| 519 | - 'The phone app home page shows the day\'s overall stress status, combining HRV, resting heart rate, and overall trends.'; | 465 | + String get todayStressStatusAppWatchDifferenceApp => 'The phone app home page shows the day\'s overall stress status, combining HRV, resting heart rate, and overall trends.'; |
| 520 | 466 | ||
| 521 | @override | 467 | @override |
| 522 | - String get todayStressStatusAppWatchDifferenceWatch => | ||
| 523 | - 'Apple Watch shows the most recent real-time stress status, which is better for quickly checking your current body changes.'; | 468 | + String get todayStressStatusAppWatchDifferenceWatch => 'Apple Watch shows the most recent real-time stress status, which is better for quickly checking your current body changes.'; |
| 524 | 469 | ||
| 525 | @override | 470 | @override |
| 526 | - String get todayStressStatusWaitingDataTitle => | ||
| 527 | - 'Why does Waiting for data appear?'; | 471 | + String get todayStressStatusWaitingDataTitle => 'Why does Waiting for data appear?'; |
| 528 | 472 | ||
| 529 | @override | 473 | @override |
| 530 | - String get todayStressStatusWaitingDataDescription1 => | ||
| 531 | - 'Waiting for data means the current amount of collected data is not enough to generate a reliable stress assessment.'; | 474 | + String get todayStressStatusWaitingDataDescription1 => 'Waiting for data means the current amount of collected data is not enough to generate a reliable stress assessment.'; |
| 532 | 475 | ||
| 533 | @override | 476 | @override |
| 534 | - String get todayStressStatusWaitingDataDescription2 => | ||
| 535 | - 'Please keep wearing your Apple Watch and wait for the system to collect data automatically.'; | 477 | + String get todayStressStatusWaitingDataDescription2 => 'Please keep wearing your Apple Watch and wait for the system to collect data automatically.'; |
| 536 | 478 | ||
| 537 | @override | 479 | @override |
| 538 | - String get todayStressStatusWaitingDataReasonsIntro => | ||
| 539 | - 'Possible reasons include:'; | 480 | + String get todayStressStatusWaitingDataReasonsIntro => 'Possible reasons include:'; |
| 540 | 481 | ||
| 541 | @override | 482 | @override |
| 542 | String get todayStressStatusWaitingDataReason1 => '1. Not enough HRV samples'; | 483 | String get todayStressStatusWaitingDataReason1 => '1. Not enough HRV samples'; |
| 543 | 484 | ||
| 544 | @override | 485 | @override |
| 545 | - String get todayStressStatusWaitingDataReason2 => | ||
| 546 | - '2. Missing resting heart rate data'; | 486 | + String get todayStressStatusWaitingDataReason2 => '2. Missing resting heart rate data'; |
| 547 | 487 | ||
| 548 | @override | 488 | @override |
| 549 | - String get todayStressStatusWaitingDataReason3 => | ||
| 550 | - '3. Apple Watch has not been worn long enough'; | 489 | + String get todayStressStatusWaitingDataReason3 => '3. Apple Watch has not been worn long enough'; |
| 551 | 490 | ||
| 552 | @override | 491 | @override |
| 553 | - String get todayStressStatusWaitingDataReason4 => | ||
| 554 | - '4. Apple Health permissions are not enabled'; | 492 | + String get todayStressStatusWaitingDataReason4 => '4. Apple Health permissions are not enabled'; |
| 555 | 493 | ||
| 556 | @override | 494 | @override |
| 557 | - String get todayHrvPrincipleHowMeasureTitle => | ||
| 558 | - 'How does DoubleFeel measure stress status?'; | 495 | + String get todayHrvPrincipleHowMeasureTitle => 'How does DoubleFeel measure stress status?'; |
| 559 | 496 | ||
| 560 | @override | 497 | @override |
| 561 | - String get todayHrvPrincipleHowMeasureDescription1 => | ||
| 562 | - 'When you wear Apple Watch normally, the system automatically collects your heart rate data and syncs it to Apple Health.'; | 498 | + String get todayHrvPrincipleHowMeasureDescription1 => 'When you wear Apple Watch normally, the system automatically collects your heart rate data and syncs it to Apple Health.'; |
| 563 | 499 | ||
| 564 | @override | 500 | @override |
| 565 | - String get todayHrvPrincipleHowMeasureDescription2 => | ||
| 566 | - 'DoubleFeel calculates HRV (heart rate variability) indicators based on this data to assess your body stress and recovery state.'; | 501 | + String get todayHrvPrincipleHowMeasureDescription2 => 'DoubleFeel calculates HRV (heart rate variability) indicators based on this data to assess your body stress and recovery state.'; |
| 567 | 502 | ||
| 568 | @override | 503 | @override |
| 569 | - String get todayHrvPrincipleHowMeasureDescription3 => | ||
| 570 | - 'HRV is sensitive to stress, fatigue, sleep, emotions, and recovery, so it helps us notice body-state changes earlier.'; | 504 | + String get todayHrvPrincipleHowMeasureDescription3 => 'HRV is sensitive to stress, fatigue, sleep, emotions, and recovery, so it helps us notice body-state changes earlier.'; |
| 571 | 505 | ||
| 572 | @override | 506 | @override |
| 573 | - String get todayHrvPrincipleHowMeasureDescription4 => | ||
| 574 | - 'To make results more accurate, DoubleFeel compares your current HRV state with your own 30-day average instead of comparing it directly with other people.'; | 507 | + String get todayHrvPrincipleHowMeasureDescription4 => 'To make results more accurate, DoubleFeel compares your current HRV state with your own 30-day average instead of comparing it directly with other people.'; |
| 575 | 508 | ||
| 576 | @override | 509 | @override |
| 577 | String get todayRealtimeStressWhatTitle => 'What is real-time stress?'; | 510 | String get todayRealtimeStressWhatTitle => 'What is real-time stress?'; |
| 578 | 511 | ||
| 579 | @override | 512 | @override |
| 580 | - String get todayRealtimeStressWhatDescription1 => | ||
| 581 | - 'Real-time stress is a body stress indicator dynamically generated by DoubleFeel based on your current HRV, heart rate state, and changes in your personal history.'; | 513 | + String get todayRealtimeStressWhatDescription1 => 'Real-time stress is a body stress indicator dynamically generated by DoubleFeel based on your current HRV, heart rate state, and changes in your personal history.'; |
| 582 | 514 | ||
| 583 | @override | 515 | @override |
| 584 | - String get todayRealtimeStressWhatDescription2 => | ||
| 585 | - 'A higher stress value means your body state is deviating more from your usual baseline and may reflect fatigue, insufficient recovery, or high stress.'; | 516 | + String get todayRealtimeStressWhatDescription2 => 'A higher stress value means your body state is deviating more from your usual baseline and may reflect fatigue, insufficient recovery, or high stress.'; |
| 586 | 517 | ||
| 587 | @override | 518 | @override |
| 588 | - String get todayRealtimeStressWhatDescription3 => | ||
| 589 | - 'It helps you notice body changes faster and adjust rest, exercise, and daily rhythm in time.'; | 519 | + String get todayRealtimeStressWhatDescription3 => 'It helps you notice body changes faster and adjust rest, exercise, and daily rhythm in time.'; |
| 590 | 520 | ||
| 591 | @override | 521 | @override |
| 592 | - String get todayRealtimeStressDivisionTitle => | ||
| 593 | - 'How is real-time stress divided?'; | 522 | + String get todayRealtimeStressDivisionTitle => 'How is real-time stress divided?'; |
| 594 | 523 | ||
| 595 | @override | 524 | @override |
| 596 | - String get todayRealtimeStressDivisionIntro => | ||
| 597 | - 'Real-time stress is shown as a percentage:'; | 525 | + String get todayRealtimeStressDivisionIntro => 'Real-time stress is shown as a percentage:'; |
| 598 | 526 | ||
| 599 | @override | 527 | @override |
| 600 | String get todayRealtimeStressExcellentRange => 'Excellent: 1%-20%'; | 528 | String get todayRealtimeStressExcellentRange => 'Excellent: 1%-20%'; |
| @@ -609,103 +537,79 @@ class AppLocalizationsEn extends AppLocalizations { | @@ -609,103 +537,79 @@ class AppLocalizationsEn extends AppLocalizations { | ||
| 609 | String get todayRealtimeStressOverloadRange => 'Stress overload: 81%-100%'; | 537 | String get todayRealtimeStressOverloadRange => 'Stress overload: 81%-100%'; |
| 610 | 538 | ||
| 611 | @override | 539 | @override |
| 612 | - String get todayRealtimeStressExcellentDescription => | ||
| 613 | - 'Your recovery state is good and you are generally relaxed.'; | 540 | + String get todayRealtimeStressExcellentDescription => 'Your recovery state is good and you are generally relaxed.'; |
| 614 | 541 | ||
| 615 | @override | 542 | @override |
| 616 | - String get todayRealtimeStressNormalDescription => | ||
| 617 | - 'Your body is within the normal fluctuation range.'; | 543 | + String get todayRealtimeStressNormalDescription => 'Your body is within the normal fluctuation range.'; |
| 618 | 544 | ||
| 619 | @override | 545 | @override |
| 620 | - String get todayRealtimeStressCautionDescription => | ||
| 621 | - 'Your body may be accumulating stress and needs proper rest and recovery.'; | 546 | + String get todayRealtimeStressCautionDescription => 'Your body may be accumulating stress and needs proper rest and recovery.'; |
| 622 | 547 | ||
| 623 | @override | 548 | @override |
| 624 | - String get todayRealtimeStressOverloadDescription => | ||
| 625 | - 'Your body stress is clearly high. Reduce load and pay attention to sleep and recovery.'; | 549 | + String get todayRealtimeStressOverloadDescription => 'Your body stress is clearly high. Reduce load and pay attention to sleep and recovery.'; |
| 626 | 550 | ||
| 627 | @override | 551 | @override |
| 628 | - String get todayRealtimeStressDivisionBaseline => | ||
| 629 | - 'These ranges are adjusted dynamically based on your personal baseline and should not be directly compared between users.'; | 552 | + String get todayRealtimeStressDivisionBaseline => 'These ranges are adjusted dynamically based on your personal baseline and should not be directly compared between users.'; |
| 630 | 553 | ||
| 631 | @override | 554 | @override |
| 632 | - String get todayRealtimeStressDivisionAwake => | ||
| 633 | - 'Real-time stress mainly reflects body stress changes while awake.'; | 555 | + String get todayRealtimeStressDivisionAwake => 'Real-time stress mainly reflects body stress changes while awake.'; |
| 634 | 556 | ||
| 635 | @override | 557 | @override |
| 636 | - String get todayRealtimeStressLowBetterTitle => | ||
| 637 | - 'Is lower real-time stress always better?'; | 558 | + String get todayRealtimeStressLowBetterTitle => 'Is lower real-time stress always better?'; |
| 638 | 559 | ||
| 639 | @override | 560 | @override |
| 640 | String get todayRealtimeStressLowBetterNo => 'Not necessarily.'; | 561 | String get todayRealtimeStressLowBetterNo => 'Not necessarily.'; |
| 641 | 562 | ||
| 642 | @override | 563 | @override |
| 643 | - String get todayRealtimeStressLowBetterType => | ||
| 644 | - 'Body stress can be normal or abnormal.'; | 564 | + String get todayRealtimeStressLowBetterType => 'Body stress can be normal or abnormal.'; |
| 645 | 565 | ||
| 646 | @override | 566 | @override |
| 647 | - String get todayRealtimeStressLowBetterExample => | ||
| 648 | - 'For example, real-time stress rising briefly during or after exercise is a normal recovery response. It can also rise temporarily during focused work or emotional excitement, which are normal body adjustments.'; | 567 | + String get todayRealtimeStressLowBetterExample => 'For example, real-time stress rising briefly during or after exercise is a normal recovery response. It can also rise temporarily during focused work or emotional excitement, which are normal body adjustments.'; |
| 649 | 568 | ||
| 650 | @override | 569 | @override |
| 651 | - String get todayRealtimeStressLowBetterHighStress => | ||
| 652 | - 'But if stress remains high while resting, sitting for a long time, or after poor sleep, it may indicate physical fatigue, mental stress, insufficient sleep recovery, incomplete exercise recovery, too much caffeine, alcohol, stimulants, or possible discomfort.'; | 570 | + String get todayRealtimeStressLowBetterHighStress => 'But if stress remains high while resting, sitting for a long time, or after poor sleep, it may indicate physical fatigue, mental stress, insufficient sleep recovery, incomplete exercise recovery, too much caffeine, alcohol, stimulants, or possible discomfort.'; |
| 653 | 571 | ||
| 654 | @override | 572 | @override |
| 655 | - String get todayRealtimeStressLowBetterTrend => | ||
| 656 | - 'DoubleFeel focuses more on your long-term trend than on a single fluctuation.'; | 573 | + String get todayRealtimeStressLowBetterTrend => 'DoubleFeel focuses more on your long-term trend than on a single fluctuation.'; |
| 657 | 574 | ||
| 658 | @override | 575 | @override |
| 659 | - String get todayRealtimeStressScenarioTitle => | ||
| 660 | - 'When should HRV and real-time stress be used?'; | 576 | + String get todayRealtimeStressScenarioTitle => 'When should HRV and real-time stress be used?'; |
| 661 | 577 | ||
| 662 | @override | 578 | @override |
| 663 | - String get todayRealtimeStressScenarioHrvDefault => | ||
| 664 | - 'With Apple Watch default settings, HRV updates every 2-5 hours.'; | 579 | + String get todayRealtimeStressScenarioHrvDefault => 'With Apple Watch default settings, HRV updates every 2-5 hours.'; |
| 665 | 580 | ||
| 666 | @override | 581 | @override |
| 667 | - String get todayRealtimeStressScenarioRegionLimit => | ||
| 668 | - 'In some regions, Apple Watch breathing features may be limited, which can affect HRV update frequency. Turning on breathing features may also consume more battery.'; | 582 | + String get todayRealtimeStressScenarioRegionLimit => 'In some regions, Apple Watch breathing features may be limited, which can affect HRV update frequency. Turning on breathing features may also consume more battery.'; |
| 669 | 583 | ||
| 670 | @override | 584 | @override |
| 671 | - String get todayRealtimeStressScenarioIntro => | ||
| 672 | - 'To address the long interval between HRV updates, DoubleFeel designed real-time stress:'; | 585 | + String get todayRealtimeStressScenarioIntro => 'To address the long interval between HRV updates, DoubleFeel designed real-time stress:'; |
| 673 | 586 | ||
| 674 | @override | 587 | @override |
| 675 | - String get todayRealtimeStressScenarioUpdateEvery6Min => | ||
| 676 | - '· Real-time stress updates every 6 minutes'; | 588 | + String get todayRealtimeStressScenarioUpdateEvery6Min => '· Real-time stress updates every 6 minutes'; |
| 677 | 589 | ||
| 678 | @override | 590 | @override |
| 679 | - String get todayRealtimeStressScenarioTimely => | ||
| 680 | - '· It can reflect body-state changes more promptly'; | 591 | + String get todayRealtimeStressScenarioTimely => '· It can reflect body-state changes more promptly'; |
| 681 | 592 | ||
| 682 | @override | 593 | @override |
| 683 | - String get todayRealtimeStressScenarioConsistentTrend => | ||
| 684 | - '· In most cases, the real-time stress trend is consistent with the HRV trend'; | 594 | + String get todayRealtimeStressScenarioConsistentTrend => '· In most cases, the real-time stress trend is consistent with the HRV trend'; |
| 685 | 595 | ||
| 686 | @override | 596 | @override |
| 687 | - String get todayRealtimeStressScenarioSummary => | ||
| 688 | - 'This lets users see long-term HRV trends while also using real-time stress as a short-term body-state reference.'; | 597 | + String get todayRealtimeStressScenarioSummary => 'This lets users see long-term HRV trends while also using real-time stress as a short-term body-state reference.'; |
| 689 | 598 | ||
| 690 | @override | 599 | @override |
| 691 | - String get todayFaqNoDataTitle => | ||
| 692 | - 'What if the app or watch face has no data?'; | 600 | + String get todayFaqNoDataTitle => 'What if the app or watch face has no data?'; |
| 693 | 601 | ||
| 694 | @override | 602 | @override |
| 695 | - String get todayFaqNoDataDescription1 => | ||
| 696 | - '1. Confirm that Apple Watch is on watchOS 10.0 or above and iPhone is on iOS 14 or above. You can check system versions in About.'; | 603 | + String get todayFaqNoDataDescription1 => '1. Confirm that Apple Watch is on watchOS 10.0 or above and iPhone is on iOS 14 or above. You can check system versions in About.'; |
| 697 | 604 | ||
| 698 | @override | 605 | @override |
| 699 | - String get todayFaqNoDataDescription2 => | ||
| 700 | - '2. Confirm all permissions are enabled: iPhone Health > Sharing > Apps > DoubleFeel > Turn On All Permissions.'; | 606 | + String get todayFaqNoDataDescription2 => '2. Confirm all permissions are enabled: iPhone Health > Sharing > Apps > DoubleFeel > Turn On All Permissions.'; |
| 701 | 607 | ||
| 702 | @override | 608 | @override |
| 703 | - String get todayFaqNoDataDescription3 => | ||
| 704 | - '3. Confirm the device is not in Low Power Mode, low battery, or worn too loosely, as these can affect data collection.'; | 609 | + String get todayFaqNoDataDescription3 => '3. Confirm the device is not in Low Power Mode, low battery, or worn too loosely, as these can affect data collection.'; |
| 705 | 610 | ||
| 706 | @override | 611 | @override |
| 707 | - String get todayFaqContactPrefix => | ||
| 708 | - 'If everything above is correct, you can '; | 612 | + String get todayFaqContactPrefix => 'If everything above is correct, you can '; |
| 709 | 613 | ||
| 710 | @override | 614 | @override |
| 711 | String get todayFaqContactAction => 'contact us'; | 615 | String get todayFaqContactAction => 'contact us'; |
| @@ -714,90 +618,70 @@ class AppLocalizationsEn extends AppLocalizations { | @@ -714,90 +618,70 @@ class AppLocalizationsEn extends AppLocalizations { | ||
| 714 | String get todayFaqContactSuffix => '.'; | 618 | String get todayFaqContactSuffix => '.'; |
| 715 | 619 | ||
| 716 | @override | 620 | @override |
| 717 | - String get todayFaqWatchNoNotificationTitle => | ||
| 718 | - 'Watch cannot receive status notifications?'; | 621 | + String get todayFaqWatchNoNotificationTitle => 'Watch cannot receive status notifications?'; |
| 719 | 622 | ||
| 720 | @override | 623 | @override |
| 721 | - String get todayFaqWatchNoNotificationDescription1 => | ||
| 722 | - 'Apple Watch and iPhone notifications have priority rules: when your iPhone is unlocked and the screen is on, notifications only appear on the phone and will not appear on the watch.'; | 624 | + String get todayFaqWatchNoNotificationDescription1 => 'Apple Watch and iPhone notifications have priority rules: when your iPhone is unlocked and the screen is on, notifications only appear on the phone and will not appear on the watch.'; |
| 723 | 625 | ||
| 724 | @override | 626 | @override |
| 725 | - String get todayFaqWatchNoNotificationDescription2 => | ||
| 726 | - 'If stress data displays and updates normally but your watch does not receive notifications, try the following:'; | 627 | + String get todayFaqWatchNoNotificationDescription2 => 'If stress data displays and updates normally but your watch does not receive notifications, try the following:'; |
| 727 | 628 | ||
| 728 | @override | 629 | @override |
| 729 | - String get todayFaqWatchNoNotificationCheckPhoneNotification => | ||
| 730 | - '1. Check whether iPhone notifications are enabled (Settings > DoubleFeel > Notifications).'; | 630 | + String get todayFaqWatchNoNotificationCheckPhoneNotification => '1. Check whether iPhone notifications are enabled (Settings > DoubleFeel > Notifications).'; |
| 731 | 631 | ||
| 732 | @override | 632 | @override |
| 733 | - String get todayFaqWatchNoNotificationCheckPhoneBackgroundRefresh => | ||
| 734 | - '2. Check whether iPhone Background App Refresh is enabled (Settings > DoubleFeel > Background App Refresh).'; | 633 | + String get todayFaqWatchNoNotificationCheckPhoneBackgroundRefresh => '2. Check whether iPhone Background App Refresh is enabled (Settings > DoubleFeel > Background App Refresh).'; |
| 735 | 634 | ||
| 736 | @override | 635 | @override |
| 737 | - String get todayFaqWatchNoNotificationCheckWatchBackgroundRefresh => | ||
| 738 | - '3. Check whether Apple Watch Background App Refresh is enabled (Settings > General > Background App Refresh, and make sure DoubleFeel is enabled).'; | 636 | + String get todayFaqWatchNoNotificationCheckWatchBackgroundRefresh => '3. Check whether Apple Watch Background App Refresh is enabled (Settings > General > Background App Refresh, and make sure DoubleFeel is enabled).'; |
| 739 | 637 | ||
| 740 | @override | 638 | @override |
| 741 | - String get todayFaqWatchNoNotificationCheckModes => | ||
| 742 | - '4. Make sure Low Power, Focus, Do Not Disturb, Theater, Sleep, and similar modes are off.'; | 639 | + String get todayFaqWatchNoNotificationCheckModes => '4. Make sure Low Power, Focus, Do Not Disturb, Theater, Sleep, and similar modes are off.'; |
| 743 | 640 | ||
| 744 | @override | 641 | @override |
| 745 | - String get todayFaqWatchNoNotificationReinstall => | ||
| 746 | - '5. Reinstall DoubleFeel and restart Apple Watch and iPhone.'; | 642 | + String get todayFaqWatchNoNotificationReinstall => '5. Reinstall DoubleFeel and restart Apple Watch and iPhone.'; |
| 747 | 643 | ||
| 748 | @override | 644 | @override |
| 749 | - String get todayFaqWatchFaceDelayTitle => | ||
| 750 | - 'Watch face data not updating or delayed?'; | 645 | + String get todayFaqWatchFaceDelayTitle => 'Watch face data not updating or delayed?'; |
| 751 | 646 | ||
| 752 | @override | 647 | @override |
| 753 | - String get todayFaqWatchFaceDelayDescription1 => | ||
| 754 | - 'Due to Apple system limits, all watch faces, third-party or official, may have delays from a few minutes to half an hour. Developers cannot control the refresh frequency.'; | 648 | + String get todayFaqWatchFaceDelayDescription1 => 'Due to Apple system limits, all watch faces, third-party or official, may have delays from a few minutes to half an hour. Developers cannot control the refresh frequency.'; |
| 755 | 649 | ||
| 756 | @override | 650 | @override |
| 757 | - String get todayFaqWatchFaceDelayIfOverOneHour => | ||
| 758 | - 'If the phone data refreshes but the watch face still has not updated after more than 1 hour:'; | 651 | + String get todayFaqWatchFaceDelayIfOverOneHour => 'If the phone data refreshes but the watch face still has not updated after more than 1 hour:'; |
| 759 | 652 | ||
| 760 | @override | 653 | @override |
| 761 | - String get todayFaqWatchFaceDelayOpenWatchApp => | ||
| 762 | - 'Manually open DoubleFeel on Apple Watch and wait about 1 minute.'; | 654 | + String get todayFaqWatchFaceDelayOpenWatchApp => 'Manually open DoubleFeel on Apple Watch and wait about 1 minute.'; |
| 763 | 655 | ||
| 764 | @override | 656 | @override |
| 765 | String get todayFaqWatchFaceDelayIfStill => 'If it still does not update:'; | 657 | String get todayFaqWatchFaceDelayIfStill => 'If it still does not update:'; |
| 766 | 658 | ||
| 767 | @override | 659 | @override |
| 768 | - String get todayFaqWatchFaceDelayRestartApp => | ||
| 769 | - 'Close the DoubleFeel background process and restart it.'; | 660 | + String get todayFaqWatchFaceDelayRestartApp => 'Close the DoubleFeel background process and restart it.'; |
| 770 | 661 | ||
| 771 | @override | 662 | @override |
| 772 | - String get todayFaqWatchFaceDelayCheckIntro => | ||
| 773 | - 'If it still does not work, check:'; | 663 | + String get todayFaqWatchFaceDelayCheckIntro => 'If it still does not work, check:'; |
| 774 | 664 | ||
| 775 | @override | 665 | @override |
| 776 | - String get todayFaqWatchFaceDelayCheckData => | ||
| 777 | - '· Whether both phone and watch apps can show HRV data normally.'; | 666 | + String get todayFaqWatchFaceDelayCheckData => '· Whether both phone and watch apps can show HRV data normally.'; |
| 778 | 667 | ||
| 779 | @override | 668 | @override |
| 780 | - String get todayFaqWatchFaceDelayCheckPhoneHealth => | ||
| 781 | - '· Make sure all permissions are enabled on iPhone: iOS Settings > Privacy & Security > Health > DoubleFeel.'; | 669 | + String get todayFaqWatchFaceDelayCheckPhoneHealth => '· Make sure all permissions are enabled on iPhone: iOS Settings > Privacy & Security > Health > DoubleFeel.'; |
| 782 | 670 | ||
| 783 | @override | 671 | @override |
| 784 | - String get todayFaqWatchFaceDelayCheckWatchHealth => | ||
| 785 | - '· Make sure all permissions are enabled on Apple Watch: Settings > Health > Data Sources & Access > DoubleFeel.'; | 672 | + String get todayFaqWatchFaceDelayCheckWatchHealth => '· Make sure all permissions are enabled on Apple Watch: Settings > Health > Data Sources & Access > DoubleFeel.'; |
| 786 | 673 | ||
| 787 | @override | 674 | @override |
| 788 | - String get todayFaqWatchFaceDelayCheckBackgroundRefresh => | ||
| 789 | - '· Confirm DoubleFeel is enabled in Apple Watch > Settings > General > Background App Refresh.'; | 675 | + String get todayFaqWatchFaceDelayCheckBackgroundRefresh => '· Confirm DoubleFeel is enabled in Apple Watch > Settings > General > Background App Refresh.'; |
| 790 | 676 | ||
| 791 | @override | 677 | @override |
| 792 | - String get todayFaqWatchFaceDelayRestartWatch => | ||
| 793 | - '· If it still does not refresh automatically, restart Apple Watch. Long runtimes or high background usage may cause watch face updates to pause.'; | 678 | + String get todayFaqWatchFaceDelayRestartWatch => '· If it still does not refresh automatically, restart Apple Watch. Long runtimes or high background usage may cause watch face updates to pause.'; |
| 794 | 679 | ||
| 795 | @override | 680 | @override |
| 796 | String get todayFaqWatchFaceBlackScreenTitle => 'Watch face turns black?'; | 681 | String get todayFaqWatchFaceBlackScreenTitle => 'Watch face turns black?'; |
| 797 | 682 | ||
| 798 | @override | 683 | @override |
| 799 | - String get todayFaqWatchFaceBlackScreenDescription => | ||
| 800 | - 'If the custom interactive watch face turns black after being added and only shows time and date, long-press the watch face, tap Edit, swipe left to Complications, choose DoubleFeel, and add each component again as needed.'; | 684 | + String get todayFaqWatchFaceBlackScreenDescription => 'If the custom interactive watch face turns black after being added and only shows time and date, long-press the watch face, tap Edit, swipe left to Complications, choose DoubleFeel, and add each component again as needed.'; |
| 801 | 685 | ||
| 802 | @override | 686 | @override |
| 803 | String get today => 'Today'; | 687 | String get today => 'Today'; |
| @@ -815,19 +699,16 @@ class AppLocalizationsEn extends AppLocalizations { | @@ -815,19 +699,16 @@ class AppLocalizationsEn extends AppLocalizations { | ||
| 815 | String get allPlans => 'All Plans'; | 699 | String get allPlans => 'All Plans'; |
| 816 | 700 | ||
| 817 | @override | 701 | @override |
| 818 | - String get clickToAddTheHrvThemedWatchFace => | ||
| 819 | - 'Click to add the HRV-themed watch face'; | 702 | + String get clickToAddTheHrvThemedWatchFace => 'Click to add the HRV-themed watch face'; |
| 820 | 703 | ||
| 821 | @override | 704 | @override |
| 822 | - String get stayOnTopOfYourHealthFluctuations => | ||
| 823 | - 'Stay on top of your health fluctuations'; | 705 | + String get stayOnTopOfYourHealthFluctuations => 'Stay on top of your health fluctuations'; |
| 824 | 706 | ||
| 825 | @override | 707 | @override |
| 826 | String get addACloseContact => 'Add a close contact'; | 708 | String get addACloseContact => 'Add a close contact'; |
| 827 | 709 | ||
| 828 | @override | 710 | @override |
| 829 | - String get oneMorePersonLookingOutForYourHealth => | ||
| 830 | - 'One more person looking out for your health'; | 711 | + String get oneMorePersonLookingOutForYourHealth => 'One more person looking out for your health'; |
| 831 | 712 | ||
| 832 | @override | 713 | @override |
| 833 | String get addAFriend => 'Add a friend'; | 714 | String get addAFriend => 'Add a friend'; |
| @@ -884,8 +765,7 @@ class AppLocalizationsEn extends AppLocalizations { | @@ -884,8 +765,7 @@ class AppLocalizationsEn extends AppLocalizations { | ||
| 884 | String get questionsAndFeedback => 'Questions and Feedback'; | 765 | String get questionsAndFeedback => 'Questions and Feedback'; |
| 885 | 766 | ||
| 886 | @override | 767 | @override |
| 887 | - String get ifYouWouldLikeUsToReplyPleaseProvideYourEmailAddress => | ||
| 888 | - 'If you would like us to reply, please provide your email address'; | 768 | + String get ifYouWouldLikeUsToReplyPleaseProvideYourEmailAddress => 'If you would like us to reply, please provide your email address'; |
| 889 | 769 | ||
| 890 | @override | 770 | @override |
| 891 | String get uploadProof => 'Upload Proof'; | 771 | String get uploadProof => 'Upload Proof'; |
| @@ -894,8 +774,7 @@ class AppLocalizationsEn extends AppLocalizations { | @@ -894,8 +774,7 @@ class AppLocalizationsEn extends AppLocalizations { | ||
| 894 | String get frequentlyAskedQuestions => 'Frequently Asked Questions'; | 774 | String get frequentlyAskedQuestions => 'Frequently Asked Questions'; |
| 895 | 775 | ||
| 896 | @override | 776 | @override |
| 897 | - String get areYouSureYouWantToDeleteYourAccount => | ||
| 898 | - 'Are you sure you want to delete your account?'; | 777 | + String get areYouSureYouWantToDeleteYourAccount => 'Are you sure you want to delete your account?'; |
| 899 | 778 | ||
| 900 | @override | 779 | @override |
| 901 | String get accountSettings => 'Account Settings'; | 780 | String get accountSettings => 'Account Settings'; |
-
Please register or login to post a comment