diff --git a/.kotlin/errors/errors-1749093424243.log b/.kotlin/errors/errors-1749093424243.log new file mode 100644 index 0000000..8285180 --- /dev/null +++ b/.kotlin/errors/errors-1749093424243.log @@ -0,0 +1,33 @@ +kotlin version: 2.1.20 +error message: java.lang.NoSuchMethodError: 'org.jetbrains.kotlin.config.LanguageVersionSettings org.jetbrains.kotlin.codegen.state.KotlinTypeMapper$Companion.getLANGUAGE_VERSION_SETTINGS_DEFAULT()' + at com.google.devtools.ksp.processing.impl.ResolverImpl.(ResolverImpl.kt:147) + at com.google.devtools.ksp.AbstractKotlinSymbolProcessingExtension.doAnalysis(KotlinSymbolProcessingExtension.kt:231) + at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(TopDownAnalyzerFacadeForJVM.kt:112) + at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration$default(TopDownAnalyzerFacadeForJVM.kt:75) + at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.analyze$lambda$7(KotlinToJVMBytecodeCompiler.kt:326) + at org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport.analyzeAndReport(AnalyzerWithCompilerReport.kt:112) + at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.analyze(KotlinToJVMBytecodeCompiler.kt:317) + at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.runFrontendAndGenerateIrUsingClassicFrontend(KotlinToJVMBytecodeCompiler.kt:154) + at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:75) + at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:167) + at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:36) + at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:113) + at org.jetbrains.kotlin.cli.common.CLICompiler.exec(CLICompiler.kt:337) + at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1700) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source) + at java.base/java.lang.reflect.Method.invoke(Unknown Source) + at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(Unknown Source) + at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) + at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.rmi/sun.rmi.transport.Transport.serviceCall(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) + at java.base/java.lang.Thread.run(Unknown Source) + + diff --git a/.kotlin/metadata/kotlinCInteropLibraries/shared-iosArm64Cinterop-cinMain-SXIigQ.klib b/.kotlin/metadata/kotlinCInteropLibraries/shared-iosArm64Cinterop-cinMain-SXIigQ.klib new file mode 100644 index 0000000..24cca31 Binary files /dev/null and b/.kotlin/metadata/kotlinCInteropLibraries/shared-iosArm64Cinterop-cinMain-SXIigQ.klib differ diff --git a/.kotlin/sessions/kotlin-compiler-7621307791481117302.salive b/.kotlin/sessions/kotlin-compiler-7621307791481117302.salive new file mode 100644 index 0000000..e69de29 diff --git a/androidApp/src/main/AndroidManifest.xml b/androidApp/src/main/AndroidManifest.xml index b698c8b..9c4a9c1 100644 --- a/androidApp/src/main/AndroidManifest.xml +++ b/androidApp/src/main/AndroidManifest.xml @@ -27,6 +27,7 @@ 3.4.1' pod 'FMDB' - - # 添加post_install脚本来配置静态库链接 - post_install do |installer| - installer.pods_project.targets.each do |target| - target.build_configurations.each do |config| - if target.name == 'iosApp' - config.build_settings['OTHER_LDFLAGS'] ||= [] - config.build_settings['OTHER_LDFLAGS'] << '-lRingSDK_2.0.2' - config.build_settings['LIBRARY_SEARCH_PATHS'] ||= [] - config.build_settings['LIBRARY_SEARCH_PATHS'] << '$(PROJECT_DIR)/iosApp/Libs' - end + post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '14.0' + end end end - end end diff --git a/iosApp/Podfile.lock b/iosApp/Podfile.lock index 13f40c4..5a71a7e 100644 --- a/iosApp/Podfile.lock +++ b/iosApp/Podfile.lock @@ -375,6 +375,6 @@ SPEC CHECKSUMS: Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e YYKit: 7cda43304a8dc3696c449041e2cb3107b4e236e7 -PODFILE CHECKSUM: 6c8b3a4cbbcb499c48be7819e3fe2546a0a463ba +PODFILE CHECKSUM: 4a1ed0dd46b1d5bb4c5e90ddd23567651f3f53c4 COCOAPODS: 1.16.2 diff --git a/iosApp/Pods/Manifest.lock b/iosApp/Pods/Manifest.lock index 13f40c4..5a71a7e 100644 --- a/iosApp/Pods/Manifest.lock +++ b/iosApp/Pods/Manifest.lock @@ -375,6 +375,6 @@ SPEC CHECKSUMS: Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e YYKit: 7cda43304a8dc3696c449041e2cb3107b4e236e7 -PODFILE CHECKSUM: 6c8b3a4cbbcb499c48be7819e3fe2546a0a463ba +PODFILE CHECKSUM: 4a1ed0dd46b1d5bb4c5e90ddd23567651f3f53c4 COCOAPODS: 1.16.2 diff --git a/iosApp/Pods/Pods.xcodeproj/project.pbxproj b/iosApp/Pods/Pods.xcodeproj/project.pbxproj index aab8c50..2fd31b9 100644 --- a/iosApp/Pods/Pods.xcodeproj/project.pbxproj +++ b/iosApp/Pods/Pods.xcodeproj/project.pbxproj @@ -16,7 +16,6 @@ dependencies = ( ); name = shared; - productName = shared; }; /* End PBXAggregateTarget section */ @@ -33,7 +32,7 @@ 02DD2667E8A91C38C53B4BD4334FEB1A /* QMUIPopupMenuItem.h in Headers */ = {isa = PBXBuildFile; fileRef = A445C85678E48F427EAB92744CE46240 /* QMUIPopupMenuItem.h */; settings = {ATTRIBUTES = (Public, ); }; }; 02FC6119083751FFF17F4240C103B0D2 /* QMUIMultipleDelegates.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D2FE254FA20B41D815CD27CD996313B /* QMUIMultipleDelegates.m */; }; 036069ADB7ED599B72ABA0B554DE988C /* UITabBar+QMUI.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B3DE9C0679562961726358E30E0CF4D /* UITabBar+QMUI.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 03F2428C9FE1AF75B287A39D46F646FA /* FMDB_Privacy.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 148D9AC15C4A9777E5ACBB46C03FE218 /* FMDB_Privacy.bundle */; }; + 03F2428C9FE1AF75B287A39D46F646FA /* FMDB-FMDB_Privacy in Resources */ = {isa = PBXBuildFile; fileRef = 148D9AC15C4A9777E5ACBB46C03FE218 /* FMDB-FMDB_Privacy */; }; 0423F148FF3BE83F1C768636D238EA31 /* UIGestureRecognizer+YYAdd.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E9C5BFBE54D1921986264CF1C25BF75 /* UIGestureRecognizer+YYAdd.h */; settings = {ATTRIBUTES = (Public, ); }; }; 043BE2D1F296D7B1DDFC338A17483F9E /* UIBlurEffect+QMUI.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BBB7BCFB162BF6A2493B48534E54217 /* UIBlurEffect+QMUI.m */; }; 0553071F3990D0A143B6E7819523F1FD /* QMUIDisplayLinkAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = FE0B5A61460CD24E4C38E1A728B29E1C /* QMUIDisplayLinkAnimation.m */; }; @@ -162,7 +161,7 @@ 32278CF2E1574E7C6286DF5DEFFDF4B1 /* NSArray+YYAdd.h in Headers */ = {isa = PBXBuildFile; fileRef = 26936DFB369958C0639E041CA3CBB850 /* NSArray+YYAdd.h */; settings = {ATTRIBUTES = (Public, ); }; }; 325CA20B9271F3E008234E1518B79061 /* MJRefresh-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 20CDAF1CB0DEA182EAEE0D1FCACD1753 /* MJRefresh-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 326E0584CC15454A4D382F12557A9E99 /* QMUIAssetsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EF88EAB035DD06CAFF5935D5765A5C7 /* QMUIAssetsManager.m */; }; - 327BA3DDA513422E632D3DA4A8FC60EC /* MJRefresh.Privacy.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 7E3097CFEFDA621E9FB0E62009FF87FC /* MJRefresh.Privacy.bundle */; }; + 327BA3DDA513422E632D3DA4A8FC60EC /* MJRefresh-MJRefresh.Privacy in Resources */ = {isa = PBXBuildFile; fileRef = 7E3097CFEFDA621E9FB0E62009FF87FC /* MJRefresh-MJRefresh.Privacy */; }; 32ED999775835BA5BCD74807860494CD /* QMUIPopupMenuItemViewProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 34E616BC50DCC2AEC75F14AA7258B164 /* QMUIPopupMenuItemViewProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; 337CF76F666E98CADF9C68E00197AD6E /* QMUIKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C85EE5A2BC00B81A1B7CB802F149A70 /* QMUIKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 33C42E79B137F94B3B149F264FB66DDE /* NSURL+QMUI.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FFBCC31B267852E2AD3B92F3C1402F2 /* NSURL+QMUI.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -308,7 +307,7 @@ 615C375B2B90EC501C06AC95C4166364 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B9F955BC50584733694713C914F62CB7 /* MobileCoreServices.framework */; }; 616439C72D917B6FF3998D29856C145A /* UINavigationController+QMUI.m in Sources */ = {isa = PBXBuildFile; fileRef = F39A09811531FB7C620856196C2835D0 /* UINavigationController+QMUI.m */; }; 61857C821395B868C65A8FFE4DA1B4E3 /* MJExtension.h in Headers */ = {isa = PBXBuildFile; fileRef = E5F341C5EB9D1F0917CAB1980CAFAB9B /* MJExtension.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 62CFA1DA1F44515EBEFAC8BBDCAFEA86 /* QMUIKit.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 77DEF480928809EA82047E24B3C57BA7 /* QMUIKit.bundle */; }; + 62CFA1DA1F44515EBEFAC8BBDCAFEA86 /* QMUIKit-QMUIKit in Resources */ = {isa = PBXBuildFile; fileRef = 77DEF480928809EA82047E24B3C57BA7 /* QMUIKit-QMUIKit */; }; 6329CC6F7AB03B6D607686F7801639E7 /* YYThreadSafeDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = E8FCEBB74589811CC98A0038E721E5A2 /* YYThreadSafeDictionary.h */; settings = {ATTRIBUTES = (Public, ); }; }; 634C47C0C99AED159B83DBF3DE7592F3 /* Toast.h in Headers */ = {isa = PBXBuildFile; fileRef = 9F56629659E56BEEABFC6C0DC292D68D /* Toast.h */; settings = {ATTRIBUTES = (Public, ); }; }; 63D8A88D36948E62F745594854E0677F /* FMDB.h in Headers */ = {isa = PBXBuildFile; fileRef = C4B65E889ABBBBCF67A6F75E33F4E929 /* FMDB.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -360,7 +359,7 @@ 7777DCBDDDD09DF78F650491FA9F029C /* QMUISheetPresentationSupports.h in Headers */ = {isa = PBXBuildFile; fileRef = F117A87C7FBEFEFE59FA67B7C2169487 /* QMUISheetPresentationSupports.h */; settings = {ATTRIBUTES = (Public, ); }; }; 780E0EF90C74434AF53C49C3594357C2 /* JTCalendarDayView.h in Headers */ = {isa = PBXBuildFile; fileRef = 1486A22660C5EA362429493C82D5A21B /* JTCalendarDayView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 784908F07EC38CCAF4704A2FD06C4713 /* NSDictionary+YYAdd.h in Headers */ = {isa = PBXBuildFile; fileRef = A57F94A84D2B5A9F1A86301DA551CE81 /* NSDictionary+YYAdd.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 7873F2F89CD0A435FAB776BC27BFB56A /* MJExtension.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 43EAAD2AB7E6B407E80E95F643F93D22 /* MJExtension.bundle */; }; + 7873F2F89CD0A435FAB776BC27BFB56A /* MJExtension-MJExtension in Resources */ = {isa = PBXBuildFile; fileRef = 43EAAD2AB7E6B407E80E95F643F93D22 /* MJExtension-MJExtension */; }; 7902D28FC9EF5AFEB452F508C7F266B1 /* MJRefreshAutoNormalFooter.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D1E8A0142331D3DF5CA8AA97FEC37C0 /* MJRefreshAutoNormalFooter.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7929CFE2870362624707CCEC764C20C7 /* QMUIWindowSizeMonitor.h in Headers */ = {isa = PBXBuildFile; fileRef = 70834183A5A494976C7DE256619019AF /* QMUIWindowSizeMonitor.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7989A6E79BFA78440C39F568D972305C /* MJRefresh.h in Headers */ = {isa = PBXBuildFile; fileRef = 3CDBED5F1B71979C8463CFEAB8C87D23 /* MJRefresh.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -635,10 +634,10 @@ D3F2C605B373F0902E2AB7F00545662E /* NSAttributedString+QMUI.h in Headers */ = {isa = PBXBuildFile; fileRef = 30D4A5765231AC1E7F4B145A7521BE61 /* NSAttributedString+QMUI.h */; settings = {ATTRIBUTES = (Public, ); }; }; D40D76BF0FF793CDA8FA9EFB67E7AE67 /* QMUIAsset.m in Sources */ = {isa = PBXBuildFile; fileRef = 88925A4E02D0802B4BC4424B98EB7DFC /* QMUIAsset.m */; }; D4C4BB5029ED675E59D58F2FAAAF454A /* YYImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 8F12F54D282E372A275B71743E3F9F3D /* YYImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D4D4E8BD5F155B6C32FE15226FD3315C /* QMUIResources.bundle in Resources */ = {isa = PBXBuildFile; fileRef = FFDF743B2D7D655F7364FF1154FFEDE6 /* QMUIResources.bundle */; }; + D4D4E8BD5F155B6C32FE15226FD3315C /* QMUIKit-QMUIResources in Resources */ = {isa = PBXBuildFile; fileRef = FFDF743B2D7D655F7364FF1154FFEDE6 /* QMUIKit-QMUIResources */; }; D5CFBD8B611789FC080D35D91127053F /* UIPasteboard+YYText.h in Headers */ = {isa = PBXBuildFile; fileRef = E11E78C23F020213B88F74354507BC71 /* UIPasteboard+YYText.h */; settings = {ATTRIBUTES = (Public, ); }; }; D606858436460D12F75B4B33F005EEA8 /* QMUIAssetsManager.h in Headers */ = {isa = PBXBuildFile; fileRef = AD7543DFBAEB5709781B696F48132044 /* QMUIAssetsManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D60FD96D6C7D9442E6A71E410301B3E2 /* Toast.bundle in Resources */ = {isa = PBXBuildFile; fileRef = F6A5F09CA59AF20B5A450FA1B72ECFB5 /* Toast.bundle */; }; + D60FD96D6C7D9442E6A71E410301B3E2 /* Toast-Toast in Resources */ = {isa = PBXBuildFile; fileRef = F6A5F09CA59AF20B5A450FA1B72ECFB5 /* Toast-Toast */; }; D63E3BDFA1F026C99920F7EA132C533C /* QMUISheetPresentationSupports.m in Sources */ = {isa = PBXBuildFile; fileRef = E02E057999FC194ADFF5BD09E510A59C /* QMUISheetPresentationSupports.m */; }; D663837F4347AF58660EE6F7FD426ECE /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52F6FA78ACC249DBB13C32B1446EA2E2 /* Foundation.framework */; }; D68875F0808F6E211E36E68591E30299 /* YYTextLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 509530A0C1B6312BF45926B12EDA08E7 /* YYTextLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -930,7 +929,7 @@ 144A00D8F331D7514F95182996BD0F5A /* CoreImage.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreImage.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/CoreImage.framework; sourceTree = DEVELOPER_DIR; }; 146A0435E39CB502277E47ABE2A2D502 /* YYWebImageOperation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = YYWebImageOperation.m; path = YYKit/Image/YYWebImageOperation.m; sourceTree = ""; }; 1486A22660C5EA362429493C82D5A21B /* JTCalendarDayView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = JTCalendarDayView.h; path = JTCalendar/Views/JTCalendarDayView.h; sourceTree = ""; }; - 148D9AC15C4A9777E5ACBB46C03FE218 /* FMDB_Privacy.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FMDB_Privacy.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; + 148D9AC15C4A9777E5ACBB46C03FE218 /* FMDB-FMDB_Privacy */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; name = "FMDB-FMDB_Privacy"; path = FMDB_Privacy.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 1538F878B1B88F08D3E0B10CBE652804 /* QMUIThemeManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUIThemeManager.h; path = QMUIKit/QMUIComponents/QMUITheme/QMUIThemeManager.h; sourceTree = ""; }; 155BD2EBB35A268BE50E5D16382E10CF /* UIBarItem+QMUIBadge.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIBarItem+QMUIBadge.m"; path = "QMUIKit/QMUIComponents/QMUIBadge/UIBarItem+QMUIBadge.m"; sourceTree = ""; }; 156999C714704B5CEC2FA8463D3AA837 /* UIVisualEffectView+QMUI.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIVisualEffectView+QMUI.m"; path = "QMUIKit/UIKitExtensions/UIVisualEffectView+QMUI.m"; sourceTree = ""; }; @@ -952,7 +951,7 @@ 1B55B628750B4DA149C65E6C804055EE /* QMUINavigationBarScrollingSnapAnimator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = QMUINavigationBarScrollingSnapAnimator.m; path = QMUIKit/QMUIComponents/QMUIScrollAnimator/QMUINavigationBarScrollingSnapAnimator.m; sourceTree = ""; }; 1BA4EE6FE0E42F8DA5BCE44A688D4021 /* UIViewController+QMUI.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIViewController+QMUI.h"; path = "QMUIKit/UIKitExtensions/UIViewController+QMUI.h"; sourceTree = ""; }; 1BBB7BCFB162BF6A2493B48534E54217 /* UIBlurEffect+QMUI.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIBlurEffect+QMUI.m"; path = "QMUIKit/UIKitExtensions/UIBlurEffect+QMUI.m"; sourceTree = ""; }; - 1BF46B4DF5FE6C6555418BB5DB0D22CE /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = MJRefresh/PrivacyInfo.xcprivacy; sourceTree = ""; }; + 1BF46B4DF5FE6C6555418BB5DB0D22CE /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = MJRefresh/PrivacyInfo.xcprivacy; sourceTree = ""; }; 1C5B0ACBC0062108B17D261A2A00F7A4 /* QMUITips.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUITips.h; path = QMUIKit/QMUIComponents/QMUITips.h; sourceTree = ""; }; 1C85EE5A2BC00B81A1B7CB802F149A70 /* QMUIKit.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUIKit.h; path = QMUIKit/QMUIKit.h; sourceTree = ""; }; 1CB507858B4CDF0C15A6DB438078A24F /* UIImage+YYAdd.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImage+YYAdd.h"; path = "YYKit/Base/UIKit/UIImage+YYAdd.h"; sourceTree = ""; }; @@ -968,7 +967,7 @@ 1F625FE003C7210B84C3183786251E4F /* NSKeyedUnarchiver+YYAdd.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSKeyedUnarchiver+YYAdd.m"; path = "YYKit/Base/Foundation/NSKeyedUnarchiver+YYAdd.m"; sourceTree = ""; }; 1FC00E30D2DF1107D7B123F9753645CE /* NSParagraphStyle+YYText.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSParagraphStyle+YYText.h"; path = "YYKit/Text/String/NSParagraphStyle+YYText.h"; sourceTree = ""; }; 1FFBCC31B267852E2AD3B92F3C1402F2 /* NSURL+QMUI.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSURL+QMUI.h"; path = "QMUIKit/UIKitExtensions/NSURL+QMUI.h"; sourceTree = ""; }; - 1FFED36A657123030ABB700256D73F15 /* Masonry.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Masonry.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 1FFED36A657123030ABB700256D73F15 /* Masonry */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Masonry; path = Masonry.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 203D7A8266F5D1948598F9634266001B /* NSString+QMUI.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSString+QMUI.h"; path = "QMUIKit/UIKitExtensions/NSString+QMUI.h"; sourceTree = ""; }; 20CDAF1CB0DEA182EAEE0D1FCACD1753 /* MJRefresh-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MJRefresh-umbrella.h"; sourceTree = ""; }; 210CE7656DBF9E5DEB549A11738B6860 /* MJRefresh.bundle */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "wrapper.plug-in"; name = MJRefresh.bundle; path = MJRefresh/MJRefresh.bundle; sourceTree = ""; }; @@ -1002,7 +1001,7 @@ 2A1A3F3F9A5AA7D840B285E28A248B0A /* QMUITableView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUITableView.h; path = QMUIKit/QMUIComponents/QMUITableView.h; sourceTree = ""; }; 2A556F85B44157EE6E41E0CF4E31D7AB /* FMResultSet.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FMResultSet.h; path = src/fmdb/FMResultSet.h; sourceTree = ""; }; 2A6E56A5F1295AB6A110D6C498B90369 /* FMDB-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "FMDB-prefix.pch"; sourceTree = ""; }; - 2B276B0A79173A1D6E83C9B4FB9A4A57 /* MJExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MJExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2B276B0A79173A1D6E83C9B4FB9A4A57 /* MJExtension */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = MJExtension; path = MJExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 2B5DADD3C70404C695F2048EFB9519E9 /* Masonry.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Masonry.debug.xcconfig; sourceTree = ""; }; 2B8C909CE5E7C512D6F7272FA5C056EB /* NSCharacterSet+QMUI.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSCharacterSet+QMUI.h"; path = "QMUIKit/UIKitExtensions/NSCharacterSet+QMUI.h"; sourceTree = ""; }; 2BAE7CE9D130CC9182E8DADCF821BB75 /* UINavigationItem+QMUI.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UINavigationItem+QMUI.h"; path = "QMUIKit/UIKitExtensions/UINavigationItem+QMUI.h"; sourceTree = ""; }; @@ -1076,7 +1075,7 @@ 43880A90B2EB6BD442270F23831D8827 /* MASViewAttribute.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MASViewAttribute.m; path = Masonry/MASViewAttribute.m; sourceTree = ""; }; 439B05E8444163695926633CD77A9B8B /* UILabel+QMUI.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UILabel+QMUI.h"; path = "QMUIKit/UIKitExtensions/UILabel+QMUI.h"; sourceTree = ""; }; 43DE542563498D897EAC83BECC760207 /* UIView+Toast.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIView+Toast.m"; path = "Toast/UIView+Toast.m"; sourceTree = ""; }; - 43EAAD2AB7E6B407E80E95F643F93D22 /* MJExtension.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MJExtension.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; + 43EAAD2AB7E6B407E80E95F643F93D22 /* MJExtension-MJExtension */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; name = "MJExtension-MJExtension"; path = MJExtension.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 44BAE1FBF34F0B7C012B8A7CDEC17793 /* MBProgressHUD.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = MBProgressHUD.modulemap; sourceTree = ""; }; 44CE09DAE6F57AF77FD209344243F791 /* NSObject+MJKeyValue.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSObject+MJKeyValue.m"; path = "MJExtension/NSObject+MJKeyValue.m"; sourceTree = ""; }; 45322EEC3B916922730EE5121AD496DC /* MJRefresh.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MJRefresh.debug.xcconfig; sourceTree = ""; }; @@ -1101,7 +1100,7 @@ 4AD1803C1C0940757571408AF4621AA2 /* QMUIEasings.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUIEasings.h; path = QMUIKit/QMUIComponents/QMUIAnimation/QMUIEasings.h; sourceTree = ""; }; 4AD7C2DC034DB543E9D24CD093F10210 /* NSObject+YYAddForARC.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSObject+YYAddForARC.m"; path = "YYKit/Base/Foundation/NSObject+YYAddForARC.m"; sourceTree = ""; }; 4AE8ECADEE4F9BE84B92B065692843F8 /* UIMenuController+QMUI.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIMenuController+QMUI.h"; path = "QMUIKit/UIKitExtensions/UIMenuController+QMUI.h"; sourceTree = ""; }; - 4AF171581392AD234F23BE913F0C22FE /* DateTools.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DateTools.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 4AF171581392AD234F23BE913F0C22FE /* DateTools */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = DateTools; path = DateTools.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 4B192178A695EE4EC2CC5B5AD3E0D06D /* NSNotificationCenter+YYAdd.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSNotificationCenter+YYAdd.m"; path = "YYKit/Base/Foundation/NSNotificationCenter+YYAdd.m"; sourceTree = ""; }; 4BF8167F1F16B53D0D36DA6486C24AB5 /* UIView+QMUITheme.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIView+QMUITheme.m"; path = "QMUIKit/QMUIComponents/QMUITheme/UIView+QMUITheme.m"; sourceTree = ""; }; 4C8567BC18809B231D1174CCA61DEE84 /* JTCalendarWeek.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = JTCalendarWeek.h; path = JTCalendar/Protocols/JTCalendarWeek.h; sourceTree = ""; }; @@ -1142,7 +1141,7 @@ 5522334BCBE27DD03CEDF79812364DB8 /* YYTextEffectWindow.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = YYTextEffectWindow.m; path = YYKit/Text/Component/YYTextEffectWindow.m; sourceTree = ""; }; 5577FE954794383A429608BE236F2105 /* UIView+QMUITheme.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIView+QMUITheme.h"; path = "QMUIKit/QMUIComponents/QMUITheme/UIView+QMUITheme.h"; sourceTree = ""; }; 55ABB06C8A1800962A74E007E7733796 /* Pods-iosApp-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-iosApp-frameworks.sh"; sourceTree = ""; }; - 55E0AFD333353D71ACC2207149E879D6 /* Toast.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Toast.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 55E0AFD333353D71ACC2207149E879D6 /* Toast */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Toast; path = Toast.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 560DBC31EF8D71C0CE5ACE67B84F7467 /* ViewController+MASAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "ViewController+MASAdditions.m"; path = "Masonry/ViewController+MASAdditions.m"; sourceTree = ""; }; 561BE7E02597857878BBD6393DB888B5 /* QMUICellSizeKeyCache.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = QMUICellSizeKeyCache.m; path = QMUIKit/QMUIComponents/QMUICellSizeKeyCache/QMUICellSizeKeyCache.m; sourceTree = ""; }; 56E257C81857ACA18812DE13B487E283 /* UIImage+QMUITheme.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIImage+QMUITheme.m"; path = "QMUIKit/QMUIComponents/QMUITheme/UIImage+QMUITheme.m"; sourceTree = ""; }; @@ -1236,7 +1235,7 @@ 7717F2669547DFACA2E8350992F2C5C4 /* QMUIStaticTableViewCellDataSource.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = QMUIStaticTableViewCellDataSource.m; path = QMUIKit/QMUIComponents/StaticTableView/QMUIStaticTableViewCellDataSource.m; sourceTree = ""; }; 77A31BF82F3B213410AA24EBC69F9864 /* DTTimePeriodChain.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTTimePeriodChain.m; path = DateTools/DateTools/DTTimePeriodChain.m; sourceTree = ""; }; 77DC5902FD0E09544E689A46772929E9 /* QMUITabBarViewController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = QMUITabBarViewController.m; path = QMUIKit/QMUIMainFrame/QMUITabBarViewController.m; sourceTree = ""; }; - 77DEF480928809EA82047E24B3C57BA7 /* QMUIKit.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = QMUIKit.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; + 77DEF480928809EA82047E24B3C57BA7 /* QMUIKit-QMUIKit */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; name = "QMUIKit-QMUIKit"; path = QMUIKit.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 79F980119DEEB9C4D92E87FE7796D2C4 /* UIBezierPath+QMUI.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIBezierPath+QMUI.m"; path = "QMUIKit/UIKitExtensions/UIBezierPath+QMUI.m"; sourceTree = ""; }; 7A2E08F52007C2164856EAD4FFA4BD7C /* ResourceBundle-QMUIKit-QMUIKit-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "ResourceBundle-QMUIKit-QMUIKit-Info.plist"; sourceTree = ""; }; 7B1A02173FD541731473196E24567A85 /* YYKitMacro.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = YYKitMacro.h; path = YYKit/Base/YYKitMacro.h; sourceTree = ""; }; @@ -1248,7 +1247,7 @@ 7D722EC0B7C39B80EC104C18DD77B22D /* YYKit.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = YYKit.modulemap; sourceTree = ""; }; 7DC03FC27C6A637A4897F2F2F113F50F /* QMUIImagePickerPreviewViewController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = QMUIImagePickerPreviewViewController.m; path = QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerPreviewViewController.m; sourceTree = ""; }; 7E0AE8C46252EC77CD69CCA94F8EAA10 /* QMUISegmentedControl.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = QMUISegmentedControl.m; path = QMUIKit/QMUIComponents/QMUISegmentedControl.m; sourceTree = ""; }; - 7E3097CFEFDA621E9FB0E62009FF87FC /* MJRefresh.Privacy.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MJRefresh.Privacy.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; + 7E3097CFEFDA621E9FB0E62009FF87FC /* MJRefresh-MJRefresh.Privacy */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; name = "MJRefresh-MJRefresh.Privacy"; path = MJRefresh.Privacy.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 7E5DA53E5B7691BE49BDF52955794E19 /* UIImageView+YYWebImage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImageView+YYWebImage.h"; path = "YYKit/Image/Categories/UIImageView+YYWebImage.h"; sourceTree = ""; }; 7E6DFB04A0486C105583FCB120FC7F5E /* QMUIConsole.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUIConsole.h; path = QMUIKit/QMUIComponents/QMUIConsole/QMUIConsole.h; sourceTree = ""; }; 7E9C2CDA618DA37BCE638643ADBCFCCC /* NSObject+QMUI.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSObject+QMUI.m"; path = "QMUIKit/UIKitExtensions/NSObject+QMUI.m"; sourceTree = ""; }; @@ -1272,7 +1271,7 @@ 83B8D4168ED903D88916BCD33A4BD3DF /* MJRefreshConfig.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshConfig.m; path = MJRefresh/MJRefreshConfig.m; sourceTree = ""; }; 84E03CF4DDF15DB471C5CDCAE7D4BF35 /* UIFont+QMUI.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIFont+QMUI.m"; path = "QMUIKit/UIKitExtensions/UIFont+QMUI.m"; sourceTree = ""; }; 8567FB68C84B78EDD5692F7CB8D37560 /* CAAnimation+QMUI.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "CAAnimation+QMUI.h"; path = "QMUIKit/QMUIComponents/CAAnimation+QMUI.h"; sourceTree = ""; }; - 8596B7A0C5A3E188061E9EDDAB573FCE /* QMUIKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = QMUIKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 8596B7A0C5A3E188061E9EDDAB573FCE /* QMUIKit */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = QMUIKit; path = QMUIKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 859F65CBAD1C7F4130531CCA3CBC642C /* UIControl+YYAdd.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIControl+YYAdd.m"; path = "YYKit/Base/UIKit/UIControl+YYAdd.m"; sourceTree = ""; }; 85B823E57AA708F105F6D3FB2B777E6C /* UITextView+QMUI.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UITextView+QMUI.m"; path = "QMUIKit/UIKitExtensions/UITextView+QMUI.m"; sourceTree = ""; }; 875F3DC8605226054EF379B96768682B /* YYThreadSafeDictionary.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = YYThreadSafeDictionary.m; path = YYKit/Utility/YYThreadSafeDictionary.m; sourceTree = ""; }; @@ -1285,7 +1284,7 @@ 8A84838CC30AC4504FE90E893AA3DA1B /* NSObject+YYAddForKVO.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSObject+YYAddForKVO.h"; path = "YYKit/Base/Foundation/NSObject+YYAddForKVO.h"; sourceTree = ""; }; 8AA10EA2ACEBF8BAE84C9BAA62D98A5B /* QMUIKit-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "QMUIKit-prefix.pch"; sourceTree = ""; }; 8B3DE9C0679562961726358E30E0CF4D /* UITabBar+QMUI.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UITabBar+QMUI.h"; path = "QMUIKit/UIKitExtensions/UITabBar+QMUI.h"; sourceTree = ""; }; - 8B8FAB0D627B17EDE1366984278705D9 /* MBProgressHUD.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MBProgressHUD.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 8B8FAB0D627B17EDE1366984278705D9 /* MBProgressHUD */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = MBProgressHUD; path = MBProgressHUD.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8BC38D1D00FF6962FC7D0377F12C10BB /* JTCalendarWeekDayView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = JTCalendarWeekDayView.m; path = JTCalendar/Views/JTCalendarWeekDayView.m; sourceTree = ""; }; 8C26259258740078C8DCAEA6A23CB14C /* QMUIImagePickerCollectionViewCell.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = QMUIImagePickerCollectionViewCell.m; path = QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerCollectionViewCell.m; sourceTree = ""; }; 8C8C1AEAB496AE6EE122EA570CD73910 /* Toast-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Toast-umbrella.h"; sourceTree = ""; }; @@ -1296,7 +1295,7 @@ 8DE5F38A08177EE9C09B6B53D7064B8A /* JTCalendarManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = JTCalendarManager.m; path = JTCalendar/JTCalendarManager.m; sourceTree = ""; }; 8ED88CA768B5C686E04F982BE1C7160A /* QMUIImagePickerHelper.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUIImagePickerHelper.h; path = QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerHelper.h; sourceTree = ""; }; 8EE583DFD1CABA9BA581808C375F4E4D /* Toast-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Toast-Info.plist"; sourceTree = ""; }; - 8EF3F6D5B12FD742052D13B61D5C814F /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = privacy/PrivacyInfo.xcprivacy; sourceTree = ""; }; + 8EF3F6D5B12FD742052D13B61D5C814F /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = privacy/PrivacyInfo.xcprivacy; sourceTree = ""; }; 8F12F54D282E372A275B71743E3F9F3D /* YYImage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = YYImage.h; path = YYKit/Image/YYImage.h; sourceTree = ""; }; 8FC8C4A87070241C640DCFAA14CCC8CB /* YYTextLine.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = YYTextLine.m; path = YYKit/Text/Component/YYTextLine.m; sourceTree = ""; }; 904A84447FB88FC309A48C948FA1E289 /* QMUIDialogViewController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = QMUIDialogViewController.m; path = QMUIKit/QMUIComponents/QMUIDialogViewController.m; sourceTree = ""; }; @@ -1323,7 +1322,7 @@ 94B9B8910B23A0BF1D0F9763A0DAB01A /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/SystemConfiguration.framework; sourceTree = DEVELOPER_DIR; }; 9537B8ABCEC1F17A6241E8C414A58019 /* MKAnnotationView+YYWebImage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "MKAnnotationView+YYWebImage.m"; path = "YYKit/Image/Categories/MKAnnotationView+YYWebImage.m"; sourceTree = ""; }; 9550C737553A5A30C38DE422329C7D20 /* QMUIKit.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = QMUIKit.modulemap; sourceTree = ""; }; - 95784DA3E052291A415D111DC394DDB0 /* JTCalendar.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = JTCalendar.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 95784DA3E052291A415D111DC394DDB0 /* JTCalendar */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = JTCalendar; path = JTCalendar.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 961F2D673D70A5598ED8D4C01E8FE173 /* YYMemoryCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = YYMemoryCache.h; path = YYKit/Cache/YYMemoryCache.h; sourceTree = ""; }; 966B7812D4FDE4B60DFBC087B973DB41 /* MJRefreshNormalHeader.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshNormalHeader.m; path = MJRefresh/Custom/Header/MJRefreshNormalHeader.m; sourceTree = ""; }; 969AEBD6B204DBEAAC40D31E0F145485 /* UINavigationBar+Transition.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UINavigationBar+Transition.h"; path = "QMUIKit/QMUIComponents/NavigationBarTransition/UINavigationBar+Transition.h"; sourceTree = ""; }; @@ -1346,7 +1345,7 @@ 9D3AB291AAA77506CEEA0B3275651F46 /* QMUILogger+QMUIConfigurationTemplate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "QMUILogger+QMUIConfigurationTemplate.h"; path = "QMUIKit/QMUIComponents/QMUILogger+QMUIConfigurationTemplate.h"; sourceTree = ""; }; 9D658656CBA1A3194388574E9D1C972F /* QMUIDialogViewController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUIDialogViewController.h; path = QMUIKit/QMUIComponents/QMUIDialogViewController.h; sourceTree = ""; }; 9D931D0D1DD2F6F967102BE1C27B58AC /* QMUIPopupMenuItemView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUIPopupMenuItemView.h; path = QMUIKit/QMUIComponents/QMUIPopupMenuView/QMUIPopupMenuItemView.h; sourceTree = ""; }; - 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 9DB09952EC2FA5B9CA5A148F7B6EF258 /* NSData+YYAdd.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSData+YYAdd.m"; path = "YYKit/Base/Foundation/NSData+YYAdd.m"; sourceTree = ""; }; 9E56471C7EDE6544F1C2DF241DC0F337 /* QMUIScrollAnimator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUIScrollAnimator.h; path = QMUIKit/QMUIComponents/QMUIScrollAnimator/QMUIScrollAnimator.h; sourceTree = ""; }; 9EA987614A541E9052D4B2379ECB6CF3 /* AssetsLibrary.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AssetsLibrary.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/AssetsLibrary.framework; sourceTree = DEVELOPER_DIR; }; @@ -1375,7 +1374,7 @@ A366D8F61B66F9B0644F4C828EBC630F /* NSThread+YYAdd.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSThread+YYAdd.m"; path = "YYKit/Base/Foundation/NSThread+YYAdd.m"; sourceTree = ""; }; A37DCDD5739412E29BC59828B9D6F6D1 /* UITableViewCell+QMUI.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UITableViewCell+QMUI.m"; path = "QMUIKit/UIKitExtensions/UITableViewCell+QMUI.m"; sourceTree = ""; }; A39ECB8468A0684D587FCCD9CF5B150D /* UIApplication+QMUI.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIApplication+QMUI.h"; path = "QMUIKit/UIKitExtensions/UIApplication+QMUI.h"; sourceTree = ""; }; - A3A80BA70CFB7F75C5391BEBBBA8C9DA /* FMDB.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FMDB.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A3A80BA70CFB7F75C5391BEBBBA8C9DA /* FMDB */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = FMDB; path = FMDB.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A3E3B2AFE230CEA9E7F8EA0DA684EC02 /* QMUITabBarViewController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUITabBarViewController.h; path = QMUIKit/QMUIMainFrame/QMUITabBarViewController.h; sourceTree = ""; }; A445C85678E48F427EAB92744CE46240 /* QMUIPopupMenuItem.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUIPopupMenuItem.h; path = QMUIKit/QMUIComponents/QMUIPopupMenuView/QMUIPopupMenuItem.h; sourceTree = ""; }; A52375CEBA5EEE849E2C3A713CFE3B63 /* MJRefreshAutoStateFooter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshAutoStateFooter.m; path = MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.m; sourceTree = ""; }; @@ -1387,7 +1386,7 @@ A7D8EBE0A05AD61C8C7D8762FF98BD09 /* QMUIImagePreviewView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = QMUIImagePreviewView.m; path = QMUIKit/QMUIComponents/QMUIImagePreviewView/QMUIImagePreviewView.m; sourceTree = ""; }; A7FACA30C97F3BFC83588B3E50AA5B4B /* DateTools-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "DateTools-Info.plist"; sourceTree = ""; }; A8169D3089AD4B331710912DFEEC08C9 /* QMUILogManagerViewController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = QMUILogManagerViewController.m; path = QMUIKit/QMUIComponents/QMUILogManagerViewController.m; sourceTree = ""; }; - A81CAF7165BA36F56425571C70138AA9 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = QMUIKit/PrivacyInfo.xcprivacy; sourceTree = ""; }; + A81CAF7165BA36F56425571C70138AA9 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = QMUIKit/PrivacyInfo.xcprivacy; sourceTree = ""; }; A82B1BED716DE5D52061470409334F68 /* MJRefresh.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = MJRefresh.modulemap; sourceTree = ""; }; A8664BF30872D5794A2246C65BE5C62F /* Photos.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Photos.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/Photos.framework; sourceTree = DEVELOPER_DIR; }; A86C1CDEA08E9FEFC85D6165E784B3A5 /* UINavigationBar+QMUIBarProtocol.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UINavigationBar+QMUIBarProtocol.h"; path = "QMUIKit/UIKitExtensions/QMUIBarProtocol/UINavigationBar+QMUIBarProtocol.h"; sourceTree = ""; }; @@ -1421,7 +1420,7 @@ AF5B039453FE62D663CB7CB29ED98F0A /* MJRefresh.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MJRefresh.release.xcconfig; sourceTree = ""; }; AFEE9CC03509452585E6C4679E10464B /* QMUICommonViewController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUICommonViewController.h; path = QMUIKit/QMUIMainFrame/QMUICommonViewController.h; sourceTree = ""; }; B04C735B97EC4B936D408F673F7FCB28 /* QMUICellHeightKeyCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUICellHeightKeyCache.h; path = QMUIKit/QMUIComponents/QMUICellHeightKeyCache/QMUICellHeightKeyCache.h; sourceTree = ""; }; - B097DD7534E741D5C41838011D755842 /* Pods_iosApp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_iosApp.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B097DD7534E741D5C41838011D755842 /* Pods-iosApp */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = "Pods-iosApp"; path = Pods_iosApp.framework; sourceTree = BUILT_PRODUCTS_DIR; }; B0E0499F15203226BF565DB1BFB6DE2B /* QMUITheme.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUITheme.h; path = QMUIKit/QMUIComponents/QMUITheme/QMUITheme.h; sourceTree = ""; }; B0FE5A520EE5266F1CBEB05466DE30C6 /* NSString+YYAdd.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSString+YYAdd.h"; path = "YYKit/Base/Foundation/NSString+YYAdd.h"; sourceTree = ""; }; B18E769BEE575C532C4B87E1AC4D560D /* QMUICore.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUICore.h; path = QMUIKit/QMUICore/QMUICore.h; sourceTree = ""; }; @@ -1448,7 +1447,7 @@ B6DB9D1615EE8A837522538688F4B777 /* NSBundle+MJRefresh.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSBundle+MJRefresh.h"; path = "MJRefresh/NSBundle+MJRefresh.h"; sourceTree = ""; }; B7925FFC9C6E0D7F7E94846D2F6570FC /* QMUIImagePickerViewController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = QMUIImagePickerViewController.m; path = QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerViewController.m; sourceTree = ""; }; B7DDEBFFF99F2983FADE57DADF047954 /* Masonry-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Masonry-Info.plist"; sourceTree = ""; }; - B859C319A7199FDF80B64D597C478C52 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = MJExtension/PrivacyInfo.xcprivacy; sourceTree = ""; }; + B859C319A7199FDF80B64D597C478C52 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = MJExtension/PrivacyInfo.xcprivacy; sourceTree = ""; }; B87072D3B9792B6CF15677BB3187DEC1 /* FMDatabaseQueue.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FMDatabaseQueue.h; path = src/fmdb/FMDatabaseQueue.h; sourceTree = ""; }; B92FF99DE7C65FF0FE229B85F4375DF2 /* UIBarButtonItem+YYAdd.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIBarButtonItem+YYAdd.h"; path = "YYKit/Base/UIKit/UIBarButtonItem+YYAdd.h"; sourceTree = ""; }; B93BB930C684F4F41D32F3208BD05836 /* Toast-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Toast-dummy.m"; sourceTree = ""; }; @@ -1564,20 +1563,20 @@ DFC8EA9D75040D5DF7E41A9EB8B06365 /* UIView+QMUI.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIView+QMUI.h"; path = "QMUIKit/UIKitExtensions/UIView+QMUI.h"; sourceTree = ""; }; E02E057999FC194ADFF5BD09E510A59C /* QMUISheetPresentationSupports.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = QMUISheetPresentationSupports.m; path = QMUIKit/QMUIComponents/QMUISheetPresentation/QMUISheetPresentationSupports.m; sourceTree = ""; }; E05874626ECD90A0246CAAB55D4EFF36 /* NSBundle+MJRefresh.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSBundle+MJRefresh.m"; path = "MJRefresh/NSBundle+MJRefresh.m"; sourceTree = ""; }; - E0808F98C2488C041B64234F38FB396F /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = Toast/Resources/PrivacyInfo.xcprivacy; sourceTree = ""; }; + E0808F98C2488C041B64234F38FB396F /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = Toast/Resources/PrivacyInfo.xcprivacy; sourceTree = ""; }; E09E3B2B558328C84304AF372F4A5194 /* JTCalendarWeekView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = JTCalendarWeekView.m; path = JTCalendar/Views/JTCalendarWeekView.m; sourceTree = ""; }; E11E78C23F020213B88F74354507BC71 /* UIPasteboard+YYText.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIPasteboard+YYText.h"; path = "YYKit/Text/String/UIPasteboard+YYText.h"; sourceTree = ""; }; E23CE514570EF1F8D4C5876F9E01B060 /* UIControl+YYAdd.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIControl+YYAdd.h"; path = "YYKit/Base/UIKit/UIControl+YYAdd.h"; sourceTree = ""; }; E2E022FDBAEF5A5C4AC8E7AD124F0877 /* UITableView+YYAdd.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UITableView+YYAdd.h"; path = "YYKit/Base/UIKit/UITableView+YYAdd.h"; sourceTree = ""; }; - E2E6BF268E792329201BC2BF5DE0B8DB /* shared.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; path = shared.podspec; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + E2E6BF268E792329201BC2BF5DE0B8DB /* shared.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; path = shared.podspec; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; E2FC95BB9F61DE49FA89BFDBE60FA448 /* UIInterface+QMUI.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIInterface+QMUI.m"; path = "QMUIKit/UIKitExtensions/UIInterface+QMUI.m"; sourceTree = ""; }; - E325DCCD357AF9DF2F7C506424659D7E /* YYKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = YYKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + E325DCCD357AF9DF2F7C506424659D7E /* YYKit */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = YYKit; path = YYKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E39BE12967CFA5BCDF680F7E8376943C /* shared.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = shared.release.xcconfig; sourceTree = ""; }; E421DAC2E39B321B2E8F66F48872AFC1 /* NSString+YYAdd.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSString+YYAdd.m"; path = "YYKit/Base/Foundation/NSString+YYAdd.m"; sourceTree = ""; }; E462E23B3674BF94EAB1504D506F2803 /* Pods-iosApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-iosApp.debug.xcconfig"; sourceTree = ""; }; E46C60D542D0A844448162142EC75414 /* View+MASAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "View+MASAdditions.m"; path = "Masonry/View+MASAdditions.m"; sourceTree = ""; }; E47DBF9B62AEE05EBC325255EEF945CF /* QMUIBarProtocolPrivate.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = QMUIBarProtocolPrivate.m; path = QMUIKit/UIKitExtensions/QMUIBarProtocol/QMUIBarProtocolPrivate.m; sourceTree = ""; }; - E49D6D248DD1CEE584E6776B9164A1B2 /* MJRefresh.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MJRefresh.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + E49D6D248DD1CEE584E6776B9164A1B2 /* MJRefresh */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = MJRefresh; path = MJRefresh.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E4C923318724794E3CC670804C2D6A6B /* Pods-iosApp-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-iosApp-acknowledgements.markdown"; sourceTree = ""; }; E54CCF51B605536EFA83945EF94B4EEA /* YYTextParser.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = YYTextParser.m; path = YYKit/Text/String/YYTextParser.m; sourceTree = ""; }; E5826B9926F269D68CAC9BD758B80CA3 /* QMUIKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = QMUIKit.debug.xcconfig; sourceTree = ""; }; @@ -1618,7 +1617,7 @@ EF049A659713139834385CEE82C69609 /* UIWindow+QMUI.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIWindow+QMUI.m"; path = "QMUIKit/UIKitExtensions/UIWindow+QMUI.m"; sourceTree = ""; }; EFA9DC5FB92D08D8950DDBD1310F1D22 /* QMUIButton.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUIButton.h; path = QMUIKit/QMUIComponents/QMUIButton/QMUIButton.h; sourceTree = ""; }; EFBDCA93B4680A2BB8A955F659251FA1 /* FMResultSet.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FMResultSet.m; path = src/fmdb/FMResultSet.m; sourceTree = ""; }; - F064C0D7CE795102A652A82AEBBA7514 /* compose-resources */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = folder; name = "compose-resources"; path = "build/compose/cocoapods/compose-resources"; sourceTree = ""; }; + F064C0D7CE795102A652A82AEBBA7514 /* compose-resources */ = {isa = PBXFileReference; includeInIndex = 1; name = "compose-resources"; path = "build/compose/cocoapods/compose-resources"; sourceTree = ""; }; F07E48F2C3EE67A8994B426EF4BF07AE /* JTCalendarMenuView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = JTCalendarMenuView.h; path = JTCalendar/Views/JTCalendarMenuView.h; sourceTree = ""; }; F117A87C7FBEFEFE59FA67B7C2169487 /* QMUISheetPresentationSupports.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUISheetPresentationSupports.h; path = QMUIKit/QMUIComponents/QMUISheetPresentation/QMUISheetPresentationSupports.h; sourceTree = ""; }; F123B632FFC891BD7ACF07399B314C59 /* MJRefreshStateTrailer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshStateTrailer.m; path = MJRefresh/Custom/Trailer/MJRefreshStateTrailer.m; sourceTree = ""; }; @@ -1641,7 +1640,7 @@ F59C3DC9BD951FF1F657BADC8BA2E399 /* Images.xcassets */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = QMUIKit/QMUIResources/Images.xcassets; sourceTree = ""; }; F660573EA68F19AEBD85810F770B605E /* NSNumber+YYAdd.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSNumber+YYAdd.m"; path = "YYKit/Base/Foundation/NSNumber+YYAdd.m"; sourceTree = ""; }; F68D082E47A4E2F4A0E530138B6A04DF /* YYSpriteSheetImage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = YYSpriteSheetImage.h; path = YYKit/Image/YYSpriteSheetImage.h; sourceTree = ""; }; - F6A5F09CA59AF20B5A450FA1B72ECFB5 /* Toast.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Toast.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; + F6A5F09CA59AF20B5A450FA1B72ECFB5 /* Toast-Toast */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; name = "Toast-Toast"; path = Toast.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; F7379BE8F406F3C72A6231E3793B2664 /* YYKVStorage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = YYKVStorage.m; path = YYKit/Cache/YYKVStorage.m; sourceTree = ""; }; F79D3EEA43B8DB0BC3CCB347E263757D /* UIActivityIndicatorView+QMUI.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIActivityIndicatorView+QMUI.m"; path = "QMUIKit/UIKitExtensions/UIActivityIndicatorView+QMUI.m"; sourceTree = ""; }; F7BFCD2C98284A7E9D224C33B48B3F7F /* NSNumber+QMUI.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSNumber+QMUI.h"; path = "QMUIKit/UIKitExtensions/NSNumber+QMUI.h"; sourceTree = ""; }; @@ -1670,7 +1669,7 @@ FEF639B20DD6F38FF8EBB3EC61143B7D /* UIView+YYAdd.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIView+YYAdd.h"; path = "YYKit/Base/UIKit/UIView+YYAdd.h"; sourceTree = ""; }; FF1C43BE49695E132A0C408E341DA0D7 /* YYKit-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "YYKit-dummy.m"; sourceTree = ""; }; FFAF591ACE0B050AED1F749C6E2F9444 /* JTVerticalCalendarView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = JTVerticalCalendarView.h; path = JTCalendar/Views/JTVerticalCalendarView.h; sourceTree = ""; }; - FFDF743B2D7D655F7364FF1154FFEDE6 /* QMUIResources.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = QMUIResources.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; + FFDF743B2D7D655F7364FF1154FFEDE6 /* QMUIKit-QMUIResources */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; name = "QMUIKit-QMUIResources"; path = QMUIResources.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1992,6 +1991,7 @@ 560DBC31EF8D71C0CE5ACE67B84F7467 /* ViewController+MASAdditions.m */, 7BBCFB40EB87A89F94E866E9E40EB17F /* Support Files */, ); + name = Masonry; path = Masonry; sourceTree = ""; }; @@ -2091,6 +2091,7 @@ 6A976840C663BDE1D11F983C55587F48 /* MBProgressHUD.m */, DBF5826E493B9701A1E7031256471519 /* Support Files */, ); + name = MBProgressHUD; path = MBProgressHUD; sourceTree = ""; }; @@ -2155,6 +2156,7 @@ AE83BD689E0100F727209055F1A8CBFE /* JTVerticalCalendarView.m */, 35368B3F77B483377BBB9CCA2D1EDEBA /* Support Files */, ); + name = JTCalendar; path = JTCalendar; sourceTree = ""; }; @@ -2185,6 +2187,7 @@ 76CE4D3A7AE1A8E71D8FD57706767234 /* Resources */, AA5316CB55C7534E376E3DF36109F8F9 /* Support Files */, ); + name = MJExtension; path = MJExtension; sourceTree = ""; }; @@ -2194,6 +2197,7 @@ 30F3A671CAA8FEC94E6F14DD3507038D /* Core */, 8B5DCD800073497DDB7B0F7613A5CDAA /* Support Files */, ); + name = FMDB; path = FMDB; sourceTree = ""; }; @@ -2344,23 +2348,23 @@ 3A74C13BD22F5258125530629A5F8D14 /* Products */ = { isa = PBXGroup; children = ( - 4AF171581392AD234F23BE913F0C22FE /* DateTools.framework */, - A3A80BA70CFB7F75C5391BEBBBA8C9DA /* FMDB.framework */, - 148D9AC15C4A9777E5ACBB46C03FE218 /* FMDB_Privacy.bundle */, - 95784DA3E052291A415D111DC394DDB0 /* JTCalendar.framework */, - 1FFED36A657123030ABB700256D73F15 /* Masonry.framework */, - 8B8FAB0D627B17EDE1366984278705D9 /* MBProgressHUD.framework */, - 2B276B0A79173A1D6E83C9B4FB9A4A57 /* MJExtension.framework */, - 43EAAD2AB7E6B407E80E95F643F93D22 /* MJExtension.bundle */, - E49D6D248DD1CEE584E6776B9164A1B2 /* MJRefresh.framework */, - 7E3097CFEFDA621E9FB0E62009FF87FC /* MJRefresh.Privacy.bundle */, - B097DD7534E741D5C41838011D755842 /* Pods_iosApp.framework */, - 8596B7A0C5A3E188061E9EDDAB573FCE /* QMUIKit.framework */, - 77DEF480928809EA82047E24B3C57BA7 /* QMUIKit.bundle */, - FFDF743B2D7D655F7364FF1154FFEDE6 /* QMUIResources.bundle */, - 55E0AFD333353D71ACC2207149E879D6 /* Toast.framework */, - F6A5F09CA59AF20B5A450FA1B72ECFB5 /* Toast.bundle */, - E325DCCD357AF9DF2F7C506424659D7E /* YYKit.framework */, + 4AF171581392AD234F23BE913F0C22FE /* DateTools */, + A3A80BA70CFB7F75C5391BEBBBA8C9DA /* FMDB */, + 148D9AC15C4A9777E5ACBB46C03FE218 /* FMDB-FMDB_Privacy */, + 95784DA3E052291A415D111DC394DDB0 /* JTCalendar */, + 1FFED36A657123030ABB700256D73F15 /* Masonry */, + 8B8FAB0D627B17EDE1366984278705D9 /* MBProgressHUD */, + 2B276B0A79173A1D6E83C9B4FB9A4A57 /* MJExtension */, + 43EAAD2AB7E6B407E80E95F643F93D22 /* MJExtension-MJExtension */, + E49D6D248DD1CEE584E6776B9164A1B2 /* MJRefresh */, + 7E3097CFEFDA621E9FB0E62009FF87FC /* MJRefresh-MJRefresh.Privacy */, + B097DD7534E741D5C41838011D755842 /* Pods-iosApp */, + 8596B7A0C5A3E188061E9EDDAB573FCE /* QMUIKit */, + 77DEF480928809EA82047E24B3C57BA7 /* QMUIKit-QMUIKit */, + FFDF743B2D7D655F7364FF1154FFEDE6 /* QMUIKit-QMUIResources */, + 55E0AFD333353D71ACC2207149E879D6 /* Toast */, + F6A5F09CA59AF20B5A450FA1B72ECFB5 /* Toast-Toast */, + E325DCCD357AF9DF2F7C506424659D7E /* YYKit */, ); name = Products; sourceTree = ""; @@ -2719,6 +2723,7 @@ EAC932B63F4CB3BC8F70262E06A858DA /* Resources */, 5F6E41DD88E68F94EBA958A6C348C3FD /* Support Files */, ); + name = Toast; path = Toast; sourceTree = ""; }; @@ -2972,6 +2977,7 @@ 1E6EAF556834D0F6AEB4BEA3D04D1AF2 /* no-arc */, 18A559C0F8BE56F8E20591E08CEBD075 /* Support Files */, ); + name = YYKit; path = YYKit; sourceTree = ""; }; @@ -3108,6 +3114,7 @@ BB895CE877FC657AB7E5B8A0035555B2 /* Resources */, C1ACA7E450ABD1AA622C5C3B4B9C682C /* Support Files */, ); + name = DateTools; path = DateTools; sourceTree = ""; }; @@ -3192,6 +3199,7 @@ E6307622D19FF978837C843981D42250 /* Resources */, 0BDACE2DF2DD5FF2526B04F2EA826B75 /* Support Files */, ); + name = MJRefresh; path = MJRefresh; sourceTree = ""; }; @@ -3258,6 +3266,7 @@ AA2F6EEF8EF543F539A97B1DF314F534 /* Resources */, 5B2FC84517AB039B6B9A71D68459A6D9 /* Support Files */, ); + name = QMUIKit; path = QMUIKit; sourceTree = ""; }; @@ -3932,7 +3941,7 @@ ); name = YYKit; productName = YYKit; - productReference = E325DCCD357AF9DF2F7C506424659D7E /* YYKit.framework */; + productReference = E325DCCD357AF9DF2F7C506424659D7E /* YYKit */; productType = "com.apple.product-type.framework"; }; 225FB3DC8F47C58DEEAE716AFE3005BD /* QMUIKit-QMUIKit */ = { @@ -3949,7 +3958,7 @@ ); name = "QMUIKit-QMUIKit"; productName = QMUIKit; - productReference = 77DEF480928809EA82047E24B3C57BA7 /* QMUIKit.bundle */; + productReference = 77DEF480928809EA82047E24B3C57BA7 /* QMUIKit-QMUIKit */; productType = "com.apple.product-type.bundle"; }; 2B1A4F9261E8F421732B6CB1319CCC3E /* DateTools */ = { @@ -3967,7 +3976,7 @@ ); name = DateTools; productName = DateTools; - productReference = 4AF171581392AD234F23BE913F0C22FE /* DateTools.framework */; + productReference = 4AF171581392AD234F23BE913F0C22FE /* DateTools */; productType = "com.apple.product-type.framework"; }; 4D3BA58D0583DF37575CACAB3DDADC85 /* MJExtension */ = { @@ -3986,7 +3995,7 @@ ); name = MJExtension; productName = MJExtension; - productReference = 2B276B0A79173A1D6E83C9B4FB9A4A57 /* MJExtension.framework */; + productReference = 2B276B0A79173A1D6E83C9B4FB9A4A57 /* MJExtension */; productType = "com.apple.product-type.framework"; }; 55AF53E6C77A10ED4985E04D74A8878E /* Masonry */ = { @@ -4004,7 +4013,7 @@ ); name = Masonry; productName = Masonry; - productReference = 1FFED36A657123030ABB700256D73F15 /* Masonry.framework */; + productReference = 1FFED36A657123030ABB700256D73F15 /* Masonry */; productType = "com.apple.product-type.framework"; }; 6868056D761E163D10FDAF8CF1C4D9B8 /* MJRefresh */ = { @@ -4023,7 +4032,7 @@ ); name = MJRefresh; productName = MJRefresh; - productReference = E49D6D248DD1CEE584E6776B9164A1B2 /* MJRefresh.framework */; + productReference = E49D6D248DD1CEE584E6776B9164A1B2 /* MJRefresh */; productType = "com.apple.product-type.framework"; }; 740124B3EE5D14F0E8AF4C9163C297A8 /* QMUIKit */ = { @@ -4043,7 +4052,7 @@ ); name = QMUIKit; productName = QMUIKit; - productReference = 8596B7A0C5A3E188061E9EDDAB573FCE /* QMUIKit.framework */; + productReference = 8596B7A0C5A3E188061E9EDDAB573FCE /* QMUIKit */; productType = "com.apple.product-type.framework"; }; 7F7C709A913CD2DAF7541A1D8CAC7706 /* JTCalendar */ = { @@ -4061,7 +4070,7 @@ ); name = JTCalendar; productName = JTCalendar; - productReference = 95784DA3E052291A415D111DC394DDB0 /* JTCalendar.framework */; + productReference = 95784DA3E052291A415D111DC394DDB0 /* JTCalendar */; productType = "com.apple.product-type.framework"; }; 82B0A41D3031FF27D78E17B0A9A46FB0 /* MBProgressHUD */ = { @@ -4079,7 +4088,7 @@ ); name = MBProgressHUD; productName = MBProgressHUD; - productReference = 8B8FAB0D627B17EDE1366984278705D9 /* MBProgressHUD.framework */; + productReference = 8B8FAB0D627B17EDE1366984278705D9 /* MBProgressHUD */; productType = "com.apple.product-type.framework"; }; 8592E0E389D40AC17881400ADC67ABC0 /* FMDB */ = { @@ -4098,7 +4107,7 @@ ); name = FMDB; productName = FMDB; - productReference = A3A80BA70CFB7F75C5391BEBBBA8C9DA /* FMDB.framework */; + productReference = A3A80BA70CFB7F75C5391BEBBBA8C9DA /* FMDB */; productType = "com.apple.product-type.framework"; }; 973B9A51B49701F13767694DCAF5C37D /* FMDB-FMDB_Privacy */ = { @@ -4115,7 +4124,7 @@ ); name = "FMDB-FMDB_Privacy"; productName = FMDB_Privacy; - productReference = 148D9AC15C4A9777E5ACBB46C03FE218 /* FMDB_Privacy.bundle */; + productReference = 148D9AC15C4A9777E5ACBB46C03FE218 /* FMDB-FMDB_Privacy */; productType = "com.apple.product-type.bundle"; }; 9972C9CC43A34349C035FE6C913368BF /* Toast-Toast */ = { @@ -4132,7 +4141,7 @@ ); name = "Toast-Toast"; productName = Toast; - productReference = F6A5F09CA59AF20B5A450FA1B72ECFB5 /* Toast.bundle */; + productReference = F6A5F09CA59AF20B5A450FA1B72ECFB5 /* Toast-Toast */; productType = "com.apple.product-type.bundle"; }; A4F02C53B5B4FD6A5A304A7F0FAC06E6 /* QMUIKit-QMUIResources */ = { @@ -4149,7 +4158,7 @@ ); name = "QMUIKit-QMUIResources"; productName = QMUIResources; - productReference = FFDF743B2D7D655F7364FF1154FFEDE6 /* QMUIResources.bundle */; + productReference = FFDF743B2D7D655F7364FF1154FFEDE6 /* QMUIKit-QMUIResources */; productType = "com.apple.product-type.bundle"; }; A80A4D6B185BA43BC06122FED0C15F94 /* Toast */ = { @@ -4168,7 +4177,7 @@ ); name = Toast; productName = Toast; - productReference = 55E0AFD333353D71ACC2207149E879D6 /* Toast.framework */; + productReference = 55E0AFD333353D71ACC2207149E879D6 /* Toast */; productType = "com.apple.product-type.framework"; }; B26054DF1DEA11585A231AF6D1D80D5E /* MJRefresh-MJRefresh.Privacy */ = { @@ -4185,7 +4194,7 @@ ); name = "MJRefresh-MJRefresh.Privacy"; productName = MJRefresh.Privacy; - productReference = 7E3097CFEFDA621E9FB0E62009FF87FC /* MJRefresh.Privacy.bundle */; + productReference = 7E3097CFEFDA621E9FB0E62009FF87FC /* MJRefresh-MJRefresh.Privacy */; productType = "com.apple.product-type.bundle"; }; B32AF3F43989CBA171BB1FB3957A4509 /* MJExtension-MJExtension */ = { @@ -4202,7 +4211,7 @@ ); name = "MJExtension-MJExtension"; productName = MJExtension; - productReference = 43EAAD2AB7E6B407E80E95F643F93D22 /* MJExtension.bundle */; + productReference = 43EAAD2AB7E6B407E80E95F643F93D22 /* MJExtension-MJExtension */; productType = "com.apple.product-type.bundle"; }; ED39C638569286489CD697A6C8964146 /* Pods-iosApp */ = { @@ -4231,7 +4240,7 @@ ); name = "Pods-iosApp"; productName = Pods_iosApp; - productReference = B097DD7534E741D5C41838011D755842 /* Pods_iosApp.framework */; + productReference = B097DD7534E741D5C41838011D755842 /* Pods-iosApp */; productType = "com.apple.product-type.framework"; }; /* End PBXNativeTarget section */ @@ -4252,6 +4261,8 @@ en, ); mainGroup = CF1408CF629C7361332E53B88F7BD30C; + minimizedProjectReferenceProxies = 0; + preferredProjectObjectVersion = 77; productRefGroup = 3A74C13BD22F5258125530629A5F8D14 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -4305,7 +4316,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 03F2428C9FE1AF75B287A39D46F646FA /* FMDB_Privacy.bundle in Resources */, + 03F2428C9FE1AF75B287A39D46F646FA /* FMDB-FMDB_Privacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4321,7 +4332,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7873F2F89CD0A435FAB776BC27BFB56A /* MJExtension.bundle in Resources */, + 7873F2F89CD0A435FAB776BC27BFB56A /* MJExtension-MJExtension in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4354,7 +4365,7 @@ buildActionMask = 2147483647; files = ( D90DF1376DF5E2EA644313BCD2E03058 /* MJRefresh.bundle in Resources */, - 327BA3DDA513422E632D3DA4A8FC60EC /* MJRefresh.Privacy.bundle in Resources */, + 327BA3DDA513422E632D3DA4A8FC60EC /* MJRefresh-MJRefresh.Privacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4369,7 +4380,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - D60FD96D6C7D9442E6A71E410301B3E2 /* Toast.bundle in Resources */, + D60FD96D6C7D9442E6A71E410301B3E2 /* Toast-Toast in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4385,8 +4396,8 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 62CFA1DA1F44515EBEFAC8BBDCAFEA86 /* QMUIKit.bundle in Resources */, - D4D4E8BD5F155B6C32FE15226FD3315C /* QMUIResources.bundle in Resources */, + 62CFA1DA1F44515EBEFAC8BBDCAFEA86 /* QMUIKit-QMUIKit in Resources */, + D4D4E8BD5F155B6C32FE15226FD3315C /* QMUIKit-QMUIResources in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4420,11 +4431,7 @@ buildActionMask = 2147483647; files = ( ); - inputPaths = ( - ); name = "[CP-User] Build shared"; - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = " if [ \"YES\" = \"$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED\" ]; then\n echo \"Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \"YES\"\"\n exit 0\n fi\n set -ev\n REPO_ROOT=\"$PODS_TARGET_SRCROOT\"\n \"$REPO_ROOT/../gradlew\" -p \"$REPO_ROOT\" $KOTLIN_PROJECT_PATH:syncFramework -Pkotlin.native.cocoapods.platform=$PLATFORM_NAME -Pkotlin.native.cocoapods.archs=\"$ARCHS\" -Pkotlin.native.cocoapods.configuration=\"$CONFIGURATION\"\n"; @@ -4991,7 +4998,7 @@ CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/MJExtension"; IBSC_MODULE = MJExtension; INFOPLIST_FILE = "Target Support Files/MJExtension/ResourceBundle-MJExtension-MJExtension-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; PRODUCT_NAME = MJExtension; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -5018,7 +5025,7 @@ ENABLE_USER_SCRIPT_SANDBOXING = NO; INFOPLIST_FILE = "Target Support Files/Pods-iosApp/Pods-iosApp-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -5058,7 +5065,7 @@ GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "Target Support Files/QMUIKit/QMUIKit-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -5087,7 +5094,7 @@ CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Toast"; IBSC_MODULE = Toast; INFOPLIST_FILE = "Target Support Files/Toast/ResourceBundle-Toast-Toast-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; PRODUCT_NAME = Toast; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -5114,7 +5121,7 @@ GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "Target Support Files/Masonry/Masonry-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -5204,7 +5211,7 @@ CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/MJExtension"; IBSC_MODULE = MJExtension; INFOPLIST_FILE = "Target Support Files/MJExtension/ResourceBundle-MJExtension-MJExtension-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; PRODUCT_NAME = MJExtension; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -5231,7 +5238,7 @@ GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "Target Support Files/DateTools/DateTools-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -5271,7 +5278,7 @@ GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "Target Support Files/MJRefresh/MJRefresh-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -5310,7 +5317,7 @@ GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "Target Support Files/Toast/Toast-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -5337,7 +5344,7 @@ CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/QMUIKit"; IBSC_MODULE = QMUIKit; INFOPLIST_FILE = "Target Support Files/QMUIKit/ResourceBundle-QMUIResources-QMUIKit-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; PRODUCT_NAME = QMUIResources; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -5365,7 +5372,7 @@ GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "Target Support Files/FMDB/FMDB-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -5404,7 +5411,7 @@ GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "Target Support Files/MJExtension/MJExtension-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -5443,7 +5450,7 @@ GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "Target Support Files/QMUIKit/QMUIKit-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -5481,7 +5488,7 @@ GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "Target Support Files/DateTools/DateTools-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -5509,7 +5516,7 @@ CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/FMDB"; IBSC_MODULE = FMDB; INFOPLIST_FILE = "Target Support Files/FMDB/ResourceBundle-FMDB_Privacy-FMDB-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; PRODUCT_NAME = FMDB_Privacy; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -5526,7 +5533,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_ENABLE_OBJC_WEAK = NO; ENABLE_USER_SCRIPT_SANDBOXING = NO; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -5556,7 +5563,7 @@ GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "Target Support Files/MJRefresh/MJRefresh-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -5595,7 +5602,7 @@ ENABLE_USER_SCRIPT_SANDBOXING = NO; INFOPLIST_FILE = "Target Support Files/Pods-iosApp/Pods-iosApp-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -5635,7 +5642,7 @@ GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "Target Support Files/MBProgressHUD/MBProgressHUD-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -5674,7 +5681,7 @@ GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "Target Support Files/FMDB/FMDB-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -5713,7 +5720,7 @@ GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "Target Support Files/YYKit/YYKit-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -5740,7 +5747,7 @@ CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/QMUIKit"; IBSC_MODULE = QMUIKit; INFOPLIST_FILE = "Target Support Files/QMUIKit/ResourceBundle-QMUIResources-QMUIKit-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; PRODUCT_NAME = QMUIResources; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -5757,7 +5764,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_ENABLE_OBJC_WEAK = NO; ENABLE_USER_SCRIPT_SANDBOXING = NO; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -5775,7 +5782,7 @@ CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Toast"; IBSC_MODULE = Toast; INFOPLIST_FILE = "Target Support Files/Toast/ResourceBundle-Toast-Toast-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; PRODUCT_NAME = Toast; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -5803,7 +5810,7 @@ GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "Target Support Files/Toast/Toast-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -5842,7 +5849,7 @@ GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "Target Support Files/Masonry/Masonry-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -5881,7 +5888,7 @@ GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "Target Support Files/JTCalendar/JTCalendar-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -5909,7 +5916,7 @@ CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/QMUIKit"; IBSC_MODULE = QMUIKit; INFOPLIST_FILE = "Target Support Files/QMUIKit/ResourceBundle-QMUIKit-QMUIKit-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; PRODUCT_NAME = QMUIKit; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -5936,7 +5943,7 @@ GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "Target Support Files/MBProgressHUD/MBProgressHUD-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -5965,7 +5972,7 @@ CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/MJRefresh"; IBSC_MODULE = MJRefresh; INFOPLIST_FILE = "Target Support Files/MJRefresh/ResourceBundle-MJRefresh.Privacy-MJRefresh-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; PRODUCT_NAME = MJRefresh.Privacy; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -5982,7 +5989,7 @@ CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/FMDB"; IBSC_MODULE = FMDB; INFOPLIST_FILE = "Target Support Files/FMDB/ResourceBundle-FMDB_Privacy-FMDB-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; PRODUCT_NAME = FMDB_Privacy; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -5999,7 +6006,7 @@ CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/MJRefresh"; IBSC_MODULE = MJRefresh; INFOPLIST_FILE = "Target Support Files/MJRefresh/ResourceBundle-MJRefresh.Privacy-MJRefresh-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; PRODUCT_NAME = MJRefresh.Privacy; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -6027,7 +6034,7 @@ GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "Target Support Files/MJExtension/MJExtension-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -6066,7 +6073,7 @@ GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "Target Support Files/YYKit/YYKit-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -6160,7 +6167,7 @@ CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/QMUIKit"; IBSC_MODULE = QMUIKit; INFOPLIST_FILE = "Target Support Files/QMUIKit/ResourceBundle-QMUIKit-QMUIKit-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; PRODUCT_NAME = QMUIKit; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -6187,7 +6194,7 @@ GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "Target Support Files/JTCalendar/JTCalendar-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj index 380808e..d58cef2 100644 --- a/iosApp/iosApp.xcodeproj/project.pbxproj +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -47,6 +47,8 @@ 204462802DF07D18009AF7B6 /* SRDeviceInfo+description.m in Sources */ = {isa = PBXBuildFile; fileRef = 204462522DF07D18009AF7B6 /* SRDeviceInfo+description.m */; }; 204462812DF07D18009AF7B6 /* libRingSDK_2.0.2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2044625A2DF07D18009AF7B6 /* libRingSDK_2.0.2.a */; }; 2044628A2DF07D95009AF7B6 /* NSString+Check.m in Sources */ = {isa = PBXBuildFile; fileRef = 204462892DF07D95009AF7B6 /* NSString+Check.m */; }; + 204462B02DF0935D009AF7B6 /* CoreBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 204462AF2DF0935D009AF7B6 /* CoreBluetooth.framework */; }; + 204463442DF1EA80009AF7B6 /* LTSRingSDK+Desc.m in Sources */ = {isa = PBXBuildFile; fileRef = 204463432DF1EA80009AF7B6 /* LTSRingSDK+Desc.m */; }; 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; }; 7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; }; /* End PBXBuildFile section */ @@ -172,8 +174,10 @@ 2044628B2DF07DCD009AF7B6 /* LTSRingSDK+Desc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LTSRingSDK+Desc.h"; sourceTree = ""; }; 2044628E2DF07DF9009AF7B6 /* LoginVc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LoginVc.h; sourceTree = ""; }; 204462912DF07E1A009AF7B6 /* MainNav.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MainNav.h; sourceTree = ""; }; - 204462972DF07E75009AF7B6 /* PrefixHeader.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PrefixHeader.pch; sourceTree = ""; }; 204462AC2DF082FA009AF7B6 /* NSString+Check.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSString+Check.h"; sourceTree = ""; }; + 204462AF2DF0935D009AF7B6 /* CoreBluetooth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBluetooth.framework; path = System/Library/Frameworks/CoreBluetooth.framework; sourceTree = SDKROOT; }; + 204463422DF1DC53009AF7B6 /* PrefixHeader.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PrefixHeader.pch; sourceTree = ""; }; + 204463432DF1EA80009AF7B6 /* LTSRingSDK+Desc.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "LTSRingSDK+Desc.m"; sourceTree = ""; }; 2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; }; 2BB8C8CFB6051CAD0EEB82BE /* Pods-iosApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iosApp.release.xcconfig"; path = "Target Support Files/Pods-iosApp/Pods-iosApp.release.xcconfig"; sourceTree = ""; }; 3F1F0F9655FE3CEE26EB7A52 /* Pods_iosApp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_iosApp.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -191,6 +195,7 @@ 204461E92DF06025009AF7B6 /* Pods_iosApp.framework in Frameworks */, 204462812DF07D18009AF7B6 /* libRingSDK_2.0.2.a in Frameworks */, 204461EF2DF06041009AF7B6 /* Security.framework in Frameworks */, + 204462B02DF0935D009AF7B6 /* CoreBluetooth.framework in Frameworks */, 204461ED2DF06034009AF7B6 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -395,7 +400,7 @@ 204462602DF07D18009AF7B6 /* Libs */ = { isa = PBXGroup; children = ( - 204462972DF07E75009AF7B6 /* PrefixHeader.pch */, + 204463432DF1EA80009AF7B6 /* LTSRingSDK+Desc.m */, 204461FD2DF07D18009AF7B6 /* BLESDK */, 204462092DF07D18009AF7B6 /* DataBase */, 204462192DF07D18009AF7B6 /* DateTools */, @@ -449,6 +454,7 @@ 7555FF7D242A565900829871 /* iosApp */ = { isa = PBXGroup; children = ( + 204463422DF1DC53009AF7B6 /* PrefixHeader.pch */, 058557BA273AAA24004C7B11 /* Assets.xcassets */, 7555FF82242A565900829871 /* ContentView.swift */, 7555FF8C242A565B00829871 /* Info.plist */, @@ -463,6 +469,7 @@ 7F492F9D678DB5C178EE9E8B /* Frameworks */ = { isa = PBXGroup; children = ( + 204462AF2DF0935D009AF7B6 /* CoreBluetooth.framework */, 204461F02DF06099009AF7B6 /* libRingSDK_2.0.2.a */, 204461EE2DF06041009AF7B6 /* Security.framework */, 204461EC2DF06034009AF7B6 /* Foundation.framework */, @@ -572,14 +579,10 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-frameworks.sh\"\n"; @@ -593,14 +596,10 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - ); name = "[CP] Copy Pods Resources"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources-${CONFIGURATION}-output-files.xcfilelist", ); - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources.sh\"\n"; @@ -640,6 +639,7 @@ 204462772DF07D18009AF7B6 /* OusideBleDiscovery.m in Sources */, 204462782DF07D18009AF7B6 /* NSString+MJExtension.m in Sources */, 204462792DF07D18009AF7B6 /* LTPHud.m in Sources */, + 204463442DF1EA80009AF7B6 /* LTSRingSDK+Desc.m in Sources */, 2044627A2DF07D18009AF7B6 /* NSDate+HMTools.m in Sources */, 2044627B2DF07D18009AF7B6 /* SleepTimeDrawObj.m in Sources */, 2044627C2DF07D18009AF7B6 /* NSObject+Tool.m in Sources */, @@ -805,7 +805,7 @@ "-lRingSDK_2.0.2", "$(inherited)", ); - PRODUCT_BUNDLE_IDENTIFIER = moe.uni.app; + PRODUCT_BUNDLE_IDENTIFIER = moe.uni.ring; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "iosApp/iosApp-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -844,7 +844,7 @@ "-lRingSDK_2.0.2", "$(inherited)", ); - PRODUCT_BUNDLE_IDENTIFIER = moe.uni.app; + PRODUCT_BUNDLE_IDENTIFIER = moe.uni.ring; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "iosApp/iosApp-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/iosApp/iosApp/ContentView.swift b/iosApp/iosApp/ContentView.swift index b1f3f1c..48beafd 100644 --- a/iosApp/iosApp/ContentView.swift +++ b/iosApp/iosApp/ContentView.swift @@ -7,6 +7,7 @@ class TransparentStatusBarViewController: UIViewController { init() { self.composeViewController = MainViewControllerKt.MainViewController() + Platform_iosKt.doInitLogger() super.init(nibName: nil, bundle: nil) } diff --git a/iosApp/iosApp/Info.plist b/iosApp/iosApp/Info.plist index aaf5e18..56f0725 100644 --- a/iosApp/iosApp/Info.plist +++ b/iosApp/iosApp/Info.plist @@ -22,6 +22,10 @@ 1 LSRequiresIPhoneOS + NSBluetoothAlwaysUsageDescription + 此应用需要使用蓝牙来扫描和连接蓝牙设备 + NSBluetoothPeripheralUsageDescription + 此应用需要使用蓝牙来扫描和连接蓝牙设备 UIApplicationSceneManifest UIApplicationSupportsMultipleScenes diff --git a/iosApp/iosApp/Libs/LTSRingSDK+Desc.m b/iosApp/iosApp/Libs/LTSRingSDK+Desc.m new file mode 100644 index 0000000..19467b5 --- /dev/null +++ b/iosApp/iosApp/Libs/LTSRingSDK+Desc.m @@ -0,0 +1,65 @@ +// +// LTSRingSDK+Desc.m +// CareRingApp +// +// Created by Linktop on 2023/8/1. +// + +#import "LTSRingSDK+Desc.h" + +@implementation LTSRingSDK (Desc) + +-(NSString *)cmdErrorDesc:(EXCUTED_CMD)cmd +{ + NSString *dec = @""; + switch (cmd) { + case EXCUTED_CMD_SET_SPORT_MODE: + { + dec = @"sport mode switch";//@"运动模式开关"; + } + break; + case EXCUTED_CMD_SYNC_TIME: + { + dec = @"time synchronization";//@"时间同步"; + } + break; + case EXCUTED_CMD_GET_STEPS: + { + dec = @"get steps";//@"获取计步"; + } + break; + case EXCUTED_CMD_GET_TEMPERATURE: + { + dec = @"get body temperature";//@"获取体温"; + } + break; + case EXCUTED_CMD_HIS_DATA: + { + dec = @"Historical data reporting";//@"历史数据"; + } + break; + case EXCUTED_CMD_HIS_COUNT: + { + dec = @"Number of historical data";//@"历史数据个数"; + } + break; + + case EXCUTED_CMD_SPORT_MODE: + { + dec = @"sports mode";//@"运动模式"; + } + break; + case EXCUTED_CMD_CLEAR_HIS_DATA: + { + dec = @"Clear device history";//@"清空设备历史记录"; + + } + break; + default: + break; + } + + return dec; +} + +@end diff --git a/iosApp/iosApp/Libs/Modules/DeviceCenter.m b/iosApp/iosApp/Libs/Modules/DeviceCenter.m index fbf1ffe..b172821 100644 --- a/iosApp/iosApp/Libs/Modules/DeviceCenter.m +++ b/iosApp/iosApp/Libs/Modules/DeviceCenter.m @@ -8,12 +8,12 @@ #import "DeviceCenter.h" #import "ConfigModel.h" -#import "../DataBase/DBTables.h" +#import "DBTables.h" #import "NotificationNameHeader.h" #import "TimeUtils.h" #import "NSString+Check.h" -#import "AboutOta/OTAHelper.h" +#import "OTAHelper.h" #import "OusideBleDiscovery.h" #import "LTSRingSDK+Desc.h" #import "SRDeviceInfo+description.h" @@ -59,6 +59,7 @@ NSString * const CP_NAME = @"BlackShark"; + (instancetype)instance { static DeviceCenter *_deviceCenter = nil; static dispatch_once_t onceToken; + NSLog(@"DeviceCenter instance"); dispatch_once(&onceToken, ^{ // 要使用self来调用 _deviceCenter = [[self alloc] init]; @@ -79,6 +80,7 @@ NSString * const CP_NAME = @"BlackShark"; self.sleepQueue = dispatch_queue_create("sleep_queue", DISPATCH_QUEUE_SERIAL); } + NSLog(@"DeviceCenter init!"); return self; } @@ -132,7 +134,7 @@ NSString * const CP_NAME = @"BlackShark"; WEAK_SELF [perphelArray enumerateObjectsUsingBlock:^(SRBLeService * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { STRONG_SELF -// DebugNSLog(@"found devcie: %@", obj.macAddress); + NSLog(@"found devcie: %@", obj.macAddress); if ([obj.macAddress isEqual:strongSelf.bindDevice.macAddress]) { // 自动连接 [strongSelf connectDevice:obj]; @@ -175,7 +177,7 @@ NSString * const CP_NAME = @"BlackShark"; -(void)startBleScan { -// DebugNSLog(@"ble CBManagerState: %ld", (long)self.sdk.bleCenterManagerState); + NSLog(@"ble CBManagerState: %ld", (long)self.sdk.bleCenterManagerState); if (self.isCustomBleManage) { [self.ousideBleManager startScanning]; } else { @@ -438,12 +440,12 @@ NSString * const CP_NAME = @"BlackShark"; case CBManagerStateUnsupported:// 不支持蓝牙 { //不支持 -// DebugNSLog(@"StateChange = CBManagerStateUnsupported"); + NSLog(@"StateChange = CBManagerStateUnsupported"); } break; case CBManagerStatePoweredOff:// 未启动 { -// DebugNSLog(@"StateChange = CBManagerStatePoweredOff"); + NSLog(@"StateChange = CBManagerStatePoweredOff"); } @@ -451,27 +453,27 @@ NSString * const CP_NAME = @"BlackShark"; case CBManagerStateUnauthorized: // 未授权 { /* Tell user the app is not allowed. */ -// DebugNSLog(@"StateChange = CBManagerStateUnauthorized"); + NSLog(@"StateChange = CBManagerStateUnauthorized"); } break; case CBManagerStateUnknown: // 未知 { /* Bad news, let's wait for another event. */ -// DebugNSLog(@"StateChange = CBManagerStateUnknown"); + NSLog(@"StateChange = CBManagerStateUnknown"); } break; case CBManagerStatePoweredOn:// 开启 { -// DebugNSLog(@"StateChange = CBManagerStatePoweredOn"); + NSLog(@"StateChange = CBManagerStatePoweredOn"); isOn = YES; } break; case CBManagerStateResetting:// 重置中 { -// DebugNSLog(@"StateChange = CBManagerStateResetting"); + NSLog(@"StateChange = CBManagerStateResetting"); break; } @@ -489,11 +491,11 @@ NSString * const CP_NAME = @"BlackShark"; - (void)srBleCmdExcute:(EXCUTED_CMD)cmd Succ:(BOOL)isSucc Reason:(CMD_EXECTE_ERROR_REASON)reason { -// DebugNSLog(@"Command 0x%.2X - %@ ,Exec result %@ , Fail reason:%lu -- %@", -// cmd, -// [self.sdk cmdErrorDesc:cmd] , -// isSucc ? @"succ" :@"fail", (unsigned long)reason, -// [SRDeviceInfo descryOfErrorReason:reason] ); + NSLog(@"Command 0x%.2X - %@ ,Exec result %@ , Fail reason:%lu -- %@", + cmd, + [self.sdk cmdErrorDesc:cmd] , + isSucc ? @"succ" :@"fail", (unsigned long)reason, + [SRDeviceInfo descryOfErrorReason:reason] ); if ([self.appDataDelegate respondsToSelector:@selector(srBleCmdExcute:Succ:Reason:)]) { [self.appDataDelegate srBleCmdExcute:cmd Succ:isSucc Reason:reason]; @@ -503,7 +505,7 @@ NSString * const CP_NAME = @"BlackShark"; - (void)srBleDeviceBatteryLevel:(NSUInteger)batteryLevel IsCharging:(BOOL)isCharging { _currentBatteryLevel = batteryLevel; _isCharging = isCharging; -// DebugNSLog(@"电量 %lu 充电: %d", (unsigned long)batteryLevel, isCharging); +// NSLog(@"电量 %lu 充电: %d", (unsigned long)batteryLevel, isCharging); if ([self.appDataDelegate respondsToSelector:@selector(srBleDeviceBatteryLevel:IsCharging:)]) { [self.appDataDelegate srBleDeviceBatteryLevel:batteryLevel IsCharging:isCharging]; } @@ -550,6 +552,8 @@ NSString * const CP_NAME = @"BlackShark"; -(void)srBleOEMAuthResult:(BOOL)authSucceddful { + NSLog(@"srBleOEMAuthResult"); + // 主动获取电池 [self.sdk functionGetDeviceBattery]; @@ -570,7 +574,7 @@ NSString * const CP_NAME = @"BlackShark"; -(void)srBleMeasureDuration:(NSInteger)seconds { self.currentDevice.hrMeasureDurations = seconds; -// DebugNSLog(@"测量时长 %ld s", (long)seconds); + NSLog(@"测量时长 %ld s", (long)seconds); if ([self.appDataDelegate respondsToSelector:@selector(srBleMeasureDuration:)]){ [self.appDataDelegate srBleMeasureDuration:seconds]; } @@ -614,7 +618,7 @@ NSString * const CP_NAME = @"BlackShark"; { if (self.historySyncCbk) { self.historySyncCbk(isComplete, percent); -// DebugNSLog(@"lzp call historySyncCbk currentaccount: %ld complete:%d", (long)currentCount, isComplete); +// NSLog(@"lzp call historySyncCbk currentaccount: %ld complete:%d", (long)currentCount, isComplete); } @@ -684,7 +688,7 @@ NSString * const CP_NAME = @"BlackShark"; } - (void)srBleSN:(nonnull NSString *)sn { -// DebugNSLog(@"sn:%@", sn); + NSLog(@"sn:%@", sn); if (self.bindDevice) { self.bindDevice.otherInfo.sn = sn; [self.bindDevice updateOtherInfo:^(BOOL succ) { @@ -721,7 +725,7 @@ NSString * const CP_NAME = @"BlackShark"; default: break; } -// DebugNSLog(@"ble cmd sending error: %@ when calling %@", errorDesc,methodNm); + NSLog(@"ble cmd sending error: %@ when calling %@", errorDesc,methodNm); if ([self.appDataDelegate respondsToSelector:@selector(srBleFunctionErrorCallBack:MehthodName:)]) { [self.appDataDelegate srBleFunctionErrorCallBack:error MehthodName:methodNm]; @@ -812,7 +816,7 @@ NSString * const CP_NAME = @"BlackShark"; WEAK_SELF [[OTAHelper Instance] otaQueryUpgrade:OTA_HOST Cat:catDwn CBK:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error, NSDictionary * _Nullable resultDict) { STRONG_SELF -// DebugNSLog(@"ota 请求 %@", resultDict); + NSLog(@"ota 请求 %@", resultDict); BOOL needUpdate = [[DeviceCenter instance] checkNeedUpdate:resultDict[@"ver"]]; if (needUpdate) { if (resultDict) { diff --git a/iosApp/iosApp/Libs/Modules/OusideBle/OusideBleDiscovery.m b/iosApp/iosApp/Libs/Modules/OusideBle/OusideBleDiscovery.m index 4ee4479..12c6db0 100755 --- a/iosApp/iosApp/Libs/Modules/OusideBle/OusideBleDiscovery.m +++ b/iosApp/iosApp/Libs/Modules/OusideBle/OusideBleDiscovery.m @@ -12,102 +12,86 @@ @end -@implementation OusideBleDiscovery -{ +@implementation OusideBleDiscovery { CBUUID *_otaMainServiceUUID; // ota 主服务 } -+ (CBUUID*) sigUUIDToCBUUID:(uint16_t)UUID -{ - uint8_t b[2] = { (UUID >> 8) & 0xff, UUID & 0xff }; ++ (CBUUID *)sigUUIDToCBUUID:(uint16_t)UUID { + uint8_t b[2] = {(UUID >> 8) & 0xff, UUID & 0xff}; return [CBUUID UUIDWithData:[NSData dataWithBytes:b length:2]]; } -+ (uint16_t) sigUUIDFromCBUUID:(CBUUID*)UUID -{ - const uint8_t* b = UUID.data.bytes; ++ (uint16_t)sigUUIDFromCBUUID:(CBUUID *)UUID { + const uint8_t *b = UUID.data.bytes; return UUID.data.length == 2 ? (b[0] << 8) | b[1] : (uint16_t) 0; } - --(instancetype)init -{ +- (instancetype)init { self = [super init]; - if (self) - { + if (self) { _pendingInit = YES; self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()]; _foundPeripherals = [[NSMutableArray alloc] init]; _otaMainServiceUUID = [[self class] sigUUIDToCBUUID:SR_SERVICE_UUID]; - - } + + } return self; } --(void)stconnectPeripheralTimeout:(id)sender { - +- (void)stconnectPeripheralTimeout:(id)sender { + } -- (void) dealloc -{ +- (void)dealloc { // We are a singleton and as such, dealloc shouldn't be called. // assert(NO); } - - -- (void) centralManager:(CBCentralManager *)central didRetrieveConnectedPeripherals:(NSArray *)peripherals -{ +- (void)centralManager:(CBCentralManager *)central didRetrieveConnectedPeripherals:(NSArray *)peripherals { } - -- (void) centralManager:(CBCentralManager *)central didRetrievePeripheral:(CBPeripheral *)peripheral -{ - [central connectPeripheral:peripheral options:nil]; +- (void)centralManager:(CBCentralManager *)central didRetrievePeripheral:(CBPeripheral *)peripheral { + [central connectPeripheral:peripheral options:nil]; // [_discoveryDelegate stdiscoveryDidRefresh]; } //delete the stored device uuid in list -- (void) centralManager:(CBCentralManager *)central didFailToRetrievePeripheralForUUID:(CFUUIDRef)UUID error:(NSError *)error -{ - /* Nuke from plist. */ +- (void)centralManager:(CBCentralManager *)central didFailToRetrievePeripheralForUUID:(CFUUIDRef)UUID error:(NSError *)error { + /* Nuke from plist. */ // [DeviceRecordManager removeSavedDevice:UUID]; - -} +} #pragma mark - #pragma mark Discovery /// 开始扫描 -- (void) startScanning -{ +- (void)startScanning { [self stopScanning]; [_foundPeripherals removeAllObjects]; - - NSArray *uuidArray = @[_otaMainServiceUUID]; - NSDictionary *options = @{ CBCentralManagerScanOptionAllowDuplicatesKey : @(NO) }; - - [_centralManager scanForPeripheralsWithServices:uuidArray options:options]; + + NSArray *uuidArray = @[_otaMainServiceUUID]; + NSDictionary *options = @{CBCentralManagerScanOptionAllowDuplicatesKey: @(NO)}; + NSLog(@"OusideBle start scan"); + [_centralManager scanForPeripheralsWithServices:uuidArray options:options]; } -- (void) stopScanning -{ +- (void)stopScanning { if (_centralManager != nil) { [_centralManager stopScan]; - + } - + } @@ -119,39 +103,37 @@ - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData - RSSI:(NSNumber *)RSSI -{ + RSSI:(NSNumber *)RSSI { NSString *advertisementName = [advertisementData objectForKey:@"kCBAdvDataLocalName"]; NSArray *services = [advertisementData valueForKey:CBAdvertisementDataServiceUUIDsKey]; NSArray *servicesOvfl = [advertisementData valueForKey:CBAdvertisementDataOverflowServiceUUIDsKey]; - __block BOOL canAdd = [services containsObject:_otaMainServiceUUID] || [servicesOvfl containsObject:_otaMainServiceUUID]; - - if (canAdd) - { + __block BOOL canAdd = [services containsObject:_otaMainServiceUUID] || + [servicesOvfl containsObject:_otaMainServiceUUID]; + + if (canAdd) { NSString *macString = [self macAddressFromBleAvdData:advertisementData]; SRBLeService *service = [[SRBLeService alloc] initWithPeripheral:peripheral]; [service setAdvData:advertisementData]; service.rssi = RSSI; [self analysisAdvData:advertisementData]; - + if (service.macAddress != nil) { - -// DebugNSLog(@"ouside scan add %@ %@", service.advDataLocalName, service.macAddress); + + NSLog(@"ouside scan add %@ %@", service.advDataLocalName, service.macAddress); [self addToFoundService:service AdvertisementData:advertisementData]; - + if ([_scanDelegate respondsToSelector:@selector(srScanDeviceDidRefresh:)]) { [_scanDelegate srScanDeviceDidRefresh:[NSArray arrayWithArray:_foundPeripherals]]; } } - + } } --(void)addToFoundService:(SRBLeService *)service AdvertisementData:(NSDictionary *)advertisementData -{ +- (void)addToFoundService:(SRBLeService *)service AdvertisementData:(NSDictionary *)advertisementData { BOOL canAdd = YES; // 防止重复加入 for (SRBLeService *s in _foundPeripherals) { @@ -159,37 +141,34 @@ canAdd = NO; break; } - + } - + if (canAdd) { [_foundPeripherals addObject:service]; } - + } --(void)sendData:(NSData *)data type:(CBCharacteristicWriteType)type -{ +- (void)sendData:(NSData *)data type:(CBCharacteristicWriteType)type { + - } #pragma mark - #pragma mark retrievePeripheral -- (void) retrievePeripheral:(NSString *)uuid -{ +- (void)retrievePeripheral:(NSString *)uuid { if (uuid != nil) { - CFUUIDRef uuidRef = CFUUIDCreateFromString(NULL, (__bridge CFStringRef)uuid); - NSArray *uuids = [NSArray arrayWithObject:(__bridge id)uuidRef]; + CFUUIDRef uuidRef = CFUUIDCreateFromString(NULL, (__bridge CFStringRef) uuid); + NSArray *uuids = [NSArray arrayWithObject:(__bridge id) uuidRef]; [_centralManager retrievePeripheralsWithIdentifiers:uuids]; } - + } -- (void)centralManager:(CBCentralManager *)central didRetrievePeripherals:(NSArray *)peripherals -{ +- (void)centralManager:(CBCentralManager *)central didRetrievePeripherals:(NSArray *)peripherals { // [self.scanDelegate stdidRetrievePeripherals:peripherals]; } @@ -198,13 +177,12 @@ /****************************************************************************/ /* Connection/Disconnection */ /****************************************************************************/ -- (void) connectPeripheral:(SRBLeService*)keyService -{ +- (void)connectPeripheral:(SRBLeService *)keyService { //停止扫描 [_centralManager stopScan]; - + if (keyService == _currentService) { - if ( _currentService.peripheral.state == CBPeripheralStateConnected) { + if (_currentService.peripheral.state == CBPeripheralStateConnected) { // 已连接 if ([self.scanDelegate respondsToSelector:@selector(srBleDidConnectPeripheral:)]) { [self.scanDelegate srBleDidConnectPeripheral:_currentService]; @@ -214,63 +192,59 @@ [_centralManager connectPeripheral:_currentService.peripheral options:nil]; return; } - + } else { - + if (_currentService.peripheral.state == CBPeripheralStateConnected) { [_centralManager cancelPeripheralConnection:_currentService.peripheral]; -// DebugNSLog(@"/*主*/动断开 %s %d", __func__, __LINE__); - + NSLog(@"/*主*/动断开 %s %d", __func__, __LINE__); + } _currentService = keyService; - + if (keyService.peripheral.state != CBPeripheralStateConnected) { [_centralManager connectPeripheral:keyService.peripheral options:nil]; [self performSelector:@selector(stconnectPeripheralTimeout:) withObject:keyService afterDelay:900.0f]; } } - - + + } //-(void) -- (void)connectPeripheralTimeout:(id)obj -{ +- (void)connectPeripheralTimeout:(id)obj { SRBLeService *p = obj; if (!(p.peripheral.state == CBPeripheralStateConnected)) { // [_scanDelegate stconnectPeripheralTimeout:p]; } } -- (void) disconnectPeripheral:(SRBLeService*)keyService -{ +- (void)disconnectPeripheral:(SRBLeService *)keyService { if (_currentService == keyService) { } - - if (![_foundPeripherals containsObject:_currentService] && _currentService != nil) - { + + if (![_foundPeripherals containsObject:_currentService] && _currentService != nil) { [_foundPeripherals addObject:_currentService]; } if (keyService.peripheral) { [_centralManager cancelPeripheralConnection:keyService.peripheral]; -// DebugNSLog(@"主动断开 %s %d", __func__, __LINE__); + NSLog(@"主动断开 %s %d", __func__, __LINE__); } _currentService = nil; } -- (void)cancelAllReconnect -{ +- (void)cancelAllReconnect { // for (LeKeyFobService *s in self.connectedServices) { - if (!(_currentService.peripheral.state == CBPeripheralStateConnected)) { - [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(stconnectPeripheralTimeout:) object:_currentService.peripheral]; - } + if (!(_currentService.peripheral.state == CBPeripheralStateConnected)) { + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(stconnectPeripheralTimeout:) object:_currentService.peripheral]; + } // } for (CBPeripheral *p in self.foundPeripherals) { - if (!(p.state ==CBPeripheralStateConnected)) { + if (!(p.state == CBPeripheralStateConnected)) { [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(stconnectPeripheralTimeout:) object:p]; } } @@ -278,23 +252,22 @@ } -- (void) centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral -{ +- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral { // LeKeyFobService *service = nil; // BOOL isExist = NO; - + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(stconnectPeripheralTimeout:) object:peripheral]; - - + + if ([_currentService.peripheral.identifier.UUIDString isEqualToString:peripheral.identifier.UUIDString]) { - - if (_currentService.peripheral.state == CBPeripheralStateConnected){ - + + if (_currentService.peripheral.state == CBPeripheralStateConnected) { + if ([self.scanDelegate respondsToSelector:@selector(srBleDidConnectPeripheral:)]) { [self.scanDelegate srBleDidConnectPeripheral:_currentService]; return; } - + // [_currentService setPerpheralDelegate:_peripheralDelegate]; // [_currentService start]; // 开始业务 // @@ -302,59 +275,53 @@ // [_peripheralDelegate stkeyFobServiceDidChangeStatus:_currentService]; // [_peripheralDelegate stkeyFobServiceDidConnectPeripheral:_currentService]; // [_discoveryDelegate stdiscoveryDidRefresh]; - + } } } - -- (void) centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error -{ -// DebugNSLog(@"Attempted connection to peripheral %@ failed: %@", [peripheral name], [error localizedDescription]); +- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error { + NSLog(@"Attempted connection to peripheral %@ failed: %@", [peripheral name], + [error localizedDescription]); } - -- (void) centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error -{ +- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error { if ([_currentService peripheral] != peripheral) { return; } -// DebugNSLog(@"异常断开 error:%@", error); + NSLog(@"异常断开 error:%@", error); //异常断开的动画 //3.通知代理 if ([self.scanDelegate respondsToSelector:@selector(srBleDidDisconnectPeripheral:)]) { [self.scanDelegate srBleDidDisconnectPeripheral:_currentService]; } - + } /// 清空所有发现和连接过的设备 -- (void) clearDevices -{ +- (void)clearDevices { [_foundPeripherals removeAllObjects]; - + if (_currentService.peripheral.state == CBPeripheralStateConnected) { [_centralManager cancelPeripheralConnection:_currentService.peripheral]; -// DebugNSLog(@"主动断开 %s %d", __func__, __LINE__); + NSLog(@"主动断开 %s %d", __func__, __LINE__); _currentService = nil; } - + } -- (CBManagerState)deviceBleCenterState -{ - return [_centralManager state]; +- (CBManagerState)deviceBleCenterState { + return [_centralManager state]; } -- (void)centralManagerDidUpdateState:(CBCentralManager *)central -{ +- (void)centralManagerDidUpdateState:(CBCentralManager *)central { static CBManagerState previousState = -1; /* ios 10.0 CBManagerStateUnknown = 0, @@ -364,59 +331,52 @@ CBManagerStatePoweredOff, CBManagerStatePoweredOn, */ -// DebugNSLog(@"手机蓝牙状态: %ld", (long)[_centralManager state]); - switch ([_centralManager state]) { - case CBManagerStateUnsupported: - { + NSLog(@"手机蓝牙状态: %ld", (long) [_centralManager state]); + switch ([_centralManager state]) { + case CBManagerStateUnsupported: { //不支持 break; } - case CBManagerStatePoweredOff: - { + case CBManagerStatePoweredOff: { + [self clearDevices]; + + break; + } + + case CBManagerStateUnauthorized: { + /* Tell user the app is not allowed. */ + break; + } + + case CBManagerStateUnknown: { + /* Bad news, let's wait for another event. */ + break; + } + + case CBManagerStatePoweredOn: { + _pendingInit = NO; + + break; + } + + case CBManagerStateResetting: { [self clearDevices]; - break; - } - - case CBManagerStateUnauthorized: - { - /* Tell user the app is not allowed. */ - break; - } - - case CBManagerStateUnknown: - { - /* Bad news, let's wait for another event. */ - break; - } - - case CBManagerStatePoweredOn: - { - _pendingInit = NO; - - break; - } - - case CBManagerStateResetting: - { - [self clearDevices]; - - _pendingInit = YES; - break; - } - } - + _pendingInit = YES; + break; + } + } + previousState = [_centralManager state]; if ([_scanDelegate respondsToSelector:@selector(srBlePowerStateChange:)]) { [_scanDelegate srBlePowerStateChange:previousState]; } } --(void)analysisAdvData:(NSDictionary *)advertisementData -{ - NSData * manufacturerData = advertisementData[@"kCBAdvDataManufacturerData"]; - NSString * advDataLocalName = [advertisementData objectForKey:@"kCBAdvDataLocalName"]; - Byte * manufacturerBybtes = (Byte *)(manufacturerData.bytes); +- (void)analysisAdvData:(NSDictionary *)advertisementData { + NSData *manufacturerData = advertisementData[@"kCBAdvDataManufacturerData"]; + NSString *advDataLocalName = [advertisementData objectForKey:@"kCBAdvDataLocalName"]; + Byte *manufacturerBybtes = (Byte * )(manufacturerData.bytes); if (manufacturerBybtes[0] == 0XA5 && manufacturerBybtes[1] == 0X0D) { // not suitable @@ -426,15 +386,16 @@ uint8_t deviceSize = 0; if (manufacturerData.length >= 6) { // color and size - Byte *byte = (Byte *)[manufacturerData bytes]; - deviceColor = (NSUInteger)(byte[4]); // color: 0-black,1-silver, 2-gold, 3-rose gold + Byte *byte = (Byte * ) + [manufacturerData bytes]; + deviceColor = (NSUInteger) (byte[4]); // color: 0-black,1-silver, 2-gold, 3-rose gold deviceSize = byte[5] & 0xFF; // ring's size } BOOL isCharging = NO; uint8_t batteryLevel = 0; - if (manufacturerData.length >= 8) - { - Byte *byte = (Byte *)[manufacturerData bytes]; + if (manufacturerData.length >= 8) { + Byte *byte = (Byte * ) + [manufacturerData bytes]; uint8_t b6 = byte[6]; isCharging = (b6 >> 7) & 0X01; // Is device charging. YES = charging batteryLevel = b6 & 0X7F; // battery level,range in 0-100 @@ -442,31 +403,32 @@ NSInteger chipType = (byte[7] >> 4) & 0XFF; // main chip NSUInteger deviceGeneration = (byte[7]) & 0X0F; // generation } - + if (manufacturerData.length >= 8) { -// DebugNSLog(@"advName:%@ color:%d,size:%d,isCharging:%d, batteryLevel:%d%% ", advDataLocalName, deviceColor, deviceSize, isCharging,batteryLevel); + NSLog(@"advName:%@ color:%d,size:%d,isCharging:%d, batteryLevel:%d%% ", advDataLocalName, deviceColor, deviceSize, isCharging,batteryLevel); return; } if (manufacturerData.length >= 6) { -// DebugNSLog(@"advName:%@ color:%d,size:%d", advDataLocalName, deviceColor, deviceSize); + NSLog(@"advName:%@ color:%d,size:%d", advDataLocalName, deviceColor, deviceSize); } } --(NSString *)macAddressFromBleAvdData:(NSDictionary *)advertisementData -{ +- (NSString *)macAddressFromBleAvdData:(NSDictionary *)advertisementData { // mac 地址拼接 NSString *macString = nil; - NSData * manufacturerData = advertisementData[@"kCBAdvDataManufacturerData"]; + NSData *manufacturerData = advertisementData[@"kCBAdvDataManufacturerData"]; NSString *advertisementName = [advertisementData objectForKey:@"kCBAdvDataLocalName"]; if (manufacturerData.length >= 4) { NSData *macDataLowBit = [manufacturerData subdataWithRange:NSMakeRange(0, 4)]; - - Byte *byte = (Byte *)[macDataLowBit bytes]; - macString = [NSString stringWithFormat:@"%.2X:%.2X:%.2X:%.2X", byte[0], byte[1], byte[2], byte[3]]; - if (advertisementName.length>=4) { - NSString *macString_high = [advertisementName substringWithRange:NSMakeRange(advertisementName.length - 4, 4)]; + + Byte *byte = (Byte * ) + [macDataLowBit bytes]; + macString = [NSString stringWithFormat:@"%.2X:%.2X:%.2X:%.2X", byte[0], byte[1], byte[2], byte[3]]; + if (advertisementName.length >= 4) { + NSString *macString_high = [advertisementName substringWithRange:NSMakeRange( + advertisementName.length - 4, 4)]; NSMutableString *mutStr = [NSMutableString stringWithString:macString_high]; [mutStr insertString:@":" atIndex:2]; macString = [NSString stringWithFormat:@"%@:%@", macString, mutStr]; diff --git a/iosApp/iosApp/PrefixHeader.pch b/iosApp/iosApp/PrefixHeader.pch new file mode 100644 index 0000000..4454631 --- /dev/null +++ b/iosApp/iosApp/PrefixHeader.pch @@ -0,0 +1,23 @@ +// +// PrefixHeader.pch +// sr01sdkProject +// +// Created by Linktop on 2022/5/30. +// + +#ifndef PrefixHeader_pch +#define PrefixHeader_pch + +//#import "TestUtils.h" + +// Include any system framework and library headers here that should be included in all compilation units. +// You will also need to set the Prefix Header build setting of one or more of your targets to reference this file. + +#ifdef DEBUG +#define DebugNSLog(...) NSLog(__VA_ARGS__) +#else +#define DebugNSLog(...) + +#endif + +#endif /* PrefixHeader_pch */ diff --git a/iosApp/iosApp/iosApp-Bridging-Header.h b/iosApp/iosApp/iosApp-Bridging-Header.h index d358b53..1b2cb5d 100644 --- a/iosApp/iosApp/iosApp-Bridging-Header.h +++ b/iosApp/iosApp/iosApp-Bridging-Header.h @@ -2,4 +2,3 @@ // Use this file to import your target's public headers that you would like to expose to Swift. // -#import "DeviceCenter.h" diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 389978d..12440f9 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -6,6 +6,7 @@ plugins { alias(libs.plugins.androidLibrary) alias(libs.plugins.compose.compiler) alias(libs.plugins.composeMultiplatform) + id("com.google.devtools.ksp") } kotlin { @@ -84,4 +85,14 @@ android { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } -} \ No newline at end of file + dependencies { + implementation(libs.androidx.room.runtime) + ksp(libs.androidx.room.compiler) + implementation(libs.android.database.sqlcipher) + implementation(fileTree("libs")) + implementation ("com.google.accompanist:accompanist-permissions:0.37.3") + } +} +dependencies { + implementation(libs.androidx.activity.ktx) +} diff --git a/shared/libs/NexRingSDK_v1.4.0_release.aar b/shared/libs/NexRingSDK_v1.4.0_release.aar new file mode 100644 index 0000000..4582983 Binary files /dev/null and b/shared/libs/NexRingSDK_v1.4.0_release.aar differ diff --git a/shared/libs/OemAuth_v2.0.0_release.aar b/shared/libs/OemAuth_v2.0.0_release.aar new file mode 100644 index 0000000..c0a0b24 Binary files /dev/null and b/shared/libs/OemAuth_v2.0.0_release.aar differ diff --git a/shared/libs/SleepStagingNativeLib_v5_ring_release_v2.5.6.1.aar b/shared/libs/SleepStagingNativeLib_v5_ring_release_v2.5.6.1.aar new file mode 100644 index 0000000..5f1bc4d Binary files /dev/null and b/shared/libs/SleepStagingNativeLib_v5_ring_release_v2.5.6.1.aar differ diff --git a/shared/src/androidMain/kotlin/com/whitefish/ring/ActivityLifecycleCb.kt b/shared/src/androidMain/kotlin/com/whitefish/ring/ActivityLifecycleCb.kt new file mode 100644 index 0000000..91a573f --- /dev/null +++ b/shared/src/androidMain/kotlin/com/whitefish/ring/ActivityLifecycleCb.kt @@ -0,0 +1,58 @@ +package com.whitefish.ring + +import android.app.Activity +import android.app.Application +import android.os.Bundle +import android.util.Log + +class ActivityLifecycleCb : Application.ActivityLifecycleCallbacks { + + /** + * 如果是从后台打开APP的,此标志意味着可以从设备拉数据 + * + * + var readDataFromDevice: Boolean = false + */ + + private var flag = 0 + val activities = ArrayList() + + /** + * 判断APP是否在前台运行 + * */ + val isAppForeground: Boolean get() = flag > 0 + var backgroundFlag = true + + val currAct: Activity? + get() = if (activities.isNotEmpty()) activities[activities.size - 1] else null + + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { + activities.add(activity) + } + + override fun onActivityStarted(activity: Activity) { + flag++ + Log.i("ActivityLifecycleCb", "onActivityStarted - flag:$flag") + } + + override fun onActivityResumed(activity: Activity) { + } + + override fun onActivityPaused(activity: Activity) { + } + + override fun onActivityStopped(activity: Activity) { + flag-- + if (!isAppForeground) { + backgroundFlag = true + } + Log.i("ActivityLifecycleCb", "onActivityStopped - flag:$flag") + } + + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) { + } + + override fun onActivityDestroyed(activity: Activity) { + activities.remove(activity) + } +} \ No newline at end of file diff --git a/shared/src/androidMain/kotlin/com/whitefish/ring/Application.kt b/shared/src/androidMain/kotlin/com/whitefish/ring/Application.kt new file mode 100644 index 0000000..15da0fe --- /dev/null +++ b/shared/src/androidMain/kotlin/com/whitefish/ring/Application.kt @@ -0,0 +1,31 @@ +package com.whitefish.ring + +import android.annotation.SuppressLint +import android.app.Application +import com.whitefish.app.bt.BleManager +import io.github.aakira.napier.DebugAntilog +import io.github.aakira.napier.Napier +import lib.linktop.nexring.api.NexRingManager + +class Application: Application() { + val bleManager by lazy { + NexRingManager.init(this) + BleManager(this) + } + + val mActivityLifecycleCb = ActivityLifecycleCb() + + companion object { + @SuppressLint("StaticFieldLeak") + var INSTANTS: com.whitefish.ring.Application? = null + private set + + } + + override fun onCreate() { + super.onCreate() + INSTANTS = this + Napier.base(DebugAntilog()) + registerActivityLifecycleCallbacks(mActivityLifecycleCb) + } +} \ No newline at end of file diff --git a/shared/src/androidMain/kotlin/com/whitefish/ring/DeviceManager.kt b/shared/src/androidMain/kotlin/com/whitefish/ring/DeviceManager.kt index 25190ea..15d2bc4 100644 --- a/shared/src/androidMain/kotlin/com/whitefish/ring/DeviceManager.kt +++ b/shared/src/androidMain/kotlin/com/whitefish/ring/DeviceManager.kt @@ -1,13 +1,235 @@ package com.whitefish.ring +import android.annotation.SuppressLint +import android.app.AlertDialog +import android.bluetooth.BluetoothManager +import android.bluetooth.BluetoothProfile +import android.content.Context +import androidx.lifecycle.MutableLiveData +import com.whitefish.app.bt.BleDevice +import com.whitefish.ring.bt.OnBleConnectionListener +import com.whitefish.ring.bt.OnBleScanCallback +import com.whitefish.ring.bean.ui.Device import com.whitefish.ring.device.IDeviceManager +import io.github.aakira.napier.Napier +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import lib.linktop.nexring.api.BATTERY_STATE_CHARGING +import lib.linktop.nexring.api.LOAD_DATA_EMPTY +import lib.linktop.nexring.api.LOAD_DATA_STATE_COMPLETED +import lib.linktop.nexring.api.LOAD_DATA_STATE_PROCESSING +import lib.linktop.nexring.api.LOAD_DATA_STATE_START +import lib.linktop.nexring.api.NexRingManager +import lib.linktop.nexring.api.OnSleepDataLoadListener +import lib.linktop.nexring.api.SleepData + + +class DeviceManager() : IDeviceManager(), OnBleConnectionListener, OnSleepDataLoadListener { + companion object{ + const val STATE_DEVICE_CHARGING = 1 + const val STATE_DEVICE_DISCHARGING = 0 + const val STATE_DEVICE_DISCONNECTED = -3 + const val STATE_DEVICE_CONNECTING = -2 + const val STATE_DEVICE_CONNECTED = -1 + } + private val context = Application.INSTANTS!! + private var isRegisterBattery = false + val batteryLevel = MutableLiveData(STATE_DEVICE_DISCONNECTED to 0) + private val sycProgress = MutableLiveData(0) + var isSyncingData: Boolean = false +// var homeViewModel: demo.linktop.nexring.ui.HomeViewModel? = null +// var workoutDetailViewModel: demo.linktop.nexring.ui.workout.WorkoutDetailViewModel? = null + + + init { + registerCb() + } + override fun onBleState(state: Int) { + bleStateListeners().forEach { + it.invoke(state) + } + when (state) { + BluetoothProfile.STATE_DISCONNECTED -> { + isRegisterBattery = false + batteryLevel.postValue(STATE_DEVICE_DISCONNECTED to 0) + } + + BluetoothProfile.STATE_CONNECTED -> { + batteryLevel.postValue(STATE_DEVICE_CONNECTED to 0) + } + } + } + + override fun onBleReady() { + bleReadyStateFlow.value = true + postDelay { + NexRingManager.get() + .deviceApi() + .getBatteryInfo { + if (it.state == BATTERY_STATE_CHARGING) { + batteryLevel.postValue(STATE_DEVICE_CHARGING to 0) + } else { + batteryLevel.postValue(STATE_DEVICE_DISCHARGING to it.level) + } + if (!isRegisterBattery) { + isRegisterBattery = true + postDelay { + NexRingManager.get() + .sleepApi() + .syncDataFromDev() + } + } + } + } + } + + override fun onSyncDataFromDevice(state: Int, progress: Int) { + Napier.i( + "onSyncDataFromDevice state: $state, progress: $progress" + ) + when (state) { + LOAD_DATA_EMPTY -> { + Napier.e("Empty data") + //TODO Callback when no data is received from the device. + } + + LOAD_DATA_STATE_START -> { + isSyncingData = true + sycProgress.postValue(progress) + } + + LOAD_DATA_STATE_PROCESSING -> sycProgress.postValue(progress) + LOAD_DATA_STATE_COMPLETED -> { + sycProgress.postValue(progress) + isSyncingData = false + //todo sync data complete + } + } + } + + override fun onSyncDataError(errorCode: Int) { + context.cmdErrorTip(errorCode) + } + + override fun onOutputNewSleepData(sleepData: ArrayList?) { + sleepData.also { + if (it.isNullOrEmpty()) { + Napier.i( + "onOutputNewSleepData NULL" + ) + } else { + Napier.i( + "onOutputNewSleepData size ${it.size}" + ) + it.forEachIndexed { index, data -> + Napier.i( + "onOutputNewSleepData $index sleep from ${data.startTs} to ${data.endTs}" + ) + } + } + } + } + + fun registerCb() { + context.bleManager.addOnBleConnectionListener(this) + NexRingManager.get().sleepApi().setOnSleepDataLoadListener(this) + } + + fun unregisterCb() { + NexRingManager.get().sleepApi().setOnSleepDataLoadListener(null) + context.bleManager.removeOnBleConnectionListener(this) + } + + + override fun connect(address: String) { + with(context.bleManager) { + when (bleState.value) { + BluetoothProfile.STATE_DISCONNECTED -> { + batteryLevel.postValue(STATE_DEVICE_CONNECTING to 0) + if (!connect(address)) { + startScan( + 20 * 1000L, + object : OnBleScanCallback { + override fun onScanning(result: BleDevice) { + if (result.device.address == address) { + connect(result.device) + } + } + + override fun onScanFinished() { + + } + }) + } + } + + BluetoothProfile.STATE_CONNECTED -> { + onBleState(bleState.value) + onBleReady() + } + } + } + } + + override fun bind() { + NexRingManager.get() + .deviceApi() + .getBindState { + if (it) { + //todo bind dialog +// AlertDialog.Builder(this@DeviceActivity) +// .setCancelable(false) +// .setTitle(R.string.dialog_title_restricted_mode) +// .setMessage(R.string.dialog_msg_restricted_mode) +// .setNegativeButton(android.R.string.cancel) { _, _ -> +// +// }.setPositiveButton(android.R.string.ok) { _, _ -> +// Logger.i("reset device") +// deviceAdapter.clear() +// NexRingManager.get() +// .deviceApi() +// .factoryReset() +// switchUI(true) +// postDelay { +// Thread.sleep(200) +// DeviceManager.INSTANCE.scan(this@DeviceActivity) +// } +// isConnecting = false +// }.create().show() + } else { + NexRingManager.get() + .deviceApi() + .bind { + //todo bind result + } + } + } + } -class DeviceManager: IDeviceManager { override fun startScan() { - TODO("Not yet implemented") + val bluetoothAdapter = + (context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager).adapter + if (bluetoothAdapter.isEnabled) { + if (!context.bleManager.isScanning) { + context.bleManager.startScan(20 * 1000L, + object : OnBleScanCallback { + @SuppressLint("MissingPermission") + override fun onScanning(result: BleDevice) { + Napier.i("scanned device:${result}") + val newDevices = arrayListOf().apply { + addAll(_deviceList.value) + add(Device(result.device.name,result.device.address)) + } + _deviceList.value = newDevices + } + + override fun onScanFinished() { + } + }) + } + } } override fun stopScan() { - TODO("Not yet implemented") } -} \ No newline at end of file +} diff --git a/shared/src/androidMain/kotlin/com/whitefish/ring/HandlerHelper.kt b/shared/src/androidMain/kotlin/com/whitefish/ring/HandlerHelper.kt new file mode 100644 index 0000000..0a5d894 --- /dev/null +++ b/shared/src/androidMain/kotlin/com/whitefish/ring/HandlerHelper.kt @@ -0,0 +1,18 @@ +package com.whitefish.ring + +import android.os.Handler +import android.os.Looper + +private val uiHandler = Handler(Looper.getMainLooper()) + +fun postDelay(r: Runnable, delay: Long) = uiHandler.postDelayed(r, delay) + +fun postDelay(r: Runnable) = postDelay(r, 100L) + +fun post(r: Runnable) = uiHandler.post(r) + +fun Runnable.handlerPost() = post(this) + +fun Runnable.handlerPostDelay(delay: Long) = postDelay(this, delay) + +fun Runnable.handlerRemove() = uiHandler.removeCallbacks(this) \ No newline at end of file diff --git a/shared/src/androidMain/kotlin/com/whitefish/ring/PermissionManager.kt b/shared/src/androidMain/kotlin/com/whitefish/ring/PermissionManager.kt new file mode 100644 index 0000000..a832abc --- /dev/null +++ b/shared/src/androidMain/kotlin/com/whitefish/ring/PermissionManager.kt @@ -0,0 +1,112 @@ +package com.whitefish.ring + +import android.Manifest +import android.annotation.SuppressLint +import android.app.AlertDialog +import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothManager +import android.content.ActivityNotFoundException +import android.content.Context +import android.content.Context.BLUETOOTH_SERVICE +import android.content.Intent +import android.content.pm.PackageManager +import android.location.LocationManager +import android.os.Build +import android.provider.Settings +import androidx.activity.result.ActivityResultLauncher +import androidx.core.app.ActivityCompat +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +object PermissionManager { + var permissionChecker: ActivityResultLauncher>? = null + + @SuppressLint("MissingPermission") + fun checkPermission(context: Context) { + context.apply { + val dinedPermissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + checkDeniedPermissions( + this, + Manifest.permission.BLUETOOTH_SCAN, + Manifest.permission.BLUETOOTH_CONNECT + ) + } else { + checkDeniedPermissions( + this, + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION + ) + } + if (dinedPermissions != null) { + permissionChecker?.launch(dinedPermissions) + return + }else{ + CoroutineScope(Dispatchers.IO).launch { + obtainDeviceManager().blePowerState.emit(true) + } + } + if (!locationServiceAllowed()) { + AlertDialog.Builder(this) + .setMessage(com.whitefish.ring.R.string.dialog_msg_turn_on_location_service) + .setCancelable(false) + .setPositiveButton(android.R.string.ok) { _, _ -> + goEnableLocationServicePage() + } + .create().show() + return + } + val bluetoothAdapter = + (getSystemService(BLUETOOTH_SERVICE) as BluetoothManager).adapter + if (!bluetoothAdapter.isEnabled) { + startActivity(Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)) + } + } + } + + private fun Context.locationServiceAllowed(): Boolean { + return if (Build.VERSION.SDK_INT in Build.VERSION_CODES.M..Build.VERSION_CODES.R) { + val manager = getSystemService(Context.LOCATION_SERVICE) as LocationManager + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) manager.isLocationEnabled + else manager.isProviderEnabled(LocationManager.GPS_PROVIDER) + } else { + //Other versions do not need to turn on location services, + //so it can be considered that location services are turned on. + true + } + } + + private fun checkDeniedPermissions( + context: Context, + vararg permissions: String, + ): Array? { + val dinedPermissions: MutableList = ArrayList() + for (permission in permissions) { + if (ActivityCompat.checkSelfPermission(context, permission) + != PackageManager.PERMISSION_GRANTED + ) { + dinedPermissions.add(permission) + } + } + return if (dinedPermissions.isEmpty()) null else dinedPermissions.toTypedArray() + } + + fun Context.goEnableLocationServicePage() { + val intent = Intent() + .setAction(Settings.ACTION_LOCATION_SOURCE_SETTINGS) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + try { + startActivity(intent) + } catch (ex: ActivityNotFoundException) { + // The Android SDK doc says that the location settings activity + // may not be found. In that case show the general settings. + // General settings activity + intent.action = Settings.ACTION_SETTINGS + try { + startActivity(intent) + } catch (e: Exception) { + toast("Can not find the LOCATION setting page.") + } + } + } +} \ No newline at end of file diff --git a/shared/src/androidMain/kotlin/com/whitefish/ring/Platform.android.kt b/shared/src/androidMain/kotlin/com/whitefish/ring/Platform.android.kt index a5ac730..eacc34c 100644 --- a/shared/src/androidMain/kotlin/com/whitefish/ring/Platform.android.kt +++ b/shared/src/androidMain/kotlin/com/whitefish/ring/Platform.android.kt @@ -1,13 +1,18 @@ package com.whitefish.ring +import android.Manifest +import android.content.Context +import android.content.pm.PackageManager import android.os.Build import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Dp -import android.content.Context +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat import com.whitefish.ring.device.IDeviceManager + class AndroidPlatform : Platform { override val name: String = "Android ${Build.VERSION.SDK_INT}" } @@ -56,6 +61,7 @@ private fun getNavigationBarHeightPx(context: Context): Int { return navigationBarHeight } +private val DeviceInstance = DeviceManager() actual fun obtainDeviceManager(): IDeviceManager { - return DeviceManager() -} \ No newline at end of file + return DeviceInstance +} diff --git a/shared/src/androidMain/kotlin/com/whitefish/ring/Utils.kt b/shared/src/androidMain/kotlin/com/whitefish/ring/Utils.kt new file mode 100644 index 0000000..dec9586 --- /dev/null +++ b/shared/src/androidMain/kotlin/com/whitefish/ring/Utils.kt @@ -0,0 +1,49 @@ +package com.whitefish.ring + +import android.Manifest +import android.annotation.SuppressLint +import android.app.AlertDialog +import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothManager +import android.content.ActivityNotFoundException +import android.content.Context +import android.content.Context.BLUETOOTH_SERVICE +import android.content.Intent +import android.content.pm.PackageManager +import android.location.LocationManager +import android.os.Build +import android.provider.Settings +import android.widget.Toast +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts +import androidx.annotation.StringRes +import androidx.core.app.ActivityCompat +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +fun Context.cmdErrorTip(code: Int) { + when (code) { + 0 -> toast(R.string.cmd_execute_success) + 1 -> toast(R.string.cmd_execute_failed_1) + 2 -> toast(R.string.cmd_execute_failed_2) + 3 -> toast(R.string.cmd_execute_failed_3) + 4 -> toast(R.string.cmd_execute_failed_4) + 5 -> toast(R.string.cmd_execute_failed_5) + 6 -> toast(R.string.cmd_execute_failed_6) + } +} + +var toast: Toast? = null + +fun Context.toast(tip: String) { + toast?.cancel() + toast = Toast.makeText(this, tip, Toast.LENGTH_SHORT) + .apply { show() } +} + +fun Context.toast(@StringRes tip: Int) { + toast?.cancel() + toast = Toast.makeText(this, tip, Toast.LENGTH_SHORT) + .apply { show() } +} \ No newline at end of file diff --git a/shared/src/androidMain/kotlin/com/whitefish/ring/bt/BleDevice.kt b/shared/src/androidMain/kotlin/com/whitefish/ring/bt/BleDevice.kt new file mode 100644 index 0000000..9d18098 --- /dev/null +++ b/shared/src/androidMain/kotlin/com/whitefish/ring/bt/BleDevice.kt @@ -0,0 +1,15 @@ +package com.whitefish.app.bt + +import android.bluetooth.BluetoothDevice + +data class BleDevice( + val device: BluetoothDevice, + val color: Int, + val size: Int, + val batteryState: Int? = null, + val batteryLevel: Int? = null, + /*val chipMode: Int = 0,*/ + val generation: Int? = null, + val sn: String? = null, + var rssi: Int, +) \ No newline at end of file diff --git a/shared/src/androidMain/kotlin/com/whitefish/ring/bt/BleManager.kt b/shared/src/androidMain/kotlin/com/whitefish/ring/bt/BleManager.kt new file mode 100644 index 0000000..536f62d --- /dev/null +++ b/shared/src/androidMain/kotlin/com/whitefish/ring/bt/BleManager.kt @@ -0,0 +1,423 @@ +package com.whitefish.app.bt + +import android.annotation.SuppressLint +import android.app.AlertDialog +import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothGatt +import android.bluetooth.BluetoothGattDescriptor +import android.bluetooth.BluetoothManager +import android.bluetooth.BluetoothProfile +import android.bluetooth.le.ScanCallback +import android.bluetooth.le.ScanResult +import android.bluetooth.le.ScanSettings +import android.content.Context +import android.content.pm.PackageManager +import android.os.Build +import com.whitefish.ring.Application +import com.whitefish.ring.R +import com.whitefish.ring.bt.OnBleConnectionListener +import com.whitefish.ring.bt.OnBleScanCallback +import com.whitefish.ring.handlerRemove +import com.whitefish.ring.post +import com.whitefish.ring.postDelay +import io.github.aakira.napier.Napier +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import lib.linktop.nexring.api.NexRingBluetoothGattCallback +import lib.linktop.nexring.api.NexRingManager +import lib.linktop.nexring.api.OEM_AUTHENTICATION_FAILED_FOR_CHECK_R2 +import lib.linktop.nexring.api.OEM_AUTHENTICATION_FAILED_FOR_DECRYPT +import lib.linktop.nexring.api.OEM_AUTHENTICATION_FAILED_FOR_SN_NULL +import lib.linktop.nexring.api.OEM_AUTHENTICATION_START +import lib.linktop.nexring.api.OEM_AUTHENTICATION_SUCCESS +import lib.linktop.nexring.api.matchFromAdvertisementData +import lib.linktop.nexring.api.parseScanRecord + + +private const val OEM_STEP_CHECK_OEM_AUTHENTICATION_STATUS = 0 +private const val OEM_STEP_AUTHENTICATE_OEM = 1 +private const val OEM_STEP_TIMESTAMP_SYNC = 2 +private const val OEM_STEP_PROCESS_COMPLETED = 3 + +class BleManager(val app: Application) { + private val tag = "BleManager" + private val mBluetoothAdapter = + (app.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager).adapter + + private val mOnBleConnectionListeners: MutableList = ArrayList() + + private var mOnBleScanCallback: OnBleScanCallback? = null + var bleGatt: BluetoothGatt? = null + private val scanDevMacList: MutableList = ArrayList() + var isScanning = false + + private val mScanCallback = object : ScanCallback() { + + @SuppressLint("MissingPermission") + override fun onScanResult(callbackType: Int, result: ScanResult) { + super.onScanResult(callbackType, result) +// loge( +// "JKL", +// "address ${result.device.address}, scanRecord.bytes ${result.scanRecord?.bytes.toByteArrayString()}" +// ) + synchronized(scanDevMacList) { + val scanRecord = result.scanRecord + if (scanRecord != null) { + val bytes = scanRecord.bytes + if (bytes.matchFromAdvertisementData()) { + val address = result.device.address + if (!scanDevMacList.contains(address).apply { + Napier.i{"scanDevMacList contains address($address) = ${!this}"} + }) { + val bleDevice = bytes.parseScanRecord().run { + BleDevice( + result.device, color, size, + batteryState, batteryLevel, + /*chipMode,*/ generation, sn, + result.rssi + ) + } + scanDevMacList.add(address) + mOnBleScanCallback?.apply { + + post { + onScanning(bleDevice) + } + } + } + } + } + } + } + } + + private val scanStopRunnable = Runnable { + cancelScan() + } + + var bleState = MutableStateFlow(0) + var connectedDevice: BluetoothDevice? = null + + private val _oemStepComplete = MutableStateFlow(false) + val oemStepComplete = _oemStepComplete.asStateFlow() + + private val mGattCallback = object : NexRingBluetoothGattCallback(NexRingManager.get()) { + + @SuppressLint("MissingPermission") + override fun onConnectionStateChange( + gatt: BluetoothGatt, status: Int, newState: Int, + ) { + super.onConnectionStateChange(gatt, status, newState) + Napier.i ( + "onConnectionStateChange->status:$status, newState:$newState" + ) + when (newState) { + BluetoothProfile.STATE_DISCONNECTED -> { + NexRingManager.get().apply { + setBleGatt(null) + unregisterRingService() + } + connectedDevice = null + gatt.close() + bleState.value = BluetoothProfile.STATE_DISCONNECTED + postBleState() + _oemStepComplete.value = false + } + + BluetoothProfile.STATE_CONNECTING -> { + bleState.value = BluetoothProfile.STATE_CONNECTING + postBleState() + } + + BluetoothProfile.STATE_CONNECTED -> { + bleState.value = BluetoothProfile.STATE_CONNECTED + connectedDevice = gatt.device + postBleState() + // The default MTU for ATT in the core spec is 23 bytes, with 1 byte for ATT's Opcode, 2 bytes for ATT's Handle, and 20 bytes for GATT. + // So if you want to set 40, you should request the MTU to be set to 43. + gatt.requestMtu(40 + 3) + } + } + } + + @SuppressLint("MissingPermission") + override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) { + super.onMtuChanged(gatt, mtu, status) + when (status) { + BluetoothGatt.GATT_SUCCESS -> { + Napier.i{ "onMtuChanged success."} + gatt.discoverServices() + } + + BluetoothGatt.GATT_FAILURE -> { + Napier.i( "onMtuChanged failure.") + } + + else -> Napier.i("onMtuChanged unknown status $status.") + } + } + + @SuppressLint("MissingPermission") + override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) { + super.onServicesDiscovered(gatt, status) + Napier.i( "onServicesDiscovered(), status:${status}") + // Refresh device cache. This is the safest place to initiate the procedure. + if (status == BluetoothGatt.GATT_SUCCESS) { + NexRingManager.get().setBleGatt(gatt) + Napier.i("onServicesDiscovered(), registerHealthData") + postDelay { + NexRingManager.get().registerRingService() + } + } + } + + override fun onDescriptorWrite( + gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int, + ) { + super.onDescriptorWrite(gatt, descriptor, status) + if (status == BluetoothGatt.GATT_SUCCESS && + NexRingManager.get().isRingServiceRegistered() + ) { +// post { +// //you need to synchronize the timestamp with the device first after +// //the the service registration is successful. +// NexRingManager.get() +// .settingsApi() +// .timestampSync(System.currentTimeMillis()) { +// synchronized(mOnBleConnectionListeners) { +// mOnBleConnectionListeners.forEach { +// it.onBleReady() +// } +// } +// } +// } + OemAuthenticationProcess().start() + } + } + } + + @SuppressLint("MissingPermission", "ObsoleteSdkInt") + private fun connectInterval(device: BluetoothDevice) { + Napier.i("connect gatt to ${device.address}") + bleGatt = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { +// device.connectGatt(context, false, gattCallback) + device.connectGatt(app, false, mGattCallback, BluetoothDevice.TRANSPORT_LE) + } else { + device.connectGatt(app, false, mGattCallback) + }.apply { connect() } + } + + fun isSupportBle(): Boolean = +// Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && + app.applicationContext.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) + + + @SuppressLint("MissingPermission") + fun startScan(timeoutMillis: Long, callback: OnBleScanCallback) { + isScanning = true + mOnBleScanCallback = callback + scanDevMacList.clear() + val scanSettings = ScanSettings.Builder() + .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) + .build() + mBluetoothAdapter.bluetoothLeScanner.startScan(null, scanSettings, mScanCallback) + postDelay(scanStopRunnable, timeoutMillis) + } + + @SuppressLint("MissingPermission") + fun cancelScan() { + if (isScanning) { + isScanning = false + mBluetoothAdapter.bluetoothLeScanner.stopScan(mScanCallback) + post { + mOnBleScanCallback?.onScanFinished() + mOnBleScanCallback = null + scanStopRunnable.handlerRemove() + } + } + scanDevMacList.clear() + } + + @SuppressLint("MissingPermission") + fun connect(address: String): Boolean { + val remoteDevice = mBluetoothAdapter.getRemoteDevice(address) + Napier.i( "connect to remoteDevice by address, ${remoteDevice.name}") + return if (!remoteDevice.name.isNullOrEmpty()) { + connect(remoteDevice) + true + } else { + Napier.i("reject, because it cannot connect success.") + false + } + } + + fun connect(device: BluetoothDevice) { + val delayConnect = isScanning + cancelScan() + if (delayConnect) { + Napier.i( "connect to ${device.address}, delay 200L") + postDelay({ + Napier.i("delay finish, connect to ${device.address}") + connectInterval(device) + }, 200L) + } else { + Napier.i("connect to ${device.address} right now.") + connectInterval(device) + } + } + + @SuppressLint("MissingPermission") + fun disconnect() { + bleGatt?.disconnect() + bleGatt = null + } + + + fun addOnBleConnectionListener(listener: OnBleConnectionListener) { + synchronized(mOnBleConnectionListeners) { + mOnBleConnectionListeners.add(listener) + } + } + + fun removeOnBleConnectionListener(listener: OnBleConnectionListener) { + synchronized(mOnBleConnectionListeners) { + mOnBleConnectionListeners.remove(listener) + } + } + + fun postBleState() { + post { + synchronized(mOnBleConnectionListeners) { + mOnBleConnectionListeners.forEach { + it.onBleState(bleState.value) + } + } + } + } + + inner class OemAuthenticationProcess : Thread() { + + private val innerTag = "OemAuthenticationProcess" + private val locked = Object() + private var step = OEM_STEP_CHECK_OEM_AUTHENTICATION_STATUS + + override fun run() { + while (step < OEM_STEP_PROCESS_COMPLETED) { + sleep(200L) + synchronized(locked) { + when (step) { + OEM_STEP_CHECK_OEM_AUTHENTICATION_STATUS -> { + Napier.i( "OEM_STEP_CHECK_OEM_AUTHENTICATION_STATUS") + NexRingManager.get().securityApi().checkOemAuthenticationStatus { + step = if (it) OEM_STEP_AUTHENTICATE_OEM else OEM_STEP_TIMESTAMP_SYNC + synchronized(locked) { + locked.notify() + } + } + } + + OEM_STEP_AUTHENTICATE_OEM -> { + Napier.i( "OEM_STEP_AUTHENTICATE_OEM") + NexRingManager.get().securityApi().authenticateOem { result -> + when (result) { + OEM_AUTHENTICATION_FAILED_FOR_CHECK_R2 -> { + Napier.i( "OEM_AUTHENTICATION_FAILED_FOR_CHECK_R2") + step = OEM_STEP_PROCESS_COMPLETED + result.showOemAuthFailDialog() + synchronized(locked) { + locked.notify() + } + } + + OEM_AUTHENTICATION_FAILED_FOR_DECRYPT -> { + Napier.i( "OEM_AUTHENTICATION_FAILED_FOR_DECRYPT") + step = OEM_STEP_PROCESS_COMPLETED + result.showOemAuthFailDialog() + synchronized(locked) { + locked.notify() + } + } + + OEM_AUTHENTICATION_FAILED_FOR_SN_NULL -> { + Napier.i("OEM_AUTHENTICATION_FAILED_FOR_SN_NULL") + step = OEM_STEP_PROCESS_COMPLETED + result.showOemAuthFailDialog() + synchronized(locked) { + locked.notify() + } + } + + OEM_AUTHENTICATION_START -> { + Napier.i( "OEM_AUTHENTICATION_START") + } + + OEM_AUTHENTICATION_SUCCESS -> { + Napier.i( "OEM_AUTHENTICATION_SUCCESS") + step = OEM_STEP_TIMESTAMP_SYNC + synchronized(locked) { + locked.notify() + } + } + } + } + } + + OEM_STEP_TIMESTAMP_SYNC -> { + Napier.i( "OEM_STEP_TIMESTAMP_SYNC") + NexRingManager.get() + .settingsApi() + .timestampSync(System.currentTimeMillis()) { + Napier.i( "OEM_STEP_TIMESTAMP_SYNC result $it") + synchronized(mOnBleConnectionListeners) { + post { + mOnBleConnectionListeners.forEach { listener -> + listener.onBleReady() + } + } + } + step = OEM_STEP_PROCESS_COMPLETED + synchronized(locked) { + locked.notify() + } + } + } + } + locked.wait() + } + } + _oemStepComplete.value = true + Napier.i("OEM_STEP_PROCESS_COMPLETED") + } + } + + private fun Int.showOemAuthFailDialog() { + app.mActivityLifecycleCb.currAct.apply { + if (this != null) { + val message = when (this@showOemAuthFailDialog) { + OEM_AUTHENTICATION_FAILED_FOR_SN_NULL -> { + getString(R.string.dialog_msg_oem_auth_failed_cause_by_sn_null) + } + + OEM_AUTHENTICATION_FAILED_FOR_DECRYPT -> { + getString(R.string.dialog_msg_oem_auth_failed_cause_by_r1_to_r2) + } + + OEM_AUTHENTICATION_FAILED_FOR_CHECK_R2 -> { + getString(R.string.dialog_msg_oem_auth_failed_cause_by_check_r2) + } + + else -> "Unknown error." + } + runOnUiThread { + AlertDialog.Builder(this) + .setCancelable(false) + .setTitle(R.string.dialog_title_oem_auth_failed) + .setMessage(message) + .setPositiveButton(R.string.btn_label_disconnected) { _, _ -> + disconnect() + }.create().show() + } + } else disconnect() + } + } +} \ No newline at end of file diff --git a/shared/src/androidMain/kotlin/com/whitefish/ring/bt/OnBleConnectionListener.kt b/shared/src/androidMain/kotlin/com/whitefish/ring/bt/OnBleConnectionListener.kt new file mode 100644 index 0000000..995afd9 --- /dev/null +++ b/shared/src/androidMain/kotlin/com/whitefish/ring/bt/OnBleConnectionListener.kt @@ -0,0 +1,8 @@ +package com.whitefish.ring.bt + +interface OnBleConnectionListener { + + fun onBleState(state: Int) + + fun onBleReady() +} \ No newline at end of file diff --git a/shared/src/androidMain/kotlin/com/whitefish/ring/bt/OnBleScanCallback.kt b/shared/src/androidMain/kotlin/com/whitefish/ring/bt/OnBleScanCallback.kt new file mode 100644 index 0000000..936d7c5 --- /dev/null +++ b/shared/src/androidMain/kotlin/com/whitefish/ring/bt/OnBleScanCallback.kt @@ -0,0 +1,10 @@ +package com.whitefish.ring.bt + +import com.whitefish.app.bt.BleDevice + +interface OnBleScanCallback { + + fun onScanning(result: BleDevice) + + fun onScanFinished() +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/whitefish/ring/App.kt b/shared/src/commonMain/kotlin/com/whitefish/ring/App.kt index 3343ab1..1cb5414 100644 --- a/shared/src/commonMain/kotlin/com/whitefish/ring/App.kt +++ b/shared/src/commonMain/kotlin/com/whitefish/ring/App.kt @@ -3,6 +3,8 @@ package com.whitefish.ring import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.* import androidx.compose.ui.Modifier +import com.whitefish.ring.ui.guide.DeviceScreen +import com.whitefish.ring.ui.guide.GuideNavigationScreen import com.whitefish.ring.ui.home.HomeScreen import org.jetbrains.compose.ui.tooling.preview.Preview @@ -10,8 +12,12 @@ import org.jetbrains.compose.ui.tooling.preview.Preview @Preview fun App() { MaterialTheme { - HomeScreen( - modifier = Modifier - ) +// HomeScreen( +// modifier = Modifier +// ) +// DeviceScreen{ +// +// } + GuideNavigationScreen() } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/whitefish/ring/bean/ui/Device.kt b/shared/src/commonMain/kotlin/com/whitefish/ring/bean/ui/Device.kt new file mode 100644 index 0000000..d5c3beb --- /dev/null +++ b/shared/src/commonMain/kotlin/com/whitefish/ring/bean/ui/Device.kt @@ -0,0 +1,3 @@ +package com.whitefish.ring.bean.ui + +data class Device (val name: String,val mac: String) diff --git a/shared/src/commonMain/kotlin/com/whitefish/ring/device/IDeviceManager.kt b/shared/src/commonMain/kotlin/com/whitefish/ring/device/IDeviceManager.kt index 55a33fb..7fd2eec 100644 --- a/shared/src/commonMain/kotlin/com/whitefish/ring/device/IDeviceManager.kt +++ b/shared/src/commonMain/kotlin/com/whitefish/ring/device/IDeviceManager.kt @@ -1,6 +1,30 @@ package com.whitefish.ring.device -interface IDeviceManager { - fun startScan() - fun stopScan() +import com.whitefish.ring.bean.ui.Device +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.asStateFlow + +abstract class IDeviceManager { + protected val _deviceList = MutableStateFlow>(emptyList()) + val deviceList = _deviceList.asStateFlow() + protected val _bleState = MutableStateFlow(-1) + val bleState = _bleState.asStateFlow() + val bleReadyStateFlow = MutableStateFlow(false) + val blePowerState = MutableStateFlow(false) // ios的蓝牙是懒加载的,安卓则无此特性 + + private val bleStateListeners = arrayListOf<(Int) -> Unit>() + fun bleStateListeners() = bleStateListeners + + + fun setOnBleStateChange(event: (Int) -> Unit) { + bleStateListeners.add(event) + } + + + abstract fun startScan() + abstract fun stopScan() + abstract fun connect(mac: String) + abstract fun bind() } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/ConnectionGuideScreen.kt b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/ConnectionGuideScreen.kt new file mode 100644 index 0000000..959e681 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/ConnectionGuideScreen.kt @@ -0,0 +1,144 @@ +package com.whitefish.ring.ui.guide + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun ConnectionGuideScreen( + onNextClick: () -> Unit = {} +) { + Box( + modifier = Modifier + .fillMaxSize() + .background(Color(0xFFF5F5F5)) + ) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Spacer(modifier = Modifier.height(80.dp)) + + // 标题 + Text( + text = "连接您的Acti戒指", + fontSize = 24.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF333333), + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() + ) + + Spacer(modifier = Modifier.height(40.dp)) + + // 说明文字 + Text( + text = "将您的戒指连接到充电器,并继续下一步。请确保您的手机已启用蓝牙功能。", + fontSize = 16.sp, + color = Color(0xFF666666), + textAlign = TextAlign.Center, + lineHeight = 24.sp, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp) + ) + + Spacer(modifier = Modifier.height(60.dp)) + + // 戒指和充电器图片占位符 + Box( + modifier = Modifier + .size(280.dp) + .background( + Color.White, + RoundedCornerShape(20.dp) + ), + contentAlignment = Alignment.Center + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + // 戒指图标 + Text( + text = "💍", + fontSize = 80.sp + ) + + Spacer(modifier = Modifier.height(20.dp)) + + // 连接线 + Box( + modifier = Modifier + .width(60.dp) + .height(4.dp) + .background( + Color(0xFF007AFF), + RoundedCornerShape(2.dp) + ) + ) + + Spacer(modifier = Modifier.height(20.dp)) + + // 充电器图标 + Box( + modifier = Modifier + .size(80.dp) + .background( + Color(0xFF333333), + RoundedCornerShape(40.dp) + ), + contentAlignment = Alignment.Center + ) { + Text( + text = "⚡", + fontSize = 40.sp, + color = Color.White + ) + } + } + } + + Spacer(modifier = Modifier.weight(1f)) + + // 下一步按钮 + Button( + onClick = onNextClick, + modifier = Modifier + .fillMaxWidth() + .height(56.dp), + shape = RoundedCornerShape(28.dp), + colors = ButtonDefaults.buttonColors( + containerColor = Color(0xFF007AFF), + contentColor = Color.White + ) + ) { + Text( + text = "下一步", + fontSize = 18.sp, + fontWeight = FontWeight.Medium + ) + } + + Spacer(modifier = Modifier.height(40.dp)) + } + } +} + +@Composable +@Preview +fun ConnectionGuideScreenPreview() { + ConnectionGuideScreen() +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/DeviceScreen.kt b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/DeviceScreen.kt new file mode 100644 index 0000000..e3ad73a --- /dev/null +++ b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/DeviceScreen.kt @@ -0,0 +1,150 @@ +package com.whitefish.ring.ui.guide + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.lifecycle.viewmodel.compose.viewModel +import com.whitefish.ring.bean.ui.Device +import com.whitefish.ring.device.IDeviceManager +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun DeviceScreen(onBind:() -> Unit){ + val viewModel: DeviceViewModel = viewModel { DeviceViewModel() } + val uiState by viewModel.uiState.collectAsState() + val bindState by viewModel.manager.bleReadyStateFlow.collectAsState() + + if (bindState){ + onBind.invoke() + } + + Box( + modifier = Modifier + .fillMaxSize() + .background(Color(0xFFF5F5F5)) + ) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 20.dp) + ) { + // 标题 + Text( + text = "附近设备", + fontSize = 24.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF333333), + modifier = Modifier + .fillMaxWidth() + .padding(top = 60.dp, bottom = 40.dp), + textAlign = TextAlign.Center + ) + + // 设备列表 + LazyColumn( + verticalArrangement = Arrangement.spacedBy(16.dp), + modifier = Modifier.weight(1f) + ) { + items(uiState.deviceList) { device -> + DeviceItem(device = device){ + viewModel.connect(it.mac) + } + } + } + } + + // 底部提示 + Text( + text = "连接失败?", + fontSize = 16.sp, + color = Color(0xFF666666), + modifier = Modifier + .align(Alignment.BottomCenter) + .padding(bottom = 21.dp) + ) + } +} + +@Composable +private fun DeviceItem(device: Device,onClick:(Device)-> Unit) { + Card( + modifier = Modifier + .fillMaxWidth() + .height(80.dp), + onClick = { + onClick.invoke(device) + }, + shape = RoundedCornerShape(16.dp), + colors = CardDefaults.cardColors( + containerColor = Color.White + ), + elevation = CardDefaults.cardElevation( + defaultElevation = 2.dp + ) + ) { + Row( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 20.dp, vertical = 16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + // 设备图标占位符 + Box( + modifier = Modifier + .size(48.dp) + .clip(RoundedCornerShape(24.dp)) + .background(Color(0xFFE5E5E5)), + contentAlignment = Alignment.Center + ) { + // 这里可以放置实际的设备图标 + Text( + text = "💍", + fontSize = 24.sp + ) + } + + Spacer(modifier = Modifier.width(16.dp)) + + // 设备信息 + Column( + modifier = Modifier.weight(1f) + ) { + Text( + text = device.name, + fontSize = 18.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF333333) + ) + + Spacer(modifier = Modifier.height(4.dp)) + + Text( + text = "设备号:${device.mac}", + fontSize = 14.sp, + color = Color(0xFF999999) + ) + } + } + } +} + +@Composable +@Preview +fun Device(){ + DeviceScreen{ + + } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/DeviceViewModel.kt b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/DeviceViewModel.kt new file mode 100644 index 0000000..90c16dc --- /dev/null +++ b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/DeviceViewModel.kt @@ -0,0 +1,64 @@ +package com.whitefish.ring.ui.guide + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.whitefish.ring.bean.ui.Device +import com.whitefish.ring.device.IDeviceManager +import com.whitefish.ring.obtainDeviceManager +import io.github.aakira.napier.Napier +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch + +class DeviceViewModel: ViewModel() { +// var currentStep by remember { mutableStateOf(GuideStep.WELCOME) } + + class UiState( + val deviceList: List = emptyList() + ) + + val manager = obtainDeviceManager() + private val _uiState = MutableStateFlow(UiState()) + val uiState = _uiState.asStateFlow() + + init { + Napier.i { "DeviceViewModel initializing..." } + + viewModelScope.launch { + + launch { + manager.deviceList.collectLatest { + Napier.i { "new device:${it}" } + _uiState.value = UiState(it) + } + } + + launch { + manager.blePowerState.collectLatest { + if (it){ + manager.startScan() + } + } + } + + launch { + manager.bleReadyStateFlow.collectLatest { + Napier.i { "ble ready:${it}" } + if (it){ + manager.bind() + } + } + } + } + } + + fun connect(mac: String){ + manager.connect(mac) + } + +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/DominantHandScreen.kt b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/DominantHandScreen.kt new file mode 100644 index 0000000..384b325 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/DominantHandScreen.kt @@ -0,0 +1,190 @@ +package com.whitefish.ring.ui.guide + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun DominantHandScreen( + onNextClick: () -> Unit = {}, + onHandSelected: (Hand) -> Unit = {} +) { + var selectedHand by remember { mutableStateOf(null) } + + Box( + modifier = Modifier + .fillMaxSize() + .background(Color(0xFFF5F5F5)) + ) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Spacer(modifier = Modifier.height(80.dp)) + + // 标题 + Text( + text = "惯用手", + fontSize = 24.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF333333), + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() + ) + + Spacer(modifier = Modifier.height(16.dp)) + + // 副标题 + Text( + text = "请选择您的惯用手", + fontSize = 16.sp, + color = Color(0xFF666666), + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() + ) + + Spacer(modifier = Modifier.height(80.dp)) + + // 选择区域 + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(24.dp) + ) { + // 左手选项 + HandOptionCard( + hand = Hand.LEFT, + isSelected = selectedHand == Hand.LEFT, + onClick = { + selectedHand = Hand.LEFT + onHandSelected(Hand.LEFT) + }, + modifier = Modifier.weight(1f) + ) + + // 右手选项 + HandOptionCard( + hand = Hand.RIGHT, + isSelected = selectedHand == Hand.RIGHT, + onClick = { + selectedHand = Hand.RIGHT + onHandSelected(Hand.RIGHT) + }, + modifier = Modifier.weight(1f) + ) + } + + Spacer(modifier = Modifier.weight(1f)) + + // 下一步按钮 + Button( + onClick = onNextClick, + enabled = selectedHand != null, + modifier = Modifier + .fillMaxWidth() + .height(56.dp), + shape = RoundedCornerShape(28.dp), + colors = ButtonDefaults.buttonColors( + containerColor = if (selectedHand != null) Color(0xFF007AFF) else Color(0xFFCCCCCC), + contentColor = Color.White + ) + ) { + Text( + text = "下一步", + fontSize = 18.sp, + fontWeight = FontWeight.Medium + ) + } + + Spacer(modifier = Modifier.height(40.dp)) + } + } +} + +@Composable +private fun HandOptionCard( + hand: Hand, + isSelected: Boolean, + onClick: () -> Unit, + modifier: Modifier = Modifier +) { + Card( + modifier = modifier + .height(200.dp) + .clickable { onClick() }, + shape = RoundedCornerShape(20.dp), + colors = CardDefaults.cardColors( + containerColor = if (isSelected) Color(0xFFE3F2FD) else Color.White + ), + elevation = CardDefaults.cardElevation( + defaultElevation = if (isSelected) 8.dp else 4.dp + ) + ) { + Box( + modifier = Modifier + .fillMaxSize() + .then( + if (isSelected) { + Modifier.border( + width = 2.dp, + color = Color(0xFF007AFF), + shape = RoundedCornerShape(20.dp) + ) + } else { + Modifier + } + ), + contentAlignment = Alignment.Center + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + // 手部图标占位符 + Box( + modifier = Modifier + .size(100.dp) + .background( + if (isSelected) Color(0xFF007AFF) else Color(0xFFE5E5E5), + RoundedCornerShape(50.dp) + ), + contentAlignment = Alignment.Center + ) { + Text( + text = if (hand == Hand.LEFT) "✋" else "🤚", + fontSize = 48.sp, + color = if (isSelected) Color.White else Color(0xFF666666) + ) + } + + Spacer(modifier = Modifier.height(20.dp)) + + Text( + text = if (hand == Hand.LEFT) "左手" else "右手", + fontSize = 18.sp, + fontWeight = FontWeight.Medium, + color = if (isSelected) Color(0xFF007AFF) else Color(0xFF333333) + ) + } + } + } +} + +@Composable +@Preview +fun DominantHandScreenPreview() { + DominantHandScreen() +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/GuideNavigationScreen.kt b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/GuideNavigationScreen.kt new file mode 100644 index 0000000..160a656 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/GuideNavigationScreen.kt @@ -0,0 +1,100 @@ +package com.whitefish.ring.ui.guide + +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier + +enum class GuideStep { + WELCOME, + REGISTER, + CONNECTION_GUIDE, + DEVICE_LIST, + PERSONAL_INFO, + WEARING_FINGER, + DOMINANT_HAND, + +} + +@Composable +fun GuideNavigationScreen( + modifier: Modifier = Modifier, + onGuideComplete: () -> Unit = {}, + +) { + var currentStep by remember { mutableStateOf(GuideStep.WELCOME) } + + when (currentStep) { + GuideStep.WELCOME -> { + WelcomeScreen( + onStartClick = { + currentStep = GuideStep.REGISTER + } + ) + } + + GuideStep.REGISTER -> { + RegisterScreen( + onLoginClick = { phoneNumber, verificationCode -> + // 这里可以添加登录验证逻辑 + if (phoneNumber.isNotEmpty() && verificationCode.isNotEmpty()) { + currentStep = GuideStep.CONNECTION_GUIDE + } + } + ) + } + + GuideStep.CONNECTION_GUIDE -> { + ConnectionGuideScreen( + onNextClick = { + currentStep = GuideStep.DEVICE_LIST + } + ) + } + + + GuideStep.DEVICE_LIST -> { + DeviceScreen{ + currentStep = GuideStep.PERSONAL_INFO + } + } + + GuideStep.PERSONAL_INFO -> { + PersonalInfoScreen( + onNextClick = { + currentStep = GuideStep.WEARING_FINGER + }, + onGenderClick = { + // 这里可以打开性别选择对话框或跳转到专门的性别选择页面 + }, + onBirthdayClick = { + // 这里可以打开日期选择器 + }, + onHeightClick = { + // 这里可以打开身高选择器 + } + ) + } + + GuideStep.WEARING_FINGER -> { + WearingFingerScreen( + onNextClick = { + currentStep = GuideStep.DOMINANT_HAND + }, + onFingerSelected = { position -> + // 保存选择的佩戴位置 + } + ) + } + + GuideStep.DOMINANT_HAND -> { + DominantHandScreen( + onNextClick = { + // 完成所有引导步骤 + onGuideComplete() + }, + onHandSelected = { hand -> + // 保存选择的惯用手 + } + ) + } + } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/GuideScreensPreviews.kt b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/GuideScreensPreviews.kt new file mode 100644 index 0000000..9fb1042 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/GuideScreensPreviews.kt @@ -0,0 +1,107 @@ +package com.whitefish.ring.ui.guide + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +@Preview +fun AllGuideScreensPreview() { + LazyColumn( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.spacedBy(16.dp), + contentPadding = PaddingValues(16.dp) + ) { + item { + Card( + modifier = Modifier + .fillMaxWidth() + .height(600.dp) + ) { + WelcomeScreen() + } + } + + item { + Card( + modifier = Modifier + .fillMaxWidth() + .height(600.dp) + ) { + RegisterScreen() + } + } + + item { + Card( + modifier = Modifier + .fillMaxWidth() + .height(600.dp) + ) { + ConnectionGuideScreen() + } + } + + item { + Card( + modifier = Modifier + .fillMaxWidth() + .height(600.dp) + ) { + SearchTip() + } + } + + item { + Card( + modifier = Modifier + .fillMaxWidth() + .height(600.dp) + ) { + DeviceScreen{ + + } + } + } + + item { + Card( + modifier = Modifier + .fillMaxWidth() + .height(600.dp) + ) { + PersonalInfoScreen() + } + } + + item { + Card( + modifier = Modifier + .fillMaxWidth() + .height(600.dp) + ) { + WearingFingerScreen() + } + } + + item { + Card( + modifier = Modifier + .fillMaxWidth() + .height(600.dp) + ) { + DominantHandScreen() + } + } + } +} + +@Composable +@Preview +fun GuideNavigationPreview() { + GuideNavigationScreen() +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/PersonalInfoScreen.kt b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/PersonalInfoScreen.kt new file mode 100644 index 0000000..2f8f79f --- /dev/null +++ b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/PersonalInfoScreen.kt @@ -0,0 +1,174 @@ +package com.whitefish.ring.ui.guide + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import org.jetbrains.compose.ui.tooling.preview.Preview + +data class PersonalInfo( + val gender: String = "", + val birthday: String = "", + val height: String = "" +) + +@Composable +fun PersonalInfoScreen( + onNextClick: () -> Unit = {}, + onGenderClick: () -> Unit = {}, + onBirthdayClick: () -> Unit = {}, + onHeightClick: () -> Unit = {} +) { + var personalInfo by remember { mutableStateOf(PersonalInfo()) } + + Box( + modifier = Modifier + .fillMaxSize() + .background(Color(0xFFF5F5F5)) + ) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 24.dp) + ) { + Spacer(modifier = Modifier.height(80.dp)) + + // 标题 + Text( + text = "个人信息完善", + fontSize = 24.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF333333), + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() + ) + + Spacer(modifier = Modifier.height(16.dp)) + + // 副标题 + Text( + text = "完善您的个人信息以便更好的服务", + fontSize = 16.sp, + color = Color(0xFF666666), + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() + ) + + Spacer(modifier = Modifier.height(60.dp)) + + // 性别选项 + PersonalInfoItem( + title = "性别", + value = personalInfo.gender.ifEmpty { "" }, + onClick = onGenderClick + ) + + Spacer(modifier = Modifier.height(24.dp)) + + // 生日选项 + PersonalInfoItem( + title = "生日", + value = personalInfo.birthday.ifEmpty { "" }, + onClick = onBirthdayClick + ) + + Spacer(modifier = Modifier.height(24.dp)) + + // 身高选项 + PersonalInfoItem( + title = "身高", + value = personalInfo.height.ifEmpty { "" }, + onClick = onHeightClick + ) + + Spacer(modifier = Modifier.weight(1f)) + + // 下一步按钮 + Button( + onClick = onNextClick, + modifier = Modifier + .fillMaxWidth() + .height(56.dp), + shape = RoundedCornerShape(28.dp), + colors = ButtonDefaults.buttonColors( + containerColor = Color(0xFF007AFF), + contentColor = Color.White + ) + ) { + Text( + text = "下一步", + fontSize = 18.sp, + fontWeight = FontWeight.Medium + ) + } + + Spacer(modifier = Modifier.height(40.dp)) + } + } +} + +@Composable +private fun PersonalInfoItem( + title: String, + value: String, + onClick: () -> Unit +) { + Card( + modifier = Modifier + .fillMaxWidth() + .clickable { onClick() }, + shape = RoundedCornerShape(16.dp), + colors = CardDefaults.cardColors( + containerColor = Color.White + ), + elevation = CardDefaults.cardElevation( + defaultElevation = 2.dp + ) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp, vertical = 20.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = title, + fontSize = 18.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF333333), + modifier = Modifier.weight(1f) + ) + + if (value.isNotEmpty()) { + Text( + text = value, + fontSize = 16.sp, + color = Color(0xFF666666), + modifier = Modifier.padding(end = 8.dp) + ) + } + +// Icon( +// imageVector = Icons.Default.KeyboardArrowRight, +// contentDescription = "展开", +// tint = Color(0xFF999999), +// modifier = Modifier.size(24.dp) +// ) + } + } +} + +@Composable +@Preview +fun PersonalInfoScreenPreview() { + PersonalInfoScreen() +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/RegisterScreen.kt b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/RegisterScreen.kt new file mode 100644 index 0000000..e3b7404 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/RegisterScreen.kt @@ -0,0 +1,214 @@ +package com.whitefish.ring.ui.guide + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun RegisterScreen( + onLoginClick: (phoneNumber: String, verificationCode: String) -> Unit = { _, _ -> } +) { + var phoneNumber by remember { mutableStateOf("") } + var verificationCode by remember { mutableStateOf("") } + var isCodeSent by remember { mutableStateOf(false) } + + Box( + modifier = Modifier + .fillMaxSize() + .background(Color(0xFFF5F5F5)) + ) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Spacer(modifier = Modifier.height(120.dp)) + + // 戒指图片占位符 + Box( + modifier = Modifier + .size(160.dp) + .background( + Color(0xFFE5E5E5), + RoundedCornerShape(80.dp) + ), + contentAlignment = Alignment.Center + ) { + Text( + text = "💍", + fontSize = 64.sp + ) + } + + Spacer(modifier = Modifier.height(40.dp)) + + // 欢迎标题 + Column( + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = "Hi,", + fontSize = 28.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF333333), + textAlign = TextAlign.Center + ) + + Text( + text = "欢迎来到Acti", + fontSize = 28.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF333333), + textAlign = TextAlign.Center + ) + } + + Spacer(modifier = Modifier.height(60.dp)) + + // 手机号输入框 + Column( + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = "手机号", + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF333333), + modifier = Modifier.padding(bottom = 8.dp) + ) + + OutlinedTextField( + value = phoneNumber, + onValueChange = { phoneNumber = it }, + placeholder = { + Text( + text = "请输入您的手机号", + color = Color(0xFF999999) + ) + }, + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(12.dp), + colors = OutlinedTextFieldDefaults.colors( + focusedBorderColor = Color(0xFF007AFF), + unfocusedBorderColor = Color(0xFFE5E5E5), + focusedContainerColor = Color.White, + unfocusedContainerColor = Color.White + ), + singleLine = true + ) + } + + Spacer(modifier = Modifier.height(24.dp)) + + // 验证码输入框 + Column( + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = "验证码", + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF333333), + modifier = Modifier.padding(bottom = 8.dp) + ) + + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + OutlinedTextField( + value = verificationCode, + onValueChange = { verificationCode = it }, + placeholder = { + Text( + text = "请输入验证码", + color = Color(0xFF999999) + ) + }, + modifier = Modifier.weight(1f), + shape = RoundedCornerShape(12.dp), + colors = OutlinedTextFieldDefaults.colors( + focusedBorderColor = Color(0xFF007AFF), + unfocusedBorderColor = Color(0xFFE5E5E5), + focusedContainerColor = Color.White, + unfocusedContainerColor = Color.White + ), + singleLine = true + ) + + Spacer(modifier = Modifier.width(12.dp)) + + TextButton( + onClick = { + if (phoneNumber.isNotEmpty()) { + isCodeSent = true + } + }, + enabled = phoneNumber.isNotEmpty() + ) { + Text( + text = if (isCodeSent) "重新发送验证码" else "获取验证码", + fontSize = 14.sp, + color = if (phoneNumber.isNotEmpty()) Color(0xFF007AFF) else Color(0xFF999999) + ) + } + } + } + + Spacer(modifier = Modifier.height(16.dp)) + + // 提示文字 + Text( + text = "未注册的手机号码会自动创建新账号", + fontSize = 12.sp, + color = Color(0xFF999999), + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() + ) + + Spacer(modifier = Modifier.height(40.dp)) + + // 登录按钮 + Button( + onClick = { onLoginClick(phoneNumber, verificationCode) }, + enabled = phoneNumber.isNotEmpty() && verificationCode.isNotEmpty(), + modifier = Modifier + .fillMaxWidth() + .height(56.dp), + shape = RoundedCornerShape(28.dp), + colors = ButtonDefaults.buttonColors( + containerColor = if (phoneNumber.isNotEmpty() && verificationCode.isNotEmpty()) + Color(0xFF007AFF) else Color(0xFFCCCCCC), + contentColor = Color.White + ) + ) { + Text( + text = "登录", + fontSize = 18.sp, + fontWeight = FontWeight.Medium + ) + } + + Spacer(modifier = Modifier.weight(1f)) + } + } +} + +@Composable +@Preview +fun RegisterScreenPreview() { + RegisterScreen() +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/SearchingScreen.kt b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/SearchingScreen.kt new file mode 100644 index 0000000..43b4c7e --- /dev/null +++ b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/SearchingScreen.kt @@ -0,0 +1,166 @@ +package com.whitefish.ring.ui.guide + +import androidx.compose.animation.core.* +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun SearchTip( + onDeviceNotFoundClick: () -> Unit = {}, + onDeviceFound: () -> Unit = {} +) { + // 旋转动画 + val infiniteTransition = rememberInfiniteTransition() + val rotation by infiniteTransition.animateFloat( + initialValue = 0f, + targetValue = 360f, + animationSpec = infiniteRepeatable( + animation = tween(2000, easing = LinearEasing), + repeatMode = RepeatMode.Restart + ) + ) + + // 模拟搜索过程,5秒后跳转到设备列表 + LaunchedEffect(Unit) { + kotlinx.coroutines.delay(5000) + onDeviceFound() + } + + Box( + modifier = Modifier + .fillMaxSize() + .background(Color(0xFFF5F5F5)) + ) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Spacer(modifier = Modifier.height(120.dp)) + + // 标题 + Text( + text = "正在搜索设备...", + fontSize = 24.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF333333), + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() + ) + + Spacer(modifier = Modifier.height(80.dp)) + + // 搜索动画 + Box( + modifier = Modifier + .size(200.dp) + .background( + Color.White, + RoundedCornerShape(100.dp) + ), + contentAlignment = Alignment.Center + ) { + // 外圆环 - 旋转动画 + Box( + modifier = Modifier + .size(160.dp) + .rotate(rotation) + .background( + Color.Transparent + ), + contentAlignment = Alignment.Center + ) { + // 虚线圆环效果 + repeat(8) { index -> + Box( + modifier = Modifier + .size(8.dp) + .background( + Color(0xFF007AFF), + RoundedCornerShape(4.dp) + ) + .offset( + x = (70 * kotlin.math.cos(index * 45.0 * kotlin.math.PI / 180)).dp, + y = (70 * kotlin.math.sin(index * 45.0 * kotlin.math.PI / 180)).dp + ) + ) + } + } + + // 中心搜索图标 + Box( + modifier = Modifier + .size(80.dp) + .background( + Color(0xFF007AFF), + RoundedCornerShape(40.dp) + ), + contentAlignment = Alignment.Center + ) { + Text( + text = "🔍", + fontSize = 32.sp, + color = Color.White + ) + } + } + + Spacer(modifier = Modifier.height(60.dp)) + + // 进度指示器 + LinearProgressIndicator( + modifier = Modifier + .fillMaxWidth() + .height(4.dp), + color = Color(0xFF007AFF), + trackColor = Color(0xFFE5E5E5) + ) + + Spacer(modifier = Modifier.height(24.dp)) + + // 提示文字 + Text( + text = "请确保戒指在充电状态并且靠近手机", + fontSize = 16.sp, + color = Color(0xFF666666), + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() + ) + + Spacer(modifier = Modifier.weight(1f)) + + // 找不到设备链接 + TextButton( + onClick = onDeviceNotFoundClick, + modifier = Modifier.padding(bottom = 40.dp) + ) { + Text( + text = "找不到设备?", + fontSize = 16.sp, + color = Color(0xFF007AFF), + textDecoration = TextDecoration.Underline + ) + } + } + } +} + +@Composable +@Preview +fun SearchingScreenPreview() { + SearchTip() +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/WearingFingerScreen.kt b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/WearingFingerScreen.kt new file mode 100644 index 0000000..3fe7040 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/WearingFingerScreen.kt @@ -0,0 +1,236 @@ +package com.whitefish.ring.ui.guide + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import org.jetbrains.compose.ui.tooling.preview.Preview + +enum class Hand { + LEFT, RIGHT +} + +enum class Finger { + THUMB, INDEX, MIDDLE, RING, PINKY +} + +data class WearingPosition( + val hand: Hand, + val finger: Finger +) + +@Composable +fun WearingFingerScreen( + onNextClick: () -> Unit = {}, + onFingerSelected: (WearingPosition) -> Unit = {} +) { + var selectedPosition by remember { mutableStateOf(null) } + + Box( + modifier = Modifier + .fillMaxSize() + .background(Color(0xFFF5F5F5)) + ) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Spacer(modifier = Modifier.height(80.dp)) + + // 标题 + Text( + text = "佩戴手指", + fontSize = 24.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF333333), + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() + ) + + Spacer(modifier = Modifier.height(16.dp)) + + // 副标题 + Text( + text = "请选择您佩戴Acti戒指的手指", + fontSize = 16.sp, + color = Color(0xFF666666), + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() + ) + + Spacer(modifier = Modifier.height(80.dp)) + + // 双手选择区域 + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceEvenly + ) { + // 左手 + HandSelector( + hand = Hand.LEFT, + selectedPosition = selectedPosition, + onFingerClick = { finger -> + val position = WearingPosition(Hand.LEFT, finger) + selectedPosition = position + onFingerSelected(position) + } + ) + + // 右手 + HandSelector( + hand = Hand.RIGHT, + selectedPosition = selectedPosition, + onFingerClick = { finger -> + val position = WearingPosition(Hand.RIGHT, finger) + selectedPosition = position + onFingerSelected(position) + } + ) + } + + Spacer(modifier = Modifier.weight(1f)) + + // 下一步按钮 + Button( + onClick = onNextClick, + enabled = selectedPosition != null, + modifier = Modifier + .fillMaxWidth() + .height(56.dp), + shape = RoundedCornerShape(28.dp), + colors = ButtonDefaults.buttonColors( + containerColor = if (selectedPosition != null) Color(0xFF007AFF) else Color(0xFFCCCCCC), + contentColor = Color.White + ) + ) { + Text( + text = "下一步", + fontSize = 18.sp, + fontWeight = FontWeight.Medium + ) + } + + Spacer(modifier = Modifier.height(40.dp)) + } + } +} + +@Composable +private fun HandSelector( + hand: Hand, + selectedPosition: WearingPosition?, + onFingerClick: (Finger) -> Unit +) { + Column( + horizontalAlignment = Alignment.CenterHorizontally + ) { + // 手部图像占位符 + Box( + modifier = Modifier + .size(140.dp, 180.dp) + .background( + Color.White, + RoundedCornerShape(20.dp) + ) + .border( + width = 2.dp, + color = Color(0xFFE5E5E5), + shape = RoundedCornerShape(20.dp) + ), + contentAlignment = Alignment.Center + ) { + // 简化的手部表示 + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + // 拇指 + FingerButton( + finger = Finger.THUMB, + isSelected = selectedPosition?.hand == hand && selectedPosition.finger == Finger.THUMB, + onClick = { onFingerClick(Finger.THUMB) }, + modifier = Modifier.offset(x = if (hand == Hand.LEFT) 20.dp else (-20).dp) + ) + + Spacer(modifier = Modifier.height(8.dp)) + + // 其他四个手指 + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + listOf(Finger.INDEX, Finger.MIDDLE, Finger.RING, Finger.PINKY).forEach { finger -> + FingerButton( + finger = finger, + isSelected = selectedPosition?.hand == hand && selectedPosition.finger == finger, + onClick = { onFingerClick(finger) } + ) + } + } + + // 显示戒指图标在选中的手指上 + if (selectedPosition?.hand == hand) { + Text( + text = "💍", + fontSize = 16.sp, + modifier = Modifier.padding(top = 4.dp) + ) + } + } + } + + Spacer(modifier = Modifier.height(16.dp)) + + // 标签 + Text( + text = if (hand == Hand.LEFT) "左手" else "右手", + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF333333) + ) + } +} + +@Composable +private fun FingerButton( + finger: Finger, + isSelected: Boolean, + onClick: () -> Unit, + modifier: Modifier = Modifier +) { + Box( + modifier = modifier + .size(20.dp, 40.dp) + .clip(RoundedCornerShape(10.dp)) + .background( + if (isSelected) Color(0xFF007AFF) else Color(0xFFE5E5E5) + ) + .clickable { onClick() }, + contentAlignment = Alignment.Center + ) { + if (isSelected) { + Text( + text = "💍", + fontSize = 12.sp + ) + } + } +} + +@Composable +@Preview +fun WearingFingerScreenPreview() { + WearingFingerScreen() +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/WelcomeScreen.kt b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/WelcomeScreen.kt new file mode 100644 index 0000000..1ed9d5d --- /dev/null +++ b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/guide/WelcomeScreen.kt @@ -0,0 +1,161 @@ +package com.whitefish.ring.ui.guide + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun WelcomeScreen( + onStartClick: () -> Unit = {} +) { + var isChecked by remember { mutableStateOf(false) } + + Box( + modifier = Modifier + .fillMaxSize() + .background(Color(0xFFF5F5F5)) + ) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Spacer(modifier = Modifier.height(120.dp)) + + // 主标题 + Text( + text = "Acti", + fontSize = 48.sp, + fontWeight = FontWeight.Bold, + color = Color(0xFF333333), + textAlign = TextAlign.Center + ) + + Spacer(modifier = Modifier.height(24.dp)) + + // 副标题 + Text( + text = "赋能每一个动作", + fontSize = 18.sp, + fontWeight = FontWeight.Normal, + color = Color(0xFF666666), + textAlign = TextAlign.Center + ) + + Spacer(modifier = Modifier.height(16.dp)) + + // 英文副标题 + Text( + text = "Empower Every Move", + fontSize = 16.sp, + fontWeight = FontWeight.Normal, + color = Color(0xFF999999), + textAlign = TextAlign.Center + ) + + Spacer(modifier = Modifier.weight(1f)) + + // 戒指图片占位符 + Box( + modifier = Modifier + .size(200.dp) + .background( + Color(0xFFE5E5E5), + RoundedCornerShape(16.dp) + ), + contentAlignment = Alignment.Center + ) { + Text( + text = "💍", + fontSize = 80.sp + ) + } + + Spacer(modifier = Modifier.weight(1f)) + + // 协议同意checkbox + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Checkbox( + checked = isChecked, + onCheckedChange = { isChecked = it }, + colors = CheckboxDefaults.colors( + checkedColor = Color(0xFF007AFF) + ) + ) + + Spacer(modifier = Modifier.width(8.dp)) + + Text( + text = "我已阅读并同意", + fontSize = 14.sp, + color = Color(0xFF666666) + ) + + Text( + text = "《用户协议》", + fontSize = 14.sp, + color = Color(0xFF007AFF) + ) + + Text( + text = "和", + fontSize = 14.sp, + color = Color(0xFF666666) + ) + + Text( + text = "《隐私政策》", + fontSize = 14.sp, + color = Color(0xFF007AFF) + ) + } + + Spacer(modifier = Modifier.height(24.dp)) + + // 立即使用按钮 + Button( + onClick = onStartClick, + enabled = isChecked, + modifier = Modifier + .fillMaxWidth() + .height(56.dp), + shape = RoundedCornerShape(28.dp), + colors = ButtonDefaults.buttonColors( + containerColor = if (isChecked) Color(0xFF007AFF) else Color(0xFFCCCCCC), + contentColor = Color.White + ) + ) { + Text( + text = "立即使用", + fontSize = 18.sp, + fontWeight = FontWeight.Medium + ) + } + + Spacer(modifier = Modifier.height(40.dp)) + } + } +} + +@Composable +@Preview +fun WelcomeScreenPreview() { + WelcomeScreen() +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/whitefish/ring/ui/home/HomeScreen.kt b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/home/HomeScreen.kt index b4bbbc1..0820a44 100644 --- a/shared/src/commonMain/kotlin/com/whitefish/ring/ui/home/HomeScreen.kt +++ b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/home/HomeScreen.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel import com.whitefish.ring.getNavigationBarHeight import com.whitefish.ring.getStatusBarHeight +import com.whitefish.ring.obtainDeviceManager import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.ui.tooling.preview.Preview import com.whitefish.ring.ui.home.state.StateScreen diff --git a/shared/src/commonMain/kotlin/com/whitefish/ring/ui/home/HomeViewModel.kt b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/home/HomeViewModel.kt index 0455c26..5880e8b 100644 --- a/shared/src/commonMain/kotlin/com/whitefish/ring/ui/home/HomeViewModel.kt +++ b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/home/HomeViewModel.kt @@ -1,9 +1,16 @@ package com.whitefish.ring.ui.home import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.whitefish.ring.device.IDeviceManager +import com.whitefish.ring.obtainDeviceManager +import io.github.aakira.napier.Napier +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch data class HomeUiState( val selectedTab: HomeTab = HomeTab.STATE, @@ -12,6 +19,30 @@ data class HomeUiState( ) class HomeViewModel : ViewModel() { + private val manager = obtainDeviceManager() + init { + collect() + } + + fun collect(){ + viewModelScope.launch { + launch { + manager.blePowerState.collectLatest { + if (it){ + Napier.i { "start scan" } + manager.startScan() + } + } + } + + launch { + manager.deviceList.collectLatest { + Napier.i { "deviceList:${it}" } + } + } + + } + } private val _uiState = MutableStateFlow(HomeUiState()) val uiState: StateFlow = _uiState.asStateFlow() diff --git a/shared/src/iosMain/kotlin/com/whitefish/ring/DeviceManager.kt b/shared/src/iosMain/kotlin/com/whitefish/ring/DeviceManager.kt index 2b3c682..9a96e0b 100644 --- a/shared/src/iosMain/kotlin/com/whitefish/ring/DeviceManager.kt +++ b/shared/src/iosMain/kotlin/com/whitefish/ring/DeviceManager.kt @@ -1,19 +1,189 @@ package com.whitefish.ring +import androidx.compose.ui.util.fastFirstOrNull +import com.whitefish.ring.bean.ui.Device import com.whitefish.ring.device.IDeviceManager +import com.whitefish.ring.objc.CMD_EXECTE_ERROR_REASON import com.whitefish.ring.objc.DeviceCenter +import com.whitefish.ring.objc.EXCUTED_CMD +import com.whitefish.ring.objc.FUNCTION_ERROR import com.whitefish.ring.objc.LTSRingSDK +import com.whitefish.ring.objc.OusideBleDiscovery +import com.whitefish.ring.objc.SRBLeService +import com.whitefish.ring.objc.SRBleDataProtocalProtocol +import com.whitefish.ring.objc.SRBleScanProtocalProtocol +import com.whitefish.ring.objc.SRDeviceInfo +import io.github.aakira.napier.Napier import kotlinx.cinterop.ExperimentalForeignApi +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.IO +import kotlinx.coroutines.launch import platform.CoreBluetooth.CBManagerState +import platform.Foundation.NSNumber +import platform.darwin.NSInteger import platform.darwin.NSObject +import platform.darwin.NSUInteger @OptIn(ExperimentalForeignApi::class) -class DeviceManager: IDeviceManager { - private val manager = DeviceCenter() - override fun startScan() { +class DeviceManager: IDeviceManager() { + private val manager = DeviceCenter.instance() + + private var iosBleList = arrayListOf() + private val scope = CoroutineScope(Dispatchers.IO) + + // 将delegate对象存储为强引用的成员变量,避免被垃圾回收 + private val scanDelegate = object : NSObject(), SRBleScanProtocalProtocol { + override fun srBleDidConnectPeripheral(service: SRBLeService) { + Napier.i { "srBleDidConnectPeripheral" } + } + + override fun srBleDidDisconnectPeripheral(service: SRBLeService) { + Napier.i { "srBleDidDisconnectPeripheral" } + } + override fun srBlePowerStateChange(state: CBManagerState) { + Napier.i { "srBlePowerStateChange:${state}" } + if (state.toInt() == 5){ + scope.launch { + blePowerState.emit(true) + } + } + } + + override fun srScanDeviceDidRefresh(perphelArray: List<*>) { + Napier.i { "srScanDeviceDidRefresh:${perphelArray}" } + iosBleList.clear() + val deviceList = perphelArray.map { + val device = it as SRBLeService + iosBleList.add(device) + Device(device.advDataLocalName.toString(),device.macAddress.toString()) + } + _deviceList.value = deviceList + } + } + + private val dataDelegate = object : NSObject(), SRBleDataProtocalProtocol { + override fun srBleDeviceDidReadyForReadAndWrite(service: SRBLeService) { + Napier.i { "srBleDeviceDidReadyForReadAndWrite" } + } + + override fun srBleRealtimeSpo(spo: NSNumber) { + Napier.i { "srBleRealtimeSpo" } + } + + override fun srBleRealtimeHeartRate(hr: NSNumber) { + } + + override fun srBleRealtimeHrv(hrv: NSNumber) { + Napier.i { "srBleRealtimeHrv" } + } + + override fun srBleDeviceBatteryLevel( + batteryLevel: NSUInteger, + IsCharging: Boolean + ) { + Napier.i { "srBleDeviceBatteryLevel" } + } + + override fun srBleSN(sn: String) { + Napier.i { "srBleSN:${sn}" } + } + + override fun srBleDeviceInfo(devInfo: SRDeviceInfo) { + Napier.i { "srBleDeviceInfo:${devInfo}" } + } + + override fun srBleHistorySr03DataWithCurrentCount( + currentCount: NSInteger, + IsComplete: Boolean + ) { + Napier.i { "srBleHistorySr03DataWithCurrentCount:${IsComplete}" } + } + + override fun srBleDeviceRealtimeSteps(steps: NSNumber) { + Napier.i { "srBleDeviceRealtimeSteps:${steps}" } + } + + override fun srBleDeviceRealtimeTemperature(temperature: NSNumber) { + Napier.i { "srBleDeviceRealtimeTemperature:${temperature}" } + } + + override fun srBleCmdExcute( + cmd: EXCUTED_CMD, + Succ: Boolean + ) { + + } + + override fun srBleCmdExcute( + cmd: EXCUTED_CMD, + Succ: Boolean, + Reason: CMD_EXECTE_ERROR_REASON + ) { + } + + override fun srBleHistoryDataCount(count: NSInteger) { + } + + override fun srBleHistoryDataProgress(percent: Float, IsComplete: Boolean) { + } + + override fun srBleHistoryDataTimeout() { + } + + override fun srBleIsbinded(isBinded: Boolean) { + } + + override fun srBleOEMAuthResult(authSucceddful: Boolean) { + bleReadyStateFlow.value = true + Napier.i { "srBleOEMAuthResult" } + } + + override fun srBleFunctionErrorCallBack( + error: FUNCTION_ERROR, + MehthodName: String + ) { + } + } + + init { + initializeManager() + } + + private fun initializeManager() { + Napier.i { "DeviceManager initializing..." } + manager.registWithisCustomBleManage(true) + // 使用成员变量而不是匿名对象 + manager.appScanDelegate = scanDelegate + manager.appDataDelegate = dataDelegate + Napier.i { "DeviceManager delegates set: scan=${scanDelegate}, data=${dataDelegate}" } + } + + // 添加重新初始化方法,在需要时可以调用 + fun reinitialize() { + Napier.i { "DeviceManager reinitializing..." } + initializeManager() + } + + override fun startScan() { + Napier.i { "Starting scan, delegate: ${manager.appScanDelegate}" } + manager.startBleScan() } override fun stopScan() { + manager.stopBleScan() + } + + override fun connect(mac: String) { + iosBleList.fastFirstOrNull { it.macAddress == mac }?.let { + manager.connectDevice(it) + Napier.i { "connect device:${it}" } + } + } + + override fun bind() { + Napier.i { "bind device:${manager.currentDevice()}" } + manager.bindCurrentDevice() } } \ No newline at end of file diff --git a/shared/src/iosMain/kotlin/com/whitefish/ring/MainViewController.kt b/shared/src/iosMain/kotlin/com/whitefish/ring/MainViewController.kt index a782d55..a746f84 100644 --- a/shared/src/iosMain/kotlin/com/whitefish/ring/MainViewController.kt +++ b/shared/src/iosMain/kotlin/com/whitefish/ring/MainViewController.kt @@ -4,6 +4,4 @@ import androidx.compose.ui.window.ComposeUIViewController import io.github.aakira.napier.DebugAntilog import io.github.aakira.napier.Napier -fun MainViewController() = ComposeUIViewController { App() }.apply { - Napier.base(DebugAntilog()) -} \ No newline at end of file +fun MainViewController() = ComposeUIViewController { App() } \ No newline at end of file diff --git a/shared/src/iosMain/kotlin/com/whitefish/ring/Platform.ios.kt b/shared/src/iosMain/kotlin/com/whitefish/ring/Platform.ios.kt index 253b651..56ed02b 100644 --- a/shared/src/iosMain/kotlin/com/whitefish/ring/Platform.ios.kt +++ b/shared/src/iosMain/kotlin/com/whitefish/ring/Platform.ios.kt @@ -63,4 +63,5 @@ actual fun obtainDeviceManager(): IDeviceManager { fun initLogger(){ Napier.base(DebugAntilog()) + Napier.i { "Logger init success on ios" } } \ No newline at end of file diff --git a/shared/src/main/res/values/string.xml b/shared/src/main/res/values/string.xml new file mode 100644 index 0000000..c1daa14 --- /dev/null +++ b/shared/src/main/res/values/string.xml @@ -0,0 +1,225 @@ + + RingApp + + Acti + 赋能每一个动作\nEmpower Every Move + 立即使用 + 我已阅读井同意《用户隐私协议》和《用户注册协议》 + Hi,\n欢迎来到Acti + 手机号 + 请输入您的手机号 + 验证码 + 请输入验证码 + 未注册的手机号验证后自动注册登录 + 登录 + 连接您的Acti戒指 + 将您的戒指连接到充电器,并继续下一步。请确保您的手机已启用蓝牙功能。 + 下一步 + 正在搜索设备... + 附近设备 + 找不到设备? + 连接失败? + + First Fragment + Second Fragment + Next + Previous + %d 次/分 + 无数据 + + 为了可以扫描蓝牙设备,现在还需要开启位置服务。 + 您未连接戒指 + 提示 + + yyyy年MM月dd日 + yyyy年MM月 + MM月dd日 + yyyy年MM月dd日 + 今天 + 昨天 + + + 静息心率 + 步数 + 心率沉浸 + 呼吸速率 + 血氧饱和度 + 睡眠%s %s效率 + 苏醒/被打扰时间 %s + REM睡眠 %s + 浅度睡眠 %s + 深度睡眠 %s + 零星小睡 %s + %d小时%d分钟 + %d小时 + %d分钟 + 睡眠分析 + 睡眠分析%d + + 睡眠%s %s效率 + 睡眠%s + 睡眠详情 + 睡眠 + 零星小睡 + + 可用设备 + 注意:未绑定的Ring在充电时才会广播,请充电,以便APP可以扫描到。 + 连接中… + 请等待设备连接断开 + 受限模式 + 已启用受限模式,只能恢复出厂设置和自检。 + + + + 其他操作 + 手指温度 + 工厂测试 + 重启 + 恢复出厂设置 + 解綁 + 关机 + 设备信息 + 设备SN + 获取PPG读数(血氧、心率) + 仅心率 + 设置PPG参数 + SpO₂测量间隔 (0, 5~360, 分钟) + SpO₂测量间隔 + 心电图 + 设置心率&体温测量时长 + 测量时长(10 ~ 180,秒) + 提交 + 请输入测量时长! + + + 戒指断开连接,设置参数无法发送。 + + + + 选择 + 查询 + 通过文件管理器APP从本地选择固件文件 + 从服务器查询并下载新的固件到本地 + 您的设备暂未支持从服务器查询新的固件,缺少关键参数【%s】 + 升级 + 在您的设备上未找到文件浏览器应用程序。你想下载一个吗? + 该戒指为无线充电模式,请选择SR09W固件更新。 + 该戒指为NFC充电模式,请选择SR09N固件更新。 + 您的设备固件已经是最新版本:v%s. + 查询到最新的固件版本:v%s,下载中… + \nMD5匹配,正在保存文件到本地… + \nMD5不匹配!请检查。 + \n文件存储路径:%s + \n文件存储出错:%s + 查询到空内容! + 请求失败,code = %d。 + 选择此固件 + + 尺寸%d + 深黑色 + 银色 + 金色 + 玫瑰金 + 金/银混色 + 紫/银混色 + 玫瑰金/银混色 + 充电中 + 放电中 + + OEM验证失败 + 设备序列号空,连接认证失败。 + R1解密并生成R2失败。 + 连接失败,请使用凌拓NexRing智能戒指。 + 断开连接 + + 您的戒指型号不支持【锻炼模式】! + 锻炼 + 锻炼记录 + 锻炼时间 + 时间间隔 + 平均心率 + 最高心率 + 最低心率 + 同步数据中… + 没有可用的心率数据! + 选择一项锻炼 + 步行 + 室内跑步 + 室外跑步 + 室内骑车 + 室外骑车 + 山地自行车 + 游泳 + 添加详情 + 开始 + 已结束 + 进行中 + 提早结束 + 是否提早结束锻炼? + 戒指充电中,请佩戴后操作 + %d 秒 + %d 分钟 + %d 小时 + + 命令执行成功。 + 命令执行失败。 + >命令执行失败。戒指连接时OEM验证未通过。 + >命令执行失败。戒指正在主动测量。 + >命令执行失败。戒指处于锻炼模式。 + >命令执行失败。戒指正在执行APP发起的测量。 + >命令执行失败。参数错误。 + + 使用内置算法 + 输出原始波形 + 采样率 + + 请输入有效值 + + + + 您的戒指型号不支持【心电图测量】! + 开始 + 停止 + 心电图设置 + 时间基准 + 时间基准:%1$s + 增益 + 增益:%1$s + 生成PDF文件 + %d 秒 + 设备端参数设置 + "'记录时间:'yyyy年MM月dd日 HH:mm" + %s, %s,导联I,512赫,Linktop NexRing + "yyyy.MM.dd HH:mm" + "'%s' yyyy-MM-dd HH_mm'.pdf'" + 正在生成PDF文件… + 请先测量一次心电图。 + 心电图PGA增益(单位:V/V) + 您的戒指现在佩戴于 + 左手指 + 右手指 + 将另一只手的手指搭在戒指上,以便形成导联并完成心电测量。 + 心率 %s BPM + 平均心率 %s BPM + 窦性心律 + 房颤 + 低心率 + 高心率 + 不确定 + 记录结果不佳 + 无结果 + + 需要权限 + 需要蓝牙权限来扫描和连接设备。请在设置中授予权限。 + 没有蓝牙权限,应用可能无法正常工作。您可以在系统设置中手动开启权限。 + + OEM 认证失败 + 设备序列号空,OEM 认证失败。 + R1 解密并生成 R2 失败。 + + 您的戒指已开启 OEM 认证,但本 APP 设定的 OEM 字符串似乎与您的戒指所写入的不匹配, + 请检查 Demo 工程的 `res/values/arrays.xml` 的 `oem_array` 中的字符串元素是否覆盖正确? + 修改后请重新编译工程生成新的 APK 文件,安装后重试。 + 如果仍然失败,请联系我们的技术支持。 + + \ No newline at end of file