Browse Source

接入整合云教练

Steven 5 months ago
parent
commit
bacd69d64c
40 changed files with 1424 additions and 2810 deletions
  1. 62 18
      KulexiuForStudent/KulexiuForStudent.xcodeproj/project.pbxproj
  2. 6 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/Contents.json
  3. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/cloud_alert_bubble.imageset/Contents.json
  4. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/cloud_alert_bubble.imageset/cloud_alert_bubble@2x.png
  5. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/cloud_alert_bubble.imageset/cloud_alert_bubble@3x.png
  6. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/cloud_alert_icon.imageset/Contents.json
  7. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/cloud_alert_icon.imageset/cloud_alert_icon@2x.png
  8. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/cloud_alert_icon.imageset/cloud_alert_icon@3x.png
  9. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/cloud_premissionCancel.imageset/Contents.json
  10. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/cloud_premissionCancel.imageset/cloud_premissionCancel@2x.png
  11. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/cloud_premissionCancel.imageset/cloud_premissionCancel@3x.png
  12. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/cloud_premissionSetting.imageset/Contents.json
  13. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/cloud_premissionSetting.imageset/cloud_premissionSetting@2x.png
  14. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/cloud_premissionSetting.imageset/cloud_premissionSetting@3x.png
  15. 0 55
      KulexiuForStudent/KulexiuForStudent/Common/Base/AccompanyWebView/AudioEnginePlayer.h
  16. 0 377
      KulexiuForStudent/KulexiuForStudent/Common/Base/AccompanyWebView/AudioEnginePlayer.m
  17. 0 16
      KulexiuForStudent/KulexiuForStudent/Common/Base/AccompanyWebView/KSAccompanyWebViewController.h
  18. 0 1854
      KulexiuForStudent/KulexiuForStudent/Common/Base/AccompanyWebView/KSAccompanyWebViewController.m
  19. 216 81
      KulexiuForStudent/KulexiuForStudent/Common/Base/AccompanyWebView/KSCloudWebManager.m
  20. 0 28
      KulexiuForStudent/KulexiuForStudent/Common/Base/AccompanyWebView/KSTargetWebSocketManager.h
  21. 0 285
      KulexiuForStudent/KulexiuForStudent/Common/Base/AccompanyWebView/KSTargetWebSocketManager.m
  22. 0 0
      KulexiuForStudent/KulexiuForStudent/Common/Base/DelayCheck/AnimationSource/delayCheck_ meteor.json
  23. 0 0
      KulexiuForStudent/KulexiuForStudent/Common/Base/DelayCheck/AnimationSource/delayCheck_musicAni.json
  24. 16 0
      KulexiuForStudent/KulexiuForStudent/Common/Base/DelayCheck/DelayCheckTipsView.h
  25. 20 0
      KulexiuForStudent/KulexiuForStudent/Common/Base/DelayCheck/DelayCheckTipsView.m
  26. 42 0
      KulexiuForStudent/KulexiuForStudent/Common/Base/DelayCheck/DelayCheckTipsView.xib
  27. 46 0
      KulexiuForStudent/KulexiuForStudent/Common/Base/DelayCheck/KSDelayCheckView.h
  28. 364 0
      KulexiuForStudent/KulexiuForStudent/Common/Base/DelayCheck/KSDelayCheckView.m
  29. 238 0
      KulexiuForStudent/KulexiuForStudent/Common/Base/DelayCheck/KSDelayCheckView.xib
  30. 24 0
      KulexiuForStudent/KulexiuForStudent/Common/Base/KSCloudPremissionAlert/KSCloudPremissionAlertView.h
  31. 76 0
      KulexiuForStudent/KulexiuForStudent/Common/Base/KSCloudPremissionAlert/KSCloudPremissionAlertView.m
  32. 102 0
      KulexiuForStudent/KulexiuForStudent/Common/Base/KSCloudPremissionAlert/KSCloudPremissionAlertView.xib
  33. 4 0
      KulexiuForStudent/KulexiuForStudent/Common/Base/WebView/KSBaseWKWebViewController.h
  34. 60 40
      KulexiuForStudent/KulexiuForStudent/Common/Base/WebView/KSBaseWKWebViewController.m
  35. 4 1
      KulexiuForStudent/KulexiuForStudent/Common/Define/KSDomain.h
  36. 7 1
      KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSAccompanyDraftViewController.h
  37. 15 4
      KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSAccompanyDraftViewController.m
  38. 14 1
      KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSMediaMergeView.h
  39. 9 48
      KulexiuForStudent/KulexiuForStudent/Module/Chat/Controller/TXCustom/KSTXBaseChatViewController.m
  40. 11 1
      KulexiuForStudent/KulexiuForStudent/Module/Mine/Works/Controller/KSDraftMergeViewController.m

+ 62 - 18
KulexiuForStudent/KulexiuForStudent.xcodeproj/project.pbxproj

@@ -88,7 +88,6 @@
 		275FA1DA27E7351900CFEA2E /* KSNetworkingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 275FA1AF27E7351400CFEA2E /* KSNetworkingManager.m */; };
 		275FA1DE27E7351900CFEA2E /* KSLocalWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 275FA1B927E7351500CFEA2E /* KSLocalWebViewController.m */; };
 		275FA1E127E7351900CFEA2E /* KSTabBarViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 275FA1BD27E7351500CFEA2E /* KSTabBarViewController.m */; };
-		275FA1E527E7351900CFEA2E /* KSAccompanyWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 275FA1C427E7351600CFEA2E /* KSAccompanyWebViewController.m */; };
 		275FA1E627E7351900CFEA2E /* KSUpdateAlert.m in Sources */ = {isa = PBXBuildFile; fileRef = 275FA1C627E7351700CFEA2E /* KSUpdateAlert.m */; };
 		275FA1E827E7351900CFEA2E /* CustomNavViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 275FA1C827E7351700CFEA2E /* CustomNavViewController.m */; };
 		275FA1E927E7351900CFEA2E /* KSNetTypeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 275FA1C927E7351700CFEA2E /* KSNetTypeManager.m */; };
@@ -554,6 +553,14 @@
 		BC71D2892888083B0010F14B /* tabbar5.json in Resources */ = {isa = PBXBuildFile; fileRef = BC71D2842888083B0010F14B /* tabbar5.json */; };
 		BC736A92288036E8004A9B0A /* MyVideoSearchView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC736A90288036E8004A9B0A /* MyVideoSearchView.m */; };
 		BC736A93288036E8004A9B0A /* MyVideoSearchView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC736A91288036E8004A9B0A /* MyVideoSearchView.xib */; };
+		BC7400E82CD1CB3D0056756A /* KSCloudPremissionAlertView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC7400E52CD1CB3D0056756A /* KSCloudPremissionAlertView.m */; };
+		BC7400E92CD1CB3D0056756A /* KSCloudPremissionAlertView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC7400E62CD1CB3D0056756A /* KSCloudPremissionAlertView.xib */; };
+		BC7400F42CD1CB450056756A /* DelayCheckTipsView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC7400EE2CD1CB450056756A /* DelayCheckTipsView.m */; };
+		BC7400F52CD1CB450056756A /* KSDelayCheckView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC7400F12CD1CB450056756A /* KSDelayCheckView.m */; };
+		BC7400F62CD1CB450056756A /* delayCheck_musicAni.json in Resources */ = {isa = PBXBuildFile; fileRef = BC7400EB2CD1CB450056756A /* delayCheck_musicAni.json */; };
+		BC7400F72CD1CB450056756A /* DelayCheckTipsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC7400EF2CD1CB450056756A /* DelayCheckTipsView.xib */; };
+		BC7400F82CD1CB450056756A /* KSDelayCheckView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC7400F22CD1CB450056756A /* KSDelayCheckView.xib */; };
+		BC7400F92CD1CB450056756A /* delayCheck_ meteor.json in Resources */ = {isa = PBXBuildFile; fileRef = BC7400EA2CD1CB450056756A /* delayCheck_ meteor.json */; };
 		BC756CB628FE4E5D00AA9ECB /* KSChatTagView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC756CB528FE4E5D00AA9ECB /* KSChatTagView.m */; };
 		BC756CB928FE7D1D00AA9ECB /* KSChatUserDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BC756CB828FE7D1D00AA9ECB /* KSChatUserDetailViewController.m */; };
 		BC756CBC28FE7D5600AA9ECB /* UserDetailBodyView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC756CBB28FE7D5600AA9ECB /* UserDetailBodyView.m */; };
@@ -567,7 +574,6 @@
 		BC7663162827E49900C91A1D /* NotiferHeadView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC7663102827E49800C91A1D /* NotiferHeadView.xib */; };
 		BC7663172827E49900C91A1D /* NotiferMessageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = BC7663122827E49800C91A1D /* NotiferMessageCell.m */; };
 		BC7663182827E49900C91A1D /* NotiferMessageCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC7663132827E49900C91A1D /* NotiferMessageCell.xib */; };
-		BC7DECA02C2D571A00154524 /* AudioEnginePlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = BC7DEC9F2C2D571A00154524 /* AudioEnginePlayer.m */; };
 		BC7F7B762C92DD9500265AE1 /* KSDeleteAccountTipsAlert.m in Sources */ = {isa = PBXBuildFile; fileRef = BC7F7B742C92DD9500265AE1 /* KSDeleteAccountTipsAlert.m */; };
 		BC7F7B772C92DD9500265AE1 /* KSDeleteAccountTipsAlert.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC7F7B752C92DD9500265AE1 /* KSDeleteAccountTipsAlert.xib */; };
 		BC7FF6A12BEB71610092E0DE /* client.p12 in Resources */ = {isa = PBXBuildFile; fileRef = BC3BF62F2B9EAFC800831494 /* client.p12 */; };
@@ -602,7 +608,6 @@
 		BC8418412AC2D96C00D8F90E /* PasswordCheckBodyView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC8418402AC2D96C00D8F90E /* PasswordCheckBodyView.xib */; };
 		BC85A9E62C6B570E003C1ABE /* KSRealtimeAnalyzer.m in Sources */ = {isa = PBXBuildFile; fileRef = BC85A9E22C6B570E003C1ABE /* KSRealtimeAnalyzer.m */; };
 		BC85A9E72C6B570E003C1ABE /* KSSpectrumView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC85A9E42C6B570E003C1ABE /* KSSpectrumView.m */; };
-		BC85A9EA2C6B5959003C1ABE /* KSTargetWebSocketManager.m in Sources */ = {isa = PBXBuildFile; fileRef = BC85A9E92C6B5959003C1ABE /* KSTargetWebSocketManager.m */; };
 		BC89AC092AB0840E00B077AF /* TenantRecommendMusicView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC89AC082AB0840E00B077AF /* TenantRecommendMusicView.m */; };
 		BC89AC0B2AB0841400B077AF /* TenantRecommendMusicView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC89AC0A2AB0841400B077AF /* TenantRecommendMusicView.xib */; };
 		BC89AC0E2AB0842B00B077AF /* TenantNewMusicView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC89AC0D2AB0842B00B077AF /* TenantNewMusicView.m */; };
@@ -1115,8 +1120,6 @@
 		275FA1BD27E7351500CFEA2E /* KSTabBarViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSTabBarViewController.m; sourceTree = "<group>"; };
 		275FA1BE27E7351600CFEA2E /* BaseViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BaseViewController.h; sourceTree = "<group>"; };
 		275FA1C027E7351600CFEA2E /* KSLocalWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KSLocalWebViewController.h; sourceTree = "<group>"; };
-		275FA1C327E7351600CFEA2E /* KSAccompanyWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KSAccompanyWebViewController.h; sourceTree = "<group>"; };
-		275FA1C427E7351600CFEA2E /* KSAccompanyWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSAccompanyWebViewController.m; sourceTree = "<group>"; };
 		275FA1C527E7351600CFEA2E /* KSBaseViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KSBaseViewController.h; sourceTree = "<group>"; };
 		275FA1C627E7351700CFEA2E /* KSUpdateAlert.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSUpdateAlert.m; sourceTree = "<group>"; };
 		275FA1C827E7351700CFEA2E /* CustomNavViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CustomNavViewController.m; sourceTree = "<group>"; };
@@ -1888,6 +1891,17 @@
 		BC736A8F288036E8004A9B0A /* MyVideoSearchView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MyVideoSearchView.h; sourceTree = "<group>"; };
 		BC736A90288036E8004A9B0A /* MyVideoSearchView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MyVideoSearchView.m; sourceTree = "<group>"; };
 		BC736A91288036E8004A9B0A /* MyVideoSearchView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MyVideoSearchView.xib; sourceTree = "<group>"; };
+		BC7400E42CD1CB3D0056756A /* KSCloudPremissionAlertView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KSCloudPremissionAlertView.h; sourceTree = "<group>"; };
+		BC7400E52CD1CB3D0056756A /* KSCloudPremissionAlertView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KSCloudPremissionAlertView.m; sourceTree = "<group>"; };
+		BC7400E62CD1CB3D0056756A /* KSCloudPremissionAlertView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = KSCloudPremissionAlertView.xib; sourceTree = "<group>"; };
+		BC7400EA2CD1CB450056756A /* delayCheck_ meteor.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "delayCheck_ meteor.json"; sourceTree = "<group>"; };
+		BC7400EB2CD1CB450056756A /* delayCheck_musicAni.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = delayCheck_musicAni.json; sourceTree = "<group>"; };
+		BC7400ED2CD1CB450056756A /* DelayCheckTipsView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DelayCheckTipsView.h; sourceTree = "<group>"; };
+		BC7400EE2CD1CB450056756A /* DelayCheckTipsView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DelayCheckTipsView.m; sourceTree = "<group>"; };
+		BC7400EF2CD1CB450056756A /* DelayCheckTipsView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DelayCheckTipsView.xib; sourceTree = "<group>"; };
+		BC7400F02CD1CB450056756A /* KSDelayCheckView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KSDelayCheckView.h; sourceTree = "<group>"; };
+		BC7400F12CD1CB450056756A /* KSDelayCheckView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KSDelayCheckView.m; sourceTree = "<group>"; };
+		BC7400F22CD1CB450056756A /* KSDelayCheckView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = KSDelayCheckView.xib; sourceTree = "<group>"; };
 		BC756CB428FE4E5D00AA9ECB /* KSChatTagView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KSChatTagView.h; sourceTree = "<group>"; };
 		BC756CB528FE4E5D00AA9ECB /* KSChatTagView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KSChatTagView.m; sourceTree = "<group>"; };
 		BC756CB728FE7D1D00AA9ECB /* KSChatUserDetailViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KSChatUserDetailViewController.h; sourceTree = "<group>"; };
@@ -1909,8 +1923,6 @@
 		BC7663122827E49800C91A1D /* NotiferMessageCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NotiferMessageCell.m; sourceTree = "<group>"; };
 		BC7663132827E49900C91A1D /* NotiferMessageCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NotiferMessageCell.xib; sourceTree = "<group>"; };
 		BC7663142827E49900C91A1D /* NotiferHeadView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NotiferHeadView.h; sourceTree = "<group>"; };
-		BC7DEC9E2C2D571A00154524 /* AudioEnginePlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioEnginePlayer.h; sourceTree = "<group>"; };
-		BC7DEC9F2C2D571A00154524 /* AudioEnginePlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AudioEnginePlayer.m; sourceTree = "<group>"; };
 		BC7F7B732C92DD9500265AE1 /* KSDeleteAccountTipsAlert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KSDeleteAccountTipsAlert.h; sourceTree = "<group>"; };
 		BC7F7B742C92DD9500265AE1 /* KSDeleteAccountTipsAlert.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSDeleteAccountTipsAlert.m; sourceTree = "<group>"; };
 		BC7F7B752C92DD9500265AE1 /* KSDeleteAccountTipsAlert.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = KSDeleteAccountTipsAlert.xib; sourceTree = "<group>"; };
@@ -1963,8 +1975,6 @@
 		BC85A9E22C6B570E003C1ABE /* KSRealtimeAnalyzer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSRealtimeAnalyzer.m; sourceTree = "<group>"; };
 		BC85A9E32C6B570E003C1ABE /* KSSpectrumView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KSSpectrumView.h; sourceTree = "<group>"; };
 		BC85A9E42C6B570E003C1ABE /* KSSpectrumView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSSpectrumView.m; sourceTree = "<group>"; };
-		BC85A9E82C6B5959003C1ABE /* KSTargetWebSocketManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KSTargetWebSocketManager.h; sourceTree = "<group>"; };
-		BC85A9E92C6B5959003C1ABE /* KSTargetWebSocketManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KSTargetWebSocketManager.m; sourceTree = "<group>"; };
 		BC89AC072AB0840E00B077AF /* TenantRecommendMusicView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TenantRecommendMusicView.h; sourceTree = "<group>"; };
 		BC89AC082AB0840E00B077AF /* TenantRecommendMusicView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TenantRecommendMusicView.m; sourceTree = "<group>"; };
 		BC89AC0A2AB0841400B077AF /* TenantRecommendMusicView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TenantRecommendMusicView.xib; sourceTree = "<group>"; };
@@ -3431,6 +3441,8 @@
 		2779350B27E324A40010E277 /* Base */ = {
 			isa = PBXGroup;
 			children = (
+				BC7400F32CD1CB450056756A /* DelayCheck */,
+				BC7400E72CD1CB3D0056756A /* KSCloudPremissionAlert */,
 				BC29AD322BFAFF3200D44848 /* LogManager */,
 				BC3300172BBAB623003D4921 /* CoursewareDownload */,
 				BC3A55752BAA8479002E1616 /* AlertView */,
@@ -4547,17 +4559,11 @@
 		BC3A55712BAA83BB002E1616 /* AccompanyWebView */ = {
 			isa = PBXGroup;
 			children = (
-				BC7DEC9E2C2D571A00154524 /* AudioEnginePlayer.h */,
-				BC7DEC9F2C2D571A00154524 /* AudioEnginePlayer.m */,
-				275FA1C327E7351600CFEA2E /* KSAccompanyWebViewController.h */,
-				275FA1C427E7351600CFEA2E /* KSAccompanyWebViewController.m */,
 				BCC5838E28A9E8A800BAB4CF /* AccompanyLoadingView.h */,
 				BCC5838F28A9E8A800BAB4CF /* AccompanyLoadingView.m */,
 				BCC5839128A9E8AF00BAB4CF /* AccompanyLoadingView.xib */,
 				BC3A55812BAA8F34002E1616 /* KSCloudWebManager.h */,
 				BC3A55802BAA8F34002E1616 /* KSCloudWebManager.m */,
-				BC85A9E82C6B5959003C1ABE /* KSTargetWebSocketManager.h */,
-				BC85A9E92C6B5959003C1ABE /* KSTargetWebSocketManager.m */,
 			);
 			path = AccompanyWebView;
 			sourceTree = "<group>";
@@ -5045,6 +5051,39 @@
 			path = TabbarAnimation;
 			sourceTree = "<group>";
 		};
+		BC7400E72CD1CB3D0056756A /* KSCloudPremissionAlert */ = {
+			isa = PBXGroup;
+			children = (
+				BC7400E42CD1CB3D0056756A /* KSCloudPremissionAlertView.h */,
+				BC7400E52CD1CB3D0056756A /* KSCloudPremissionAlertView.m */,
+				BC7400E62CD1CB3D0056756A /* KSCloudPremissionAlertView.xib */,
+			);
+			path = KSCloudPremissionAlert;
+			sourceTree = "<group>";
+		};
+		BC7400EC2CD1CB450056756A /* AnimationSource */ = {
+			isa = PBXGroup;
+			children = (
+				BC7400EA2CD1CB450056756A /* delayCheck_ meteor.json */,
+				BC7400EB2CD1CB450056756A /* delayCheck_musicAni.json */,
+			);
+			path = AnimationSource;
+			sourceTree = "<group>";
+		};
+		BC7400F32CD1CB450056756A /* DelayCheck */ = {
+			isa = PBXGroup;
+			children = (
+				BC7400EC2CD1CB450056756A /* AnimationSource */,
+				BC7400ED2CD1CB450056756A /* DelayCheckTipsView.h */,
+				BC7400EE2CD1CB450056756A /* DelayCheckTipsView.m */,
+				BC7400EF2CD1CB450056756A /* DelayCheckTipsView.xib */,
+				BC7400F02CD1CB450056756A /* KSDelayCheckView.h */,
+				BC7400F12CD1CB450056756A /* KSDelayCheckView.m */,
+				BC7400F22CD1CB450056756A /* KSDelayCheckView.xib */,
+			);
+			path = DelayCheck;
+			sourceTree = "<group>";
+		};
 		BC802D9F28BC5F650079E350 /* HotMusic */ = {
 			isa = PBXGroup;
 			children = (
@@ -6437,6 +6476,10 @@
 				BCFCE44E28DD5C000051FED8 /* Low.wav in Resources */,
 				BC802D9428B897670079E350 /* LiveApplyingView.xib in Resources */,
 				BC71D244288804CD0010F14B /* img_10.png in Resources */,
+				BC7400F62CD1CB450056756A /* delayCheck_musicAni.json in Resources */,
+				BC7400F72CD1CB450056756A /* DelayCheckTipsView.xib in Resources */,
+				BC7400F82CD1CB450056756A /* KSDelayCheckView.xib in Resources */,
+				BC7400F92CD1CB450056756A /* delayCheck_ meteor.json in Resources */,
 				BC0212F827FC4A080040569F /* SubjectImageCell.xib in Resources */,
 				BCC583B728A9EC6400BAB4CF /* cloud_animation_21.png in Resources */,
 				BC11921B280ED6A900A716F7 /* NewClassPopCell.xib in Resources */,
@@ -6523,6 +6566,7 @@
 				BC802D9E28BC546B0079E350 /* HomeHotMusicCollectionCell.xib in Resources */,
 				BCCC36212AAF0EDF000D60CA /* SettingBottomView.xib in Resources */,
 				275E8AB327E18F8800DD3F6E /* Main.storyboard in Resources */,
+				BC7400E92CD1CB3D0056756A /* KSCloudPremissionAlertView.xib in Resources */,
 				BC71D258288804CD0010F14B /* img_49.png in Resources */,
 				BC11922C280ED8E800A716F7 /* LTSCalendarBottomView.xib in Resources */,
 			);
@@ -6794,7 +6838,6 @@
 				BC50171527FC0D8300F8BCBC /* SubjectChooseBodyView.m in Sources */,
 				BC8C2C5C2823F57100FBA5D5 /* KSAddressPickerView.m in Sources */,
 				2723B62727F157D500E0B90B /* GroupMemberViewController.m in Sources */,
-				BC85A9EA2C6B5959003C1ABE /* KSTargetWebSocketManager.m in Sources */,
 				BC38C3EE2AF893B400ABFCC2 /* KSPlayerSliderView.m in Sources */,
 				BC29AD332BFAFF3200D44848 /* KSLogManager.m in Sources */,
 				BC119216280ED6A900A716F7 /* MyLiveCourseCell.m in Sources */,
@@ -6866,7 +6909,6 @@
 				277935B427E324A90010E277 /* FSCalendarDelegationProxy.m in Sources */,
 				BCC0F6CA2A8CDDFE00C4EFA4 /* ClassroomService.m in Sources */,
 				BCC0F6C52A8CDDEB00C4EFA4 /* Classroom.m in Sources */,
-				BC7DECA02C2D571A00154524 /* AudioEnginePlayer.m in Sources */,
 				BC106C502A9351DA000759A9 /* LiveModuleService.m in Sources */,
 				2723B5BB27F157B100E0B90B /* ChatAddressBodyView.m in Sources */,
 				BC71D27F288807680010F14B /* AnimationHelper.m in Sources */,
@@ -6979,6 +7021,8 @@
 				BCFDA61228BC8FCE0022B497 /* HomeHotVideoCell.m in Sources */,
 				BCECE24D2B3D670500C0D555 /* FeedbackListNavView.m in Sources */,
 				BC106C182A933829000759A9 /* TXLiveMessageShopRush.m in Sources */,
+				BC7400F42CD1CB450056756A /* DelayCheckTipsView.m in Sources */,
+				BC7400F52CD1CB450056756A /* KSDelayCheckView.m in Sources */,
 				BC756CB928FE7D1D00AA9ECB /* KSChatUserDetailViewController.m in Sources */,
 				BC43CACA2A88C9570011EB5D /* KSTXC2CChatViewController.m in Sources */,
 				BC119267280FA92700A716F7 /* HomeworkDetailViewController.m in Sources */,
@@ -7062,6 +7106,7 @@
 				BCDE358E289A7D8700A9A560 /* KSGroupTagImageView.m in Sources */,
 				2779352D27E324A60010E277 /* NSString+CZHSizeExtension.m in Sources */,
 				BCC0F6612A8CD8F500C4EFA4 /* TXMainEmtpyView.m in Sources */,
+				BC7400E82CD1CB3D0056756A /* KSCloudPremissionAlertView.m in Sources */,
 				BC8C2C7E28265D8E00FBA5D5 /* KSNewsAlert.m in Sources */,
 				BCC0F6642A8CD8F500C4EFA4 /* TXDanBottomView.m in Sources */,
 				BC119259280FA85300A716F7 /* HomeworkListCell.m in Sources */,
@@ -7164,7 +7209,6 @@
 				275FA23227E7356B00CFEA2E /* LoginViewController.m in Sources */,
 				2723B5C127F157B100E0B90B /* ContractListCell.m in Sources */,
 				BC802D9728BC4FC40079E350 /* HomeHotMusicView.m in Sources */,
-				275FA1E527E7351900CFEA2E /* KSAccompanyWebViewController.m in Sources */,
 				BC756CB628FE4E5D00AA9ECB /* KSChatTagView.m in Sources */,
 				BC11923A280ED98E00A716F7 /* AccompanyCourseCell.m in Sources */,
 				BCFDA62F28BC99410022B497 /* HomeBannerView.m in Sources */,

+ 6 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/Contents.json

@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/cloud_alert_bubble.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "cloud_alert_bubble@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "cloud_alert_bubble@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/cloud_alert_bubble.imageset/cloud_alert_bubble@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/cloud_alert_bubble.imageset/cloud_alert_bubble@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/cloud_alert_icon.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "cloud_alert_icon@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "cloud_alert_icon@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/cloud_alert_icon.imageset/cloud_alert_icon@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/cloud_alert_icon.imageset/cloud_alert_icon@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/cloud_premissionCancel.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "cloud_premissionCancel@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "cloud_premissionCancel@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/cloud_premissionCancel.imageset/cloud_premissionCancel@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/cloud_premissionCancel.imageset/cloud_premissionCancel@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/cloud_premissionSetting.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "cloud_premissionSetting@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "cloud_premissionSetting@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/cloud_premissionSetting.imageset/cloud_premissionSetting@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/CloudAlert/cloud_premissionSetting.imageset/cloud_premissionSetting@3x.png


+ 0 - 55
KulexiuForStudent/KulexiuForStudent/Common/Base/AccompanyWebView/AudioEnginePlayer.h

@@ -1,55 +0,0 @@
-//
-//  AudioEnginePlayer.h
-//  KulexiuForStudent
-//
-//  Created by 王智 on 2024/6/4.
-//
-
-#import <Foundation/Foundation.h>
-
-@class AudioEnginePlayer;
-
-@protocol AudioEnginePlayerDelegate <NSObject>
-
-- (void)updatePlayProgress:(NSInteger)playTime andTotalTime:(NSInteger)totalTime andProgress:(CGFloat)progress currentInterval:(NSTimeInterval)currentInterval inPlayer:(AudioEnginePlayer *_Nonnull)player;
-
-@optional
-
-- (void)enginePlayFinished:(AudioEnginePlayer *_Nonnull)player;
-
-- (void)enginePlayerIsReadyPlay:(AudioEnginePlayer *_Nonnull)player;
-
-- (void)enginePlayerDidError:(AudioEnginePlayer *_Nonnull)player error:(NSError *_Nullable)error;
-
-@end
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface AudioEnginePlayer : NSObject
-
-@property (nonatomic, weak) id <AudioEnginePlayerDelegate>delegate;
-
-@property (nonatomic, assign) float rate; // 播放速度
-
-@property (nonatomic, assign) BOOL isReady;
-
-@property (nonatomic, assign) BOOL isPlaying;
-
-@property (nonatomic, assign) BOOL isMute; // 是否禁音
-
-- (void)prepareNativeSongWithUrl:(NSURL *)nativeMusicUrl;
-
-- (void)startPlay;
-
-- (void)stopPlay;
-
-- (void)freePlayer;
-
-// 从某个位置开始播放 ms
-- (void)seekToTimePlay:(NSInteger)time;
-
-- (NSTimeInterval)getCurrentPlayTime;
-
-@end
-
-NS_ASSUME_NONNULL_END

+ 0 - 377
KulexiuForStudent/KulexiuForStudent/Common/Base/AccompanyWebView/AudioEnginePlayer.m

@@ -1,377 +0,0 @@
-//
-//  AudioEnginePlayer.m
-//  KulexiuForStudent
-//
-//  Created by 王智 on 2024/6/4.
-//
-
-#import "AudioEnginePlayer.h"
-#import <AVFoundation/AVFoundation.h>
-
-@interface AudioEnginePlayer ()
-/** 定时器 */
-@property (nonatomic, strong) NSTimer *timer;
-
-@property (nonatomic, strong) AVAudioEngine *audioEngine;
-
-@property (nonatomic, strong) AVAudioPlayerNode *bgPlayer;
-
-@property (nonatomic, strong) AVAudioUnitTimePitch *timePitchUnit;
-
-@property (nonatomic, strong) AVAudioFile *audioFile;
-
-@property (nonatomic, strong) AVAudioFormat *audioFormat;
-
-@property (nonatomic, strong) dispatch_queue_t sourceQueue;
-
-@property (nonatomic, assign) NSTimeInterval totalDuration;
-
-@property (nonatomic, assign) AVAudioFramePosition startPosition; // 开始位置
-
-@property (nonatomic, assign) BOOL isPlayEnd;  // 是否播放完成
-
-@end
-
-@implementation AudioEnginePlayer
-
-- (instancetype)init {
-    self = [super init];
-    if (self) {
-    }
-    return self;
-}
-
-
-- (void)setupAudioSession {
-    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
-    
-    @try {
-        NSError *err = nil;
-        AVAudioSessionCategory category = AVAudioSessionCategoryPlayAndRecord;
-        if (@available(iOS 10.0, *)) {
-            [audioSession setCategory:category mode:AVAudioSessionModeDefault options:AVAudioSessionCategoryOptionAllowBluetooth|AVAudioSessionCategoryOptionAllowBluetoothA2DP|AVAudioSessionCategoryOptionDefaultToSpeaker|AVAudioSessionCategoryOptionMixWithOthers error:&err];
-        }
-        else {
-            [audioSession setCategory:category withOptions:AVAudioSessionCategoryOptionAllowBluetooth|AVAudioSessionCategoryOptionDefaultToSpeaker|AVAudioSessionCategoryOptionMixWithOthers error:&err];
-        }
-        [audioSession setActive:YES error:&err];
-    } @catch (NSException *exception) {
-        NSLog(@"----- exception --- %@", exception);
-    } @finally {
-        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterruption:) name:AVAudioSessionInterruptionNotification object:audioSession];
-        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleRouteChange:) name:AVAudioSessionRouteChangeNotification object:audioSession];
-    }
-}
-
-- (void)handleRouteChange:(NSNotification *)notification {
-    NSDictionary *info = notification.userInfo;
-    AVAudioSessionRouteChangeReason reason = [info[AVAudioSessionRouteChangeReasonKey] unsignedIntegerValue];
-    if (reason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
-        // 耳机拔出时暂停音频
-        if (self.bgPlayer.isPlaying) {
-            NSError *error = nil;
-            [self sendInterruptError:error];
-        }
-        
-    } else if (reason == AVAudioSessionRouteChangeReasonNewDeviceAvailable) {
-        // 耳机插入时恢复音频
-        if (self.bgPlayer.isPlaying) {
-            NSError *error = nil;
-            [self sendInterruptError:error];
-        }
-    }
-}
-
-// 打断处理
-- (void)handleInterruption:(NSNotification *)notification {
-    
-    NSDictionary *info = notification.userInfo;
-    AVAudioSessionInterruptionType type = [info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
-    if (type == AVAudioSessionInterruptionTypeBegan) {
-        //Handle InterruptionBegan
-        if (self.bgPlayer.isPlaying) {
-            [self.bgPlayer stop];
-            [self sendInterruptError:nil];
-        }
-    }
-    else if (type == AVAudioSessionInterruptionTypeEnded) {
-        AVAudioSessionInterruptionOptions options = [info[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue];
-        if (options == AVAudioSessionInterruptionOptionShouldResume) {
-            //Handle Resume
-            [self resumeAudioSession];
-            NSLog(@"---- 播放恢复");
-        }
-    }
-}
-
-- (void)resumeAudioSession {
-    NSError *error = nil;
-    [[AVAudioSession sharedInstance] setActive:YES error:&error];
-    if (error) {
-        NSLog(@"------ error desc %@", error.description);
-    }
-}
-
-- (void)sendInterruptError:(NSError *)error {
-    if (self.delegate && [self.delegate respondsToSelector:@selector(enginePlayerDidError:error:)]) {
-        [self.delegate enginePlayerDidError:self error:error];
-    }
-}
-
-- (void)configEngine {
-    [self setupAudioSession];
-    self.audioEngine = [[AVAudioEngine alloc] init];
-    self.bgPlayer = [[AVAudioPlayerNode alloc] init];
-    
-    // attach node
-    [self.audioEngine attachNode:self.bgPlayer];
-    [self.audioEngine attachNode:self.timePitchUnit];
-}
-
-- (void)loadAudioFile:(NSURL *)audioUrl {
-    NSError *error = nil;
-    @try {
-        self.audioFile = [[AVAudioFile alloc] initForReading:audioUrl error:&error];
-        self.audioFormat = self.audioFile.processingFormat;
-        self.totalDuration = (AVAudioFramePosition)self.audioFile.length * 1000.0 / self.audioFormat.sampleRate;
-    } @catch (NSException *exception) {
-        self.audioFile = nil;
-        self.audioFormat = nil;
-    } @finally {
-        if (error) {
-            // 错误回调
-            [self sendInterruptError:error];
-        }
-    }
-    
-}
-
-- (void)prepareNativeSongWithUrl:(NSURL *)nativeMusicUrl {
-    dispatch_async(self.sourceQueue, ^{
-        [self loadAudioFile:nativeMusicUrl];
-        if (self.audioFile && self.audioFormat) {
-            [self configEngine];
-            // connect node
-            [self.audioEngine connect:self.bgPlayer to:self.timePitchUnit format:self.audioFormat];
-            [self.audioEngine connect:self.timePitchUnit to:self.audioEngine.mainMixerNode format:self.audioFormat];
-            
-            [self.audioEngine prepare];
-            
-            [self startEngine];
-            if (self.audioEngine && self.audioEngine.isRunning) {
-                dispatch_main_async_safe(^{
-                    if (self.delegate && [self.delegate respondsToSelector:@selector(enginePlayerIsReadyPlay:)]) {
-                        [self.delegate enginePlayerIsReadyPlay:self];
-                    }
-                });
-            }
-        }
-    });
-}
-
-- (void)retryInitEngine {
-    // 如果audio engine不存在
-    self.audioEngine = [[AVAudioEngine alloc] init];
-    // 重新添加节点
-    [self.audioEngine attachNode:self.bgPlayer];
-    [self.audioEngine attachNode:self.timePitchUnit];
-    // 重新建立链接
-    [self.audioEngine connect:self.bgPlayer to:self.timePitchUnit format:self.audioFormat];
-    [self.audioEngine connect:self.timePitchUnit to:self.audioEngine.mainMixerNode format:self.audioFormat];
-}
-
-- (void)startEngine {
-    if (!self.audioEngine) { // 如果audio engine 被释放
-        [self retryInitEngine];
-    }
-    // 启动engine
-    NSError *error = nil;
-    @try {
-        [self.audioEngine startAndReturnError:&error];
-    } @catch (NSException *exception) {
-        
-    } @finally {
-        if (error) {
-            self.audioEngine = nil;
-            [self sendInterruptError:error];
-        }
-    }
-}
-
-- (void)startPlay {
-    if (self.audioEngine.isRunning == NO) {
-        [self startEngine];
-    }
-    if (self.audioEngine.isRunning) {
-        [self.bgPlayer play];
-        [self startTimer];
-    }
-}
-
-- (void)stopPlay {
-    [self stopTimer];
-    if (self.bgPlayer.isPlaying) {
-        [self.bgPlayer stop];
-    }
-}
-
-// 从某个位置开始播放 ms
-- (void)seekToTimePlay:(NSInteger)time {
-    if (self.audioEngine.isRunning == NO) {
-        [self startEngine];
-    }
-    if (self.audioEngine.isRunning) {
-        [self playAudioWithStartTime:time];
-    }
-}
-
-- (void)playAudioWithStartTime:(NSTimeInterval)startTime {
-    
-    if (self.audioEngine.isRunning == NO) {
-        [self startEngine];
-    }
-    if (self.bgPlayer.isPlaying) {
-        [self.bgPlayer stop];
-    }
-    
-    AVAudioFramePosition startPosition = startTime / 1000.0 * self.audioFormat.sampleRate;
-    self.startPosition = startPosition;
-    AVAudioFrameCount frameCount = (AVAudioFrameCount)(self.audioFile.length - startPosition);
-    if (frameCount > 0) {
-        [self.bgPlayer scheduleSegment:self.audioFile startingFrame:startPosition frameCount:frameCount atTime:nil completionHandler:^{
-            
-        }];
-        
-        
-        if (self.audioEngine.isRunning) {
-            [self.bgPlayer play];
-            [self startTimer];
-        }
-    }
-    else { // 已无后续片段
-        // 播放完成
-        if (self.delegate && [self.delegate respondsToSelector:@selector(enginePlayFinished:)]) {
-            [self.delegate enginePlayFinished:self];
-        }
-    }
-}
-
-
-- (void)freePlayer {
-    
-    if (self.bgPlayer.isPlaying) {
-        [self stopPlay];
-    }
-}
-
-
-- (void)startTimer {
-    NSLog(@"----- start timer ");
-    self.isPlayEnd = NO;
-    [self.timer setFireDate:[NSDate distantPast]];
-}
-
-- (void)stopTimer {
-    
-    [self.timer setFireDate:[NSDate distantFuture]];//暂停计时器
-}
-
-
-#pragma mark ----- lazying
-- (NSTimer *)timer{
-    
-    if (!_timer) {
-        MJWeakSelf;
-        _timer = [NSTimer scheduledTimerWithTimeInterval:0.01 repeats:YES block:^(NSTimer * _Nonnull timer) {
-            [weakSelf timeFunction];
-        }];
-        [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
-        [_timer setFireDate:[NSDate distantFuture]];
-    }
-    return _timer;
-}
-
-- (void)timeFunction {
-    NSTimeInterval currentTime = [self getCurrentPlayTime];
-    float progress = currentTime / self.totalDuration;
-    NSDate *date = [NSDate date];
-    NSTimeInterval inteveral = [date timeIntervalSince1970];
-    if (self.delegate && [self.delegate respondsToSelector:@selector(updatePlayProgress:andTotalTime:andProgress:currentInterval:inPlayer:)]) {
-        [self.delegate updatePlayProgress:currentTime andTotalTime:self.totalDuration andProgress:progress currentInterval:inteveral*1000 inPlayer:self];
-    }
-//    NSLog(@"------- current progress %f", progress);
-    if (progress >= 1 && self.isPlayEnd == NO) {
-        self.isPlayEnd = YES;
-        NSLog(@"0---isPlayEnd--------");
-        // 播放完成
-        if (self.delegate && [self.delegate respondsToSelector:@selector(enginePlayFinished:)]) {
-            [self.delegate enginePlayFinished:self];
-        }
-    }
-}
-
-- (void)setRate:(float)rate {
-    _rate = rate;
-    float speed = rate;
-    if (self.timePitchUnit) {
-        self.timePitchUnit.rate = speed;
-    }
-}
-
-- (void)setIsMute:(BOOL)isMute {
-    _isMute = isMute;
-    if (self.bgPlayer) {
-        self.bgPlayer.volume = isMute ? 0.0 : 1.0;
-    }
-}
-
-- (AVAudioUnitTimePitch *)timePitchUnit {
-    if (!_timePitchUnit) {
-        _timePitchUnit = [[AVAudioUnitTimePitch alloc] init];
-    }
-    return _timePitchUnit;
-}
-
-- (NSTimeInterval)getCurrentPlayTime {
-    AVAudioTime *nodeTime = [self.bgPlayer lastRenderTime];
-    if (nodeTime.sampleTimeValid && nodeTime.hostTimeValid && nodeTime && self.audioFile) {
-        
-        AVAudioTime *playerTime = [self.bgPlayer playerTimeForNodeTime:nodeTime];
-        double sampleRate = [playerTime sampleRate];
-        double elapsedSamples = (double)[playerTime sampleTime];
-        if (elapsedSamples <= 0) {
-            elapsedSamples = 0;
-        }
-        elapsedSamples += self.startPosition;
-        NSTimeInterval currentTime = elapsedSamples / sampleRate;
-        NSLog(@"当前时间----- %f",currentTime*1000);
-        return currentTime*1000;
-    }
-    else {
-        return 0;
-    }
-}
-
-- (void)dealloc {
-    [[NSNotificationCenter defaultCenter] removeObserver:self];
-    NSLog(@"---------AudioEnginePlayer dealloc ");
-    if (_timer) {
-        [_timer invalidate];
-        _timer = nil;
-    }
-}
-
-- (BOOL)isPlaying {
-    if (self.bgPlayer) {
-        return self.bgPlayer.isPlaying;
-    }
-    return NO;
-}
-
-- (dispatch_queue_t)sourceQueue {
-    if (!_sourceQueue) {
-        _sourceQueue = dispatch_queue_create("KSAudioEnginePlayer_queue", DISPATCH_QUEUE_SERIAL);
-    }
-    return _sourceQueue;
-}
-@end

+ 0 - 16
KulexiuForStudent/KulexiuForStudent/Common/Base/AccompanyWebView/KSAccompanyWebViewController.h

@@ -1,16 +0,0 @@
-//
-//  KSAccompanyWebViewController.h
-//  KulexiuForTeacher
-//
-//  Created by Kyle on 2022/3/20.
-//
-
-#import "KSBaseWKWebViewController.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface KSAccompanyWebViewController : KSBaseWKWebViewController
-
-@end
-
-NS_ASSUME_NONNULL_END

+ 0 - 1854
KulexiuForStudent/KulexiuForStudent/Common/Base/AccompanyWebView/KSAccompanyWebViewController.m

@@ -1,1854 +0,0 @@
-//
-//  KSAccompanyWebViewController.m
-//  KulexiuForTeacher
-//
-//  Created by Kyle on 2022/3/20.
-//
-
-#import "KSAccompanyWebViewController.h"
-#import "WebViewBaseConfig.h"     // 基础配置
-#import "SwiftImportHeader.h"     // swift 桥接
-#import <KSTunerLibrary/KSTunerLibrary-Swift.h>
-
-#import <KSAudioSessionManager.h> // audio session
-#import <KSAQRecordManager.h>     // 录音
-#import "KSTargetWebSocketManager.h"    // web socket
-#import <KSVideoRecordManager.h>  // 视频录制
-#import <KSCloudBeatView.h>      // 节拍器
-#import <MidiPlayerEngine.h>      // midi 播放
-
-#import "AccompanyLoadingView.h"
-
-#define KSMidiSongFileKey (@"KSDownloadMidiSong")
-
-// 合成
-#import <KSMediaEditor.h>
-#import "KSAccompanyDraftViewController.h"
-#import "KSLogManager.h"
-
-#import "AudioEnginePlayer.h"
-
-@interface KSAccompanyWebViewController ()<KSAQRecordManagerDelegate,KSAudioSessionManagerDelegate,PlayerEngineDelegate,TunerDelegate,AudioEnginePlayerDelegate>
-
-@property (nonatomic, strong) KSAudioSessionManager *audioSessionManager;
-
-@property (nonatomic, assign) MetronomeType beatType;
-
-@property (nonatomic, strong) MidiPlayerEngine *playerEngine; // player
-
-@property (nonatomic, assign) BOOL initEngineSuccess; // 加载引擎是否成功
-
-@property (nonatomic, assign) float songOriginalSpeed;
-
-@property (nonatomic, assign) float currentSpeed; // 当前播放的速度
-
-@property (nonatomic, assign) BOOL isPlaying; // 是否正在播放的状态
-
-@property (nonatomic, strong) NSString *currentSongId; // song id
-
-@property (nonatomic, strong) NSDictionary *configEngineParm; // 初始化mid播放引擎的参数
-
-@property (nonatomic, strong) NSMutableDictionary *metronomeParm; // 节拍器播放的参数
-
-// 是否发送了musicXML信息
-@property (nonatomic, assign) BOOL hasSendStartMessage;
-
-@property (nonatomic, strong) KSAQRecordManager *AQManager;
-
-@property (nonatomic, strong) KSTargetWebSocketManager *socketManager;
-
-@property (nonatomic, strong) NSMutableDictionary *evaluatParm;
-
-// 录制的message
-@property (nonatomic, strong) NSMutableDictionary *recordParm;
-
-// 评测开始时发送recordStart消息的标识
-@property (nonatomic, assign) BOOL isCompareStart;
-// 校音开始时发送 checkStart消息标识
-@property (nonatomic, assign) BOOL isSoundCheckStart;
-
-// 自定义UI控件容器
-@property (nonatomic, strong) UIView *viewContainer;
-
-@property (nonatomic, strong) KSVideoRecordManager *videoRecordManager;
-
-@property (nonatomic, strong) NSDictionary *endRecordParm;
-
-@property (nonatomic, assign) BOOL isCameraOpen;
-
-@property (nonatomic, strong) AccompanyLoadingView *loadingView;
-
-@property (nonatomic, assign) BOOL isTunerRuning;
-
-@property (nonatomic, strong) Tuner *tuner;
-
-// 延迟校准开始时发送 AdjustStart消息标识
-@property (nonatomic, assign) BOOL isDelayCheckStart;
-// 检测延迟播放器
-@property (nonatomic, strong) AudioEnginePlayer *delayCheckPlayer;
-
-// 音频播放器
-@property (nonatomic, strong) AudioEnginePlayer *musicPlayer;
-
-@property (nonatomic, assign) float musicSpeed; //播放速度
-// 录音开始时间
-@property (nonatomic, assign) NSTimeInterval recordStartTime;
-// 播放开始时间
-@property (nonatomic, assign) NSTimeInterval playerStartTime;
-// 播放延迟
-@property (nonatomic, assign) NSInteger offsetTime;
-
-
-@property (nonatomic, assign) BOOL checkPlayerReady;
-
-@property (nonatomic, assign) BOOL musicPlayerReady;
-
-@property (nonatomic, strong) NSDictionary *playerParm;
-
-@property (nonatomic, strong) NSMutableArray *delayArray;
-
-@property (nonatomic, assign) NSInteger checkIndex;
-
-@property (nonatomic, assign) NSInteger checkPrequence;
-
-@property (nonatomic, strong) NSURL *bgAudioUrl;
-
-@property (nonatomic, strong) NSURL *recordUrl;
-
-@property (nonatomic, strong) NSString *accompanyUrl;
-
-@property (nonatomic, assign) BOOL muteAccompany; // 是否静音
-
-@property (nonatomic, assign) NSInteger musicStartTime;  // 开始播放的时间
-
-@property (nonatomic, assign) BOOL hasRecordMusicOffset; // 是否记录了播放延迟
-
-@end
-
-@implementation KSAccompanyWebViewController
-
-- (void)handerAudioInterruption {
-    if (_playerEngine) {
-        [self stopPlayAction];
-    }
-    if (_musicPlayer) {
-        [self stopMp3Player];
-    }
-}
-
-- (void)resumeAudioSession {
-    // 恢复
-    if (_playerEngine) {
-        [self.playerEngine resumeAUGraph];
-    }
-}
-
-// 打断处理
-- (void)audioInterruption {
-    if (_videoRecordManager) {
-        [self.videoRecordManager resetSession];
-    }
-    [self handerAudioInterruption];
-}
-
-- (void)ignorRecordVideo {
-    if (_videoRecordManager) {
-        self.videoRecordManager.skipSaveRecord = YES;
-    }
-}
-
-- (void)stopSession {
-    if (_videoRecordManager) {
-        [self.videoRecordManager stopSession];
-    }
-}
-
-- (void)resetPlayerConfig {
-    [self freeMp3Player]; // 若之前有播放器,剔除
-    self.checkPrequence = 800;
-    self.checkPlayerReady = NO;
-    self.musicPlayerReady = NO;
-    self.recordStartTime = 0;
-    self.playerStartTime = 0;
-    self.offsetTime = 0;
-}
-
-- (void)downloadMp3File:(NSString *)musicUrl {
-    if (![NSString isEmptyString:musicUrl]) {
-        NSString *fileName = [musicUrl getUrlFileName];
-        NSString *filePath = [self getAccompanyFilePathWithName:fileName];
-        NSURL *fileUrl = [[NSURL alloc] initFileURLWithPath:filePath];
-        if ([self checkSongHasSaveAccompanyWithSongUrl:musicUrl]) {
-            [self configVideoRecord:fileUrl];
-            [self initMusicPlayer:fileUrl];
-        }
-        else {
-            MJWeakSelf;
-            [self downloadUrl:musicUrl success:^{
-                [weakSelf configVideoRecord:fileUrl];
-                [weakSelf initMusicPlayer:fileUrl];
-            } faliure:^{
-                
-            }];
-        }
-    }
-    else {
-        [self configVideoRecord:nil];
-    }
-}
-
-- (void)initMusicPlayer:(NSURL *)musicUrl {
-    
-    [self.musicPlayer prepareNativeSongWithUrl:musicUrl];
-}
-
-- (void)downloadCheckMusic:(NSString *)checkMusicUrl {
-    if (![NSString isEmptyString:checkMusicUrl]) {
-        NSString *fileName = [checkMusicUrl getUrlFileName];
-        NSString *filePath = [self getAccompanyFilePathWithName:fileName];
-        NSURL *fileUrl = [[NSURL alloc] initFileURLWithPath:filePath];
-        if ([self checkSongHasSaveAccompanyWithSongUrl:checkMusicUrl]) {
-            [self initCheckPlayer:fileUrl];
-        }
-        else {
-            MJWeakSelf;
-            [self downloadUrl:checkMusicUrl success:^{
-                [weakSelf initCheckPlayer:fileUrl];
-            } faliure:^{
-                
-            }];
-        }
-    }
-}
-
-- (void)initCheckPlayer:(NSURL *)checkUrl {
-    [self.delayCheckPlayer prepareNativeSongWithUrl:checkUrl];
-}
-
-- (void)initMp3Player:(NSString *)musicUrl checkUrl:(NSString *)checkUrl {
-    
-    [self resetPlayerConfig];
-    self.accompanyUrl = musicUrl;
-    [self downloadCheckMusic:checkUrl];
-}
-
--  (void)freeMp3Player {
-    if (_delayCheckPlayer) {
-        [self.delayCheckPlayer freePlayer];
-    }
-     if (_musicPlayer) {
-          [self.musicPlayer freePlayer];
-     }
-}
-
-- (void)stopMp3Player {
-    if (_delayCheckPlayer) {
-        [self.delayCheckPlayer stopPlay];
-    }
-     if (_musicPlayer) {
-          [self.musicPlayer stopPlay];
-     }
-}
-
-
-
-- (void)viewDidLoad {
-    [super viewDidLoad];
-    // Do any additional setup after loading the view.
-    [self configAudioSession];
-    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appEnterBackground) name:@"appEnterBackground" object:nil];
-    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appEnterForeground) name:@"appEnterForeground" object:nil];
-}
-
-
-- (void)checkMessage:(id)message {
-    NSDictionary *result = [message mj_JSONObject];
-    NSString *type = [[result ks_dictionaryValueForKey:@"header"] ks_stringValueForKey:@"type"];
-    NSString *commond = [[result ks_dictionaryValueForKey:@"header"] ks_stringValueForKey:@"commond"];
-    if ([type isEqualToString:@"DELAY_CHECK"] && [commond isEqualToString:@"recordEnd"]) {
-        NSDictionary *body = [result ks_dictionaryValueForKey:@"body"];
-        NSTimeInterval micDelay = [body ks_doubleValueForKey:@"firstNoteDelayDuration"] - self.offsetTime;
-        [self.delayArray addObject:[NSNumber numberWithDouble:micDelay]];
-    }
-}
-
-- (void)connectSocketService {
-    
-    MJWeakSelf;
-    self.socketManager.didReceiveMessage = ^(id  _Nonnull message) {
-        dispatch_async(dispatch_get_main_queue(), ^{
-            [weakSelf checkMessage:message];
-            // api
-            NSMutableDictionary *sendMessage = [NSMutableDictionary dictionary];
-            [sendMessage setValue:@"sendResult" forKey:@"api"];
-            [sendMessage setValue:message forKey:@"content"];
-            // 服务返回数据传给H5
-            [weakSelf postMessage:sendMessage];
-        });
-    };
-    self.socketManager.connectionStatus = ^(BOOL isSuccess) {
-        dispatch_async(dispatch_get_main_queue(), ^{
-            
-            if (!isSuccess) {
-                //                NSLog(@"-----连接失败");
-                if (weakSelf.hasSendStartMessage) {
-                    if (weakSelf.AQManager.isRunning) {
-                        NSDictionary *postParm = @{@"api" : @"cancelEvaluating",
-                                                   @"content" : @{@"reson":@"服务异常,请重试"}
-                        };
-                        [weakSelf postMessage:postParm];
-                        [weakSelf stopRecordService];
-                        weakSelf.isCompareStart = NO;
-                        weakSelf.isSoundCheckStart = NO;
-                        weakSelf.isDelayCheckStart = NO;
-                    }
-                }
-                else if (weakSelf.evaluatParm) {
-                    NSMutableDictionary *sendParm = [NSMutableDictionary dictionaryWithDictionary:weakSelf.evaluatParm];
-                    NSDictionary *valueDic = [weakSelf.evaluatParm ks_dictionaryValueForKey:@"content"];
-                    [valueDic setValue:@"服务异常,请重试" forKey:@"reson"];
-                    [sendParm setValue:valueDic forKey:@"content"];
-                    [weakSelf postMessage:sendParm];
-                    weakSelf.hasSendStartMessage = YES;
-                    weakSelf.isCompareStart = NO;
-                    weakSelf.isSoundCheckStart = NO;
-                    weakSelf.isDelayCheckStart = NO;
-                }
-                else { // 其他断开
-                    if (weakSelf.AQManager.isRunning) {
-                        NSDictionary *postParm = @{@"api" : @"cancelEvaluating",
-                                                   @"content" : @{@"reson":@"服务异常,请重试"}
-                        };
-                        [weakSelf postMessage:postParm];
-                        [weakSelf stopRecordService];
-                        weakSelf.isCompareStart = NO;
-                        weakSelf.isSoundCheckStart = NO;
-                        weakSelf.isDelayCheckStart = NO;
-                    }
-                }
-            }
-            else {
-                //                NSLog(@"-----连接成功");
-                if (weakSelf.hasSendStartMessage == NO && weakSelf.evaluatParm) {
-                    NSDictionary *content = [weakSelf.evaluatParm ks_dictionaryValueForKey:@"content"];
-                    NSString *sendData = [weakSelf configDataCommond:@"musicXml" body:content type:@"SOUND_COMPARE"];
-                    [weakSelf sendDataToSocketService:sendData];
-                    [weakSelf postMessage:weakSelf.evaluatParm];
-                    weakSelf.hasSendStartMessage = YES;
-                    }
-            }
-        });
-    };
-    [self.socketManager SRWebSocketOpen];
-}
-
-- (void)appEnterBackground {
-    NSDictionary *parm = @{@"api":@"suspendPlay"};
-    [self postMessage:parm];
-    [self handerAudioInterruption];
-}
-
-- (void)appEnterForeground {
-    if (self.isCameraOpen) {
-        if ([self.videoRecordManager getSessionStatusisActive] == NO) {
-            [self.videoRecordManager configSessiondisplayInView:self.viewContainer];
-        }
-    }
-}
-
-- (void)initWebView {
-    [self.scrollView removeFromSuperview];
-    
-    [self.view addSubview:self.viewContainer];
-    [self.viewContainer mas_makeConstraints:^(MASConstraintMaker *make) {
-        make.left.right.top.bottom.mas_equalTo(self.view);
-    }];
-    
-    if (self.myWebView == nil) {
-        
-        WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
-        config.selectionGranularity = WKSelectionGranularityDynamic;
-        config.allowsInlineMediaPlayback = YES;
-        config.mediaTypesRequiringUserActionForPlayback = NO;
-        
-        config.processPool = [KSBaseWKWebViewController singleWkProcessPool];
-        config.websiteDataStore = [WKWebsiteDataStore defaultDataStore];
-        [self configUserAgent:config];
-        
-        //自定义的WKScriptMessageHandler 是为了解决内存不释放的问题
-        WeakWebViewScriptMessageDelegate *weakScriptMessageDelegate = [[WeakWebViewScriptMessageDelegate alloc] initWithDelegate:self];
-        //这个类主要用来做native与JavaScript的交互管理
-        WKUserContentController * wkUController = [[WKUserContentController alloc] init];
-        [wkUController addScriptMessageHandler:weakScriptMessageDelegate name:SCRIPT_NAME];
-        config.userContentController = wkUController;
-        
-        WKPreferences *preferences = [WKPreferences new];
-        // 是否支出javaScript
-        preferences.javaScriptEnabled = YES;
-        //不通过用户交互,是否可以打开窗口
-        preferences.javaScriptCanOpenWindowsAutomatically = YES;
-        config.preferences = preferences;
-        self.myWebView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config];
-        self.myWebView.opaque = NO;
-        self.myWebView.UIDelegate = self;
-        self.myWebView.navigationDelegate = self;
-        self.myWebView.scrollView.bounces = NO;
-        self.myWebView.backgroundColor = [UIColor clearColor];
-        self.myWebView.scrollView.backgroundColor = [UIColor clearColor];
-#ifdef DEBUG
-            if (@available(iOS 16.4, *)) {
-                self.myWebView.inspectable = YES;
-            }
-#endif
-        // 加载进度条和title
-        [self.myWebView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
-        [self.myWebView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:NULL];
-        [self.view addSubview:self.myWebView];
-        [self.myWebView mas_makeConstraints:^(MASConstraintMaker *make) {
-            make.left.right.mas_equalTo(self.view);
-            make.top.mas_equalTo(self.view.mas_top);
-            make.bottom.mas_equalTo(self.view.mas_bottom);
-        }];
-        self.myWebView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
-        
-        [self setupProgress];
-        
-        [self loadRequest];
-    }
-    else {
-        [self.myWebView reload];
-    }
-}
-
-- (void)configUserAgent:(WKWebViewConfiguration *)config {
-    NSString *oldUserAgent = config.applicationNameForUserAgent;
-    NSString *newAgent = [NSString stringWithFormat:@"%@ %@ %@",oldUserAgent,AGENT_NAME,AGENT_DOMAIN];
-    config.applicationNameForUserAgent = newAgent;
-}
-
-- (void)configRecordManager {
-    if (self.AQManager.isRunning) {
-        [self.AQManager stopRecord];
-    }
-    self.AQManager = [[KSAQRecordManager alloc] init];
-    self.AQManager.delegate = self;
-}
-
-- (void)viewWillAppear:(BOOL)animated {
-    [super viewWillAppear:animated];
-    [self connectSocketService];
-    if (self.isCameraOpen) {
-        [self.videoRecordManager configSessiondisplayInView:self.viewContainer];
-    }
-}
-
-- (void)viewWillDisappear:(BOOL)animated {
-    [super viewWillDisappear:animated];
-    
-    if (_socketManager) {
-        [self.socketManager SRWebSocketClose];
-        _socketManager = nil;
-    }
-    // 停止播放和录制
-    [self stopSession];
-    [self stopPlayAction];
-    [self stopRecordService];
-    [self stopTuner];
-    
-    BOOL isBack = [self isViewPopDismiss];
-    if (isBack) { // 页面销毁才删除
-        if (_AQManager) {
-            [_AQManager freeAudioQueue];
-        }
-        // 如果退出评测页面 清除 playerEngine
-        if (self.playerEngine) {
-            [self.playerEngine cleanup];
-            self.playerEngine = nil;
-        }
-        // 返回不保存视频
-        [self ignorRecordVideo];
-        [self freeMp3Player];
-        [self removeTuner];
-    }
-}
-
-- (void)removeTuner {
-    if (_tuner) {
-        [_tuner freeTuner];
-        _tuner = nil;
-        NSLog(@"--- free tuner ");
-    }
-}
-
-
-- (void)sendDataToSocketService:(id)data {
-    if (_socketManager) {
-        [self.socketManager sendData:data];
-    }
-}
-
-#pragma mark --- WKScriptMessageHandler
-
-- (void)userContentController:(WKUserContentController *)userContentController
-      didReceiveScriptMessage:(WKScriptMessage *)message {
-    if ([message.name isEqualToString:SCRIPT_NAME]) {
-        NSDictionary *parm = [self convertJsonStringToNSDictionary:message.body];
-        // 回到主线程
-        dispatch_async(dispatch_get_main_queue(), ^{
-            if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"createMusicPlayer"]) {
-                NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
-                NSString *musicUrl = [content ks_stringValueForKey:@"musicSrc"];
-                NSString *checkUrl = [content ks_stringValueForKey:@"tuneSrc"];
-                self.playerParm = parm;
-                [self initMp3Player:musicUrl checkUrl:checkUrl];
-                // 下载文件
-                [self downloadMp3File:musicUrl];
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"startEvaluating"]) { // 开始评测
-                [self configRecordManager];
-                self.hasSendStartMessage = NO;
-                [RecordCheckManager checkMicPermissionAvaiableCallback:^(PREMISSIONTYPE type) {
-                    if (type == PREMISSIONTYPE_YES) {
-                        self.evaluatParm = [NSMutableDictionary dictionaryWithDictionary:parm];
-                        // 如果socket 连上了
-                        if (self.socketManager.socketReadyState == SR_OPEN) {
-                            NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
-                            NSString *sendData = [self configDataCommond:@"musicXml" body:content type:@"SOUND_COMPARE"];
-                            [self sendDataToSocketService:sendData];
-                            [self postMessage:parm];
-                            self.hasSendStartMessage = YES;
-                        }
-                        else {
-                            [self connectSocketService];
-                        }
-                    }
-                    else {
-                        [self responseMessage:@"storageUnable" desc:@"没有麦克风访问权限" parm:parm];
-                        [self showAlertWithMessage:@"请开启麦克风访问权限" type:CHECKDEVICETYPE_MIC];
-                    }
-                }];
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"endEvaluating"]) {// 停止评测
-                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
-                    self.musicStartTime = 0;
-                    self.evaluatParm = nil;
-                    [self stopRecordService];
-                    [self postMessage:parm];
-                    [self sendEndMessage];
-                    [self stopMp3Player]; // 停止播放
-                });
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cancelEvaluating"]) { // 取消评测
-                self.musicStartTime = 0;
-                self.evaluatParm = nil;
-                [self stopRecordService];
-                [self postMessage:parm];
-                [self stopMp3Player]; // 停止播放
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"startRecording"]) { //  开始录制
-                NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
-                self.musicStartTime = [content ks_integerValueForKey:@"firstNoteTime"];
-                if ([[content allKeys] containsObject:@"accompanimentState"]) {
-                    BOOL mute = [content ks_boolValueForKey:@"accompanimentState"] == NO;
-                    self.muteAccompany = mute;
-                }
-                else {
-                    self.muteAccompany = NO;
-                }
-                if ([[content allKeys] containsObject:@"speedRate"]) { // 播放速度
-                    self.musicSpeed = [content ks_floatValueForKey:@"speedRate"];
-                }
-                else {
-                    self.musicSpeed = 1.0f;
-                }
-
-                if (self->_videoRecordManager) {
-                    [self.videoRecordManager clearVideoFile];
-                }
-                [self postMessage:parm];
-                self.isCompareStart = YES;
-                [self startRecordService];
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"endRecording"]) { // 停止录音
-                self.recordParm = nil;
-                [self stopRecordService];
-                [self postMessage:parm];
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"pauseRecording"]) {
-                [self puaseRecordService];
-                [self postMessage:parm];
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"resumeRecording"]) {
-                [self resumeRecordService];
-                [self postMessage:parm];
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"isWiredHeadsetOn"]) {
-                [self configAudioDeviceType:parm];
-            }
-            // 发送消息给socket service
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"proxyMessage"]) {
-                [self sendMessageToSocket:parm];
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"proxyServiceMessage"]) {
-                NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
-                NSString *sendData = [content mj_JSONString];
-                NSLog(@"proxyServiceMessage ------- %@",sendData);
-                [self sendDataToSocketService:sendData];
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"openCamera"]) { // 开启摄像头
-                [RecordCheckManager checkCameraPremissionAvaiableCallback:^(PREMISSIONTYPE type) {
-                    [self afterCheckCameraCheckAlbum:type parm:parm];
-                }];
-                
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"closeCamera"]) { // 关闭摄像头
-                self.isCameraOpen = NO;
-                if (self->_videoRecordManager) {
-                    [self.videoRecordManager removeDisplay];
-                }
-                [self postMessage:parm];
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"startCapture"]) { // 开始录制
-                [RecordCheckManager checkPhotoLibraryPremissionAvaiableCallback:^(PREMISSIONTYPE type) {
-                    if (type == PREMISSIONTYPE_YES) {
-                        [self.videoRecordManager setIgnoreAudio:YES];
-                        self.videoRecordManager.audioUrl = self.AQManager.audioUrl;
-                        [self.videoRecordManager startRecord];
-                        [self postMessage:parm];
-                    }
-                    else {
-//                        [self responseMessage:@"storageUnable" desc:@"没有相册存储权限" parm:parm];
-//                        [self showAlertWithMessage:@"开启相册存储" type:CHECKDEVICETYPE_CAMREA];
-                    }
-                }];
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"endCapture"]) { // 结束录制
-                if (self->_videoRecordManager) {
-                    self.endRecordParm = parm;
-                    [self.videoRecordManager stopRecord];
-                }
-                else {
-                    [self postMessage:parm];
-                }
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"setCaptureMode"]) {
-                NSString *modeString = [[parm ks_dictionaryValueForKey:@"content"] ks_stringValueForKey:@"mode"];
-                BOOL isIgnoreAudio = [modeString isEqualToString:@"evaluating"];
-                [self postMessage:parm];
-                [self.videoRecordManager setIgnoreAudio:isIgnoreAudio];
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"startSoundCheck"]) { // 开始校音
-                [self configRecordManager];
-                [self postMessage:parm];
-                self.isCompareStart = NO;
-                self.isSoundCheckStart = YES;
-                self.isDelayCheckStart = NO;
-                [self startRecordService];
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"endSoundCheck"]) {
-                // 结束校音
-                self.isSoundCheckStart = NO;
-                [self stopRecordService];
-                [self postMessage:parm];
-            }
-            // 音视频合成
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"openAdjustRecording"]) {
-                KSAccompanyDraftViewController *ctrl = [[KSAccompanyDraftViewController alloc] init];
-                ctrl.ks_landScape = YES;
-                
-                if (self.bgAudioUrl == nil) {
-                    [LOADING_MANAGER MBShowAUTOHidingInWindow:@"当前曲目无mp3伴奏"];
-                }
-                else {
-                    if (self.AQManager && self.AQManager.audioUrl) {
-                        self.recordUrl = self.AQManager.audioUrl;
-                        
-                        NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
-                        ctrl.recordId = [content ks_stringValueForKey:@"recordId"];
-                        ctrl.songName = [content ks_stringValueForKey:@"title"];
-                        ctrl.coverImage = [content ks_stringValueForKey:@"coverImg"];
-                        if ([[content allKeys] containsObject:@"speedRate"]) {
-                            ctrl.musicSpeed = [content ks_floatValueForKey:@"speedRate"];
-                        }
-                        else {
-                            ctrl.musicSpeed = 1.0f;
-                        }
-                        MJWeakSelf;
-                        NSInteger micDelay = [UserDefaultObjectForKey(@"micDelay") integerValue];
-                        NSInteger defaultDelay = self.offsetTime + micDelay;
-                        [ctrl configWithVideoUrl:self.videoRecordManager.videoFileURL bgAudioUrl:self.bgAudioUrl remoteBgUrl:self.accompanyUrl  recordUrl:self.recordUrl offsetTime:defaultDelay mergeCallback:^(BOOL isPublished) {
-                            if (isPublished) {
-                                [weakSelf musicPublishCallBack:content];
-                            }
-                        }];
-                        [self.navigationController pushViewController:ctrl animated:NO];
-                    }
-                    else {
-                        [LOADING_MANAGER MBShowAUTOHidingInWindow:@"麦克风被占用"];
-                    }
-                }
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"videoUpdate"]) { // 上传
-                NSMutableDictionary *sendParm = [NSMutableDictionary dictionaryWithDictionary:parm];
-                NSMutableDictionary *contentParm = [NSMutableDictionary dictionaryWithDictionary:[sendParm ks_dictionaryValueForKey:@"content"]];
-                if (self.videoRecordManager) {
-                    
-                    MJWeakSelf;
-                    [self.videoRecordManager saveVideoCallback:^(BOOL isSuccess, NSString * _Nullable message) {
-                        if (isSuccess) {
-                            [LOADING_MANAGER MBShowAUTOHidingInWindow:@"已保存到相册"];
-                            [weakSelf uploadVideoWithParm:contentParm sendParm:sendParm];
-                        }
-                    }];
-                }
-            }
-#pragma mark -------- 云教练原生播放对接
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudDetail"]) { // 初始化方法
-                /**
-                 api: 'cloudDetail',
-                 content: {
-                 midi: '',
-                 denominator: 4,
-                 numerator: 4,
-                 // xml整体原始速度
-                 originalSpeed: 90,
-                 // 间隔(ms)
-                 interval: 50
-                 }
-                 */
-                
-                [self configAudioSession];
-                // 重置track num array
-                self.configEngineParm = [parm copy];
-                NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
-                NSString *midiUrl = [content ks_stringValueForKey:@"midi"];
-                NSInteger denominator = [content ks_integerValueForKey:@"denominator"];
-                NSInteger numerator = [content ks_integerValueForKey:@"numerator"];
-                NSString *beatString = [NSString stringWithFormat:@"%zd/%zd", numerator,denominator];
-                self.beatType = [self getBeatTypeFromString:beatString];
-                
-                float originalSpeed = [content ks_floatValueForKey:@"originalSpeed"];
-                self.songOriginalSpeed = originalSpeed;
-                self.currentSpeed = originalSpeed;
-                NSInteger reportInterval = [content ks_integerValueForKey:@"interval"];
-                // 下载midi文件
-                BOOL hasSaveSong = [self checkSongHasSaveWithSongUrl:midiUrl];
-                NSString *fileName = [midiUrl getUrlFileName];
-                NSString *filePath = [self getFilePathWithName:fileName];
-                if (hasSaveSong) {
-                    [self configPlayerEngineWithSong:filePath reportTime:reportInterval];
-                }
-                else {
-                    MJWeakSelf;
-                    [self downloadMidiFile:midiUrl success:^{
-                        [weakSelf configPlayerEngineWithSong:filePath reportTime:reportInterval];
-                    } faliure:^{
-                        
-                    }];
-                }
-                
-            }
-            
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudGetMediaStatus"]) { // 获取播放状态
-                /**
-                 api: 'cloudGetMediaStatus',
-                 content: {
-                 status: 'init' | 'play' | 'suspend'
-                 }
-                 */
-                NSString *engineStatus = @"init";
-                if (self.initEngineSuccess) {
-                    if (self.isPlaying) {
-                        engineStatus = @"play";
-                    }
-                    else {
-                        engineStatus = @"suspend";
-                    }
-                }
-                NSMutableDictionary *valueDic = [NSMutableDictionary dictionaryWithDictionary:[parm ks_dictionaryValueForKey:@"content"]];
-                [valueDic setValue:engineStatus forKey:@"status"];
-                NSMutableDictionary *sendParm = [NSMutableDictionary dictionaryWithDictionary:parm];
-                [sendParm setValue:valueDic forKey:@"content"];
-                [self postMessage:sendParm];
-            }
-            // 播放、暂停、进度、播放结束、跳转指定位置
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudPlay"]) { // 播放
-                /**
-                 api: 'cloudPlay',
-                 content: {
-                 // 当前曲目id
-                 songID: 0,
-                 // xml整体原始速度
-                 originalSpeed: 90,
-                 // 当前选择速度
-                 speed: 90,
-                 // 开始时间(ms)
-                 startTime: 0
-                 // 播放频率
-                 hertz:440
-                 }
-                 */
-                NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
-                self.currentSongId = [content ks_stringValueForKey:@"songID"];
-                float speed = [content ks_floatValueForKey:@"speed"];
-                float originalSpeed = [content ks_floatValueForKey:@"originalSpeed"];
-                double rate = speed / originalSpeed;
-                self.currentSpeed = speed;
-                float hertz = [content ks_floatValueForKey:@"hertz"];
-                // 播放的hertz
-                [self.playerEngine adjustPitchByHZ:hertz];
-                
-                [self.playerEngine setMusicPlayerSpeed:rate];
-                Float64 startTime = [content ks_doubleValueForKey:@"startTime"];
-                NSLog(@"------%@", [content ks_stringValueForKey:@"startTime"]);
-                [self.playerEngine setProgressTime:(startTime/1000)];
-                // 播放
-                [self playAction];
-                [self postMessage:parm];
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudSuspend"]) { // 暂停
-                /**
-                 api: 'cloudSuspend',
-                 content: {
-                 // 当前曲目id
-                 songID: 0,
-                 }
-                 */
-                [self stopPlayAction];
-                [self postMessage:parm];
-                
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudSetCurrentTime"]) { // // 跳转指定位置
-                /**
-                 api: 'cloudSetCurrentTime',
-                 content: {
-                 // 当前曲目id
-                 songID: 0,
-                 // 指定位置时间(ms)
-                 currentTime: 0
-                 }
-                 */
-                NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
-                Float64 currentTime = [content ks_doubleValueForKey:@"currentTime"];
-                if (self.playerEngine) {
-                    [self.playerEngine setProgressTime:(currentTime/1000)];
-                }
-                [self postMessage:parm];
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudChangeSpeed"]) { // 调速 范围(45-270整数)
-                /**
-                 api: 'cloudChangeSpeed',
-                 content: {
-                 // 当前曲目id
-                 songID: 0,
-                 // 调整速度
-                 speed: 90
-                 // xml整体原始速度
-                 originalSpeed: 90,
-                 }
-                 */
-                NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
-                float speed = [content ks_floatValueForKey:@"speed"];
-                float originalSpeed = [content ks_floatValueForKey:@"originalSpeed"];
-                double rate = speed / originalSpeed;
-                self.currentSpeed = speed;
-                [self.playerEngine setMusicPlayerSpeed:rate];
-                // 回报信息
-                [self postMessage:parm];
-            }
-            // 设置每个轨道音量
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudVolume"]) {
-                /**
-                 api: 'cloudVolume',
-                 content: {
-                 parts: [
-                 {
-                 name: '',
-                 volume: 90,//0-100
-                 }
-                 ]
-                 }
-                 */
-                NSLog(@"-cloudVolume -----%@",parm);
-                NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
-                int instrumentId = [content ks_intValueForKey:@"activeMidiId"];
-                float volume = [content ks_floatValueForKey:@"activeMidiVolume"];
-                if (instrumentId < 0) {
-                    [self postMessage:parm];
-                    [self.playerEngine getAllTrackVolume];
-                    return;
-                }
-                [self.playerEngine volumeTrackVolumeWithInstrumentId:instrumentId volume:volume/100];
-                [self postMessage:parm];
-                [self.playerEngine getAllTrackVolume];
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudMetronome"]) { // 节拍器播放
-                self.metronomeParm = [NSMutableDictionary dictionaryWithDictionary:parm];
-                NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
-                NSInteger repeatCount = [content ks_integerValueForKey:@"repeat"];
-                NSInteger denominator = [content ks_integerValueForKey:@"denominator"];
-                NSInteger numerator = [content ks_integerValueForKey:@"numerator"];
-                NSInteger supplement = [content ks_integerValueForKey:@"supplement"];
-                NSString *beatString = [NSString stringWithFormat:@"%zd/%zd", numerator,denominator];
-                self.beatType = [self getBeatTypeFromString:beatString];
-                [self showBeatViewRepeatCount:repeatCount supplement:supplement];
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudDestroy"]) { // 销毁播放器
-                self.initEngineSuccess = NO;
-                // 重置track num array
-                self.isPlaying = NO;
-                if (self.playerEngine) {
-                    [self.playerEngine cleanup];
-                    self.playerEngine = nil;
-                }
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudLoading"]) { // loading
-                NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
-                BOOL showLoading = [content ks_boolValueForKey:@"show"];
-                if (showLoading) {
-                    [self showCustomLoading];
-                }
-                else {
-                    [self removeCustomLoadingView];
-                }
-            }
-            // 跟音
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudToggleFollow"]) { // 跟音
-                [RecordCheckManager checkMicPermissionAvaiableCallback:^(PREMISSIONTYPE type) {
-                    if (type == PREMISSIONTYPE_YES) {
-                        NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
-                        NSString *status = [content ks_stringValueForKey:@"state"];
-                        if ([status isEqualToString:@"start"]) { // 开始
-                            [self configAudioSession];
-                            [self startTuner];
-                        }
-                        else if ([status isEqualToString:@"end"]) { // 结束
-                            [self stopTuner];
-                        }
-                        [self postMessage:parm];
-                    }
-                    else {
-                        [self responseMessage:@"storageUnable" desc:@"没有麦克风权限" parm:parm];
-                        [self showAlertWithMessage:@"请开启麦克风访问权限" type:CHECKDEVICETYPE_MIC];
-                    }
-                }];
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudAccompanyMessage"]) { // 获取伴奏 废弃⚠️
-                
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"startTune"]) { // 延迟测试开始
-                NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
-                NSInteger count = [content ks_integerValueForKey:@"count"];
-                if (count == 0) {
-                    [self.delayArray removeAllObjects];
-                    self.checkIndex = 0;
-                }
-                self.checkIndex += 1;
-                [self configRecordManager];
-                [self postMessage:parm];
-                self.isCompareStart = NO;
-                self.isSoundCheckStart = NO;
-                self.isDelayCheckStart = YES;
-                [self startRecordService];
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"endTune"]) { // 延迟测试停止
-                
-                self.isDelayCheckStart = NO;
-                [self stopRecordService];
-                [self postMessage:parm];
-                [self stopMp3Player];
-                [self sendAdjustEndMessage]; // 发送结束消息
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"finishTune"]) { // 延迟测试结束
-                NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
-                
-                NSInteger errorCount = 0;
-                NSInteger totalDelay = 0;
-                for (NSInteger index = 0 ; index < self.delayArray.count; index++) {
-                    NSInteger micDelay = [self.delayArray[index] integerValue];
-                    if (micDelay > 10 && micDelay < 300) {
-                        totalDelay += micDelay;
-                    }
-                    else {
-                        errorCount++;
-                    }
-                }
-                NSMutableDictionary *contentParm = [NSMutableDictionary dictionaryWithDictionary:content];
-                if (errorCount > 1) { // 错误次数过多
-                    [contentParm setValue:@(NO) forKey:@"result"];
-                }
-                else {
-                    [contentParm setValue:@(YES) forKey:@"result"];
-                    NSInteger averageDelay = totalDelay / (self.delayArray.count - errorCount);
-                    UserDefaultSet([NSNumber numberWithDouble:averageDelay], @"micDelay");
-                }
-                NSMutableDictionary *sendParm =  [NSMutableDictionary dictionary];
-                [sendParm setValue:@"finishTune" forKey:@"api"];
-                [sendParm setValue:contentParm forKey:@"content"];
-                [self postMessage:sendParm];
-                [self.delayArray removeAllObjects];
-                self.checkIndex = 0;
-            }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"getDeviceDelay"]) {
-                NSInteger micDelay = [UserDefaultObjectForKey(@"micDelay") integerValue];
-                NSMutableDictionary *content = [NSMutableDictionary dictionaryWithDictionary:[parm ks_dictionaryValueForKey:@"content"]];
-                [content setValue:[NSNumber numberWithInteger:micDelay] forKey:@"value"];
-                NSMutableDictionary *sendParm = [NSMutableDictionary dictionary];
-                [sendParm setValue:@"getDeviceDelay" forKey:@"api"];
-                [sendParm setValue:content forKey:@"content"];
-                [self postMessage:sendParm];
-            }
-            else {
-                [super handleScriptMessageSource:parm];
-            }
-            
-        });
-    }
-}
-
-- (void)afterCheckCameraCheckAlbum:(PREMISSIONTYPE)cameraType parm:(NSDictionary *)sourceParm {
-    
-    [RecordCheckManager checkPhotoLibraryPremissionAvaiableCallback:^(PREMISSIONTYPE type) {
-        if (type == PREMISSIONTYPE_YES && cameraType == PREMISSIONTYPE_YES) {
-            self.isCameraOpen = YES;
-            [self.videoRecordManager setIgnoreAudio:YES];
-            [self.videoRecordManager configSessiondisplayInView:self.viewContainer];
-            [self postMessage:sourceParm];
-        }
-        else { //
-            
-            NSString *content = @"";
-            NSString *des = @"";;
-            if (cameraType == PREMISSIONTYPE_NO && type == PREMISSIONTYPE_NO) {
-                des = @"没有相机和相册访问权限";
-                content = @"请开启相机和相册访问权限";
-            }
-            else if (cameraType == PREMISSIONTYPE_NO && type == PREMISSIONTYPE_YES) {
-                des = @"没有相机访问权限";
-                content = @"请开启相机访问权限";
-            }
-            else if (cameraType == PREMISSIONTYPE_YES && type == PREMISSIONTYPE_NO) {
-                des = @"没有相册访问权限";
-                content = @"请开启相册访问权限";
-            }
-            [self responseMessage:@"storageUnable" desc:des parm:sourceParm];
-            [self showAlertWithMessage:content type:CHECKDEVICETYPE_CAMREA];
-
-        }
-    }];
-}
-
-- (void)responseMessage:(NSString *)reson desc:(NSString *)desc parm:(NSDictionary *)parm {
-    NSMutableDictionary *sendContent = [NSMutableDictionary dictionaryWithDictionary:[parm ks_dictionaryValueForKey:@"content"]];
-    [sendContent setValue:reson forKey:@"reson"];
-    [sendContent setValue:desc forKey:@"des"];
-    NSMutableDictionary *sendParm = [NSMutableDictionary dictionaryWithDictionary:parm];
-    [sendParm setValue:sendContent forKey:@"content"];
-    [self postMessage:sendParm];
-}
-
-- (void)musicPublishCallBack:(NSDictionary *)content {
-    NSMutableDictionary *parm = [NSMutableDictionary dictionary];
-    [parm setValue:@"hideComplexButton" forKey:@"api"];
-    [parm setValue:@{} forKey:@"content"];
-    [self postMessage:parm];
-}
-
-- (void)uploadVideoWithParm:(NSMutableDictionary *)contentParm sendParm:(NSMutableDictionary *)sendParm {
-//    MJWeakSelf;
-//    [self.videoRecordManager uploadRecordVideoSuccess:^(NSString * _Nonnull videoUrl) {
-//        [contentParm setValue:@"success" forKey:@"type"];
-//        [contentParm setValue:videoUrl forKey:@"filePath"];
-//        [contentParm setValue:@"上传成功" forKey:@"message"];
-//        [sendParm setValue:contentParm forKey:@"content"];
-//        
-//        [weakSelf postMessage:sendParm];
-//    } failure:^(NSString * _Nonnull desc) {
-//        [contentParm setValue:@"error" forKey:@"type"];
-//        [contentParm setValue:desc forKey:@"message"];
-//        [sendParm setValue:contentParm forKey:@"content"];
-//        [weakSelf postMessage:sendParm];
-//    }];
-}
-- (void)showAlertWithMessage:(NSString *)message type:(CHECKDEVICETYPE)deviceType {
-    [KSPremissionAlert shareInstanceDisplayImage:deviceType message:message showInView:self.view cancel:^{
-        
-    } confirm:^{
-        [self openSettingView];
-    }];
-    
-}
-
-- (void)openSettingView {
-    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
-}
-
-- (void)downloadUrl:(NSString *)url success:(void(^)(void))success faliure:(void(^)(void))faliure {
-    
-    [KSNetworkingManager downloadFileRequestWithFileUrl:url progress:^(int64_t bytesRead, int64_t totalBytes) {
-        
-    } success:^(NSURL * _Nonnull fileUrl) {
-        
-        if ([self saveAccompanyFileWithUrl:fileUrl accompanyUrl:url]) {
-            if (success) {
-                success();
-            }
-        }
-    } faliure:^(NSError * _Nonnull error) {
-        if (faliure) {
-            faliure();
-        }
-    }];
-}
-
-- (void)configVideoRecord:(NSURL *)path {
-    self.videoRecordManager.bgAudioUrl = path;
-    self.bgAudioUrl = path;
-}
-
-- (void)showBeatViewRepeatCount:(NSInteger)repeatCount supplement:(NSInteger)supplement {
-    KSCloudBeatView *beatView = [KSCloudBeatView shareInstanceWithBeatType:self.beatType speed:self.currentSpeed repeatCount:repeatCount supplement:supplement];
-    MJWeakSelf;
-    [beatView startPlayWithEndCallback:^(BOOL isCancle) {
-        if (isCancle) { // 取消
-            [weakSelf sendEndMetronomeMessage:YES];
-        }
-        else { // 播放完成
-            [weakSelf sendEndMetronomeMessage:NO];
-        }
-    }];
-    [self.view addSubview:beatView];
-}
-
-
-- (void)sendEndMetronomeMessage:(BOOL)isCancel {
-    NSString *status = isCancel ? @"cancel" : @"finish";
-    NSMutableDictionary *valueDic = [NSMutableDictionary dictionaryWithDictionary:[self.metronomeParm ks_dictionaryValueForKey:@"content"]];
-    [valueDic setValue:status forKey:@"status"];
-    NSMutableDictionary *sendParm = [NSMutableDictionary dictionaryWithDictionary:self.metronomeParm];
-    [sendParm setValue:valueDic forKey:@"content"];
-    [self postMessage:sendParm];
-}
-
-- (void)downloadMidiFile:(NSString *)midiUrl success:(void(^)(void))success faliure:(void(^)(void))faliure {
-    
-    [KSNetworkingManager downloadFileRequestWithFileUrl:midiUrl progress:^(int64_t bytesRead, int64_t totalBytes) {
-        
-    } success:^(NSURL * _Nonnull fileUrl) {
-
-        if ([self saveMidiFileWithUrl:fileUrl midiUrl:midiUrl]) {
-            if (success) {
-                success();
-            }
-        }
-    } faliure:^(NSError * _Nonnull error) {
-        if (faliure) {
-            faliure();
-        }
-    }];
-}
-
-- (BOOL)saveMidiFileWithUrl:(NSURL *)fileUrl midiUrl:(NSString *)midiUrl {
-    NSData *sourceData = [NSData dataWithContentsOfURL:fileUrl];
-    NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
-    NSString *filePath= [cachePath stringByAppendingPathComponent:@"MidiSong"];
-    // 先创建子目录
-    NSFileManager *fileManager = [NSFileManager defaultManager];
-    if (![fileManager fileExistsAtPath:filePath]) {
-        [fileManager createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:nil];
-    }else{
-        NSLog(@"已创建文件夹");
-    }
-    NSString *fileName = [midiUrl getUrlFileName];
-    NSString *tempPath = [filePath stringByAppendingPathComponent:fileName];
-    BOOL success = [sourceData writeToFile:tempPath atomically:NO];
-    return success;
-}
-
-- (NSString *)getFilePathWithName:(NSString *)fileName {
-    NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
-    NSString *filePath= [cachePath stringByAppendingPathComponent:@"MidiSong"];
-    return [filePath stringByAppendingPathComponent:fileName];;
-}
-
-
-
-- (BOOL)checkSongHasSaveWithSongUrl:(NSString *)songUrl {
-    BOOL hasSaveFile = NO;
-    NSString *fileName = [songUrl getUrlFileName];
-    NSString *filePath = [self getFilePathWithName:fileName];
-    
-    NSFileManager *fileManager = [NSFileManager defaultManager];
-    if ([fileManager fileExistsAtPath:filePath]) {
-        hasSaveFile = YES;
-    }
-    return hasSaveFile;
-}
-
-- (void)sendMessageToSocket:(NSDictionary *)parm {
-    NSString *messageHeader = @"proxyMessage";
-    NSString *sendMessage = [self configDataCommond:messageHeader body:[parm ks_dictionaryValueForKey:@"content"]];
-    [self sendDataToSocketService:sendMessage];
-}
-
-- (void)configAudioDeviceType:(NSDictionary *)parm {
-    AUDIODEVICE_TYPE type = [KSAQRecordManager queryAudioOutputDeviceType];
-    NSString *valueStr = @"";
-    BOOL checkIsWired = NO;
-    switch (type) {
-        case AUDIODEVICE_TYPE_HEADPHONE:
-        {
-            valueStr = @"有线耳机";
-            checkIsWired = YES;
-        }
-            break;
-        case AUDIODEVICE_TYPE_BLUETOOTH:
-        {
-            valueStr = @"蓝牙耳机";
-            checkIsWired = YES;
-        }
-            break;
-        case AUDIODEVICE_TYPE_NONE:
-        {
-            valueStr = @"";
-            checkIsWired = NO;
-        }
-            break;
-        default:
-            break;
-    }
-    NSMutableDictionary *valueDic = [NSMutableDictionary dictionaryWithDictionary:[parm ks_dictionaryValueForKey:@"content"]];
-    [valueDic setValue:valueStr forKey:@"type"];
-    [valueDic setValue:[NSNumber numberWithBool:checkIsWired] forKey:@"checkIsWired"];
-    NSMutableDictionary *sendParm = [NSMutableDictionary dictionaryWithDictionary:parm];
-    [sendParm setValue:valueDic forKey:@"content"];
-    [self postMessage:sendParm];
-}
-
-- (void)startRecordService {
-    if (self.AQManager.isRunning) {
-        [self.AQManager stopRecord];
-    }
-    [self.AQManager startRecord];
-}
-
-- (void)stopRecordService {
-    if (self.AQManager.isRunning) {
-        [self.AQManager stopRecord];
-    }
-}
-
-- (void)puaseRecordService {
-    if (self.AQManager.isRunning) {
-        [self.AQManager pauserRecord];
-    }
-}
-
-- (void)resumeRecordService {
-    [self.AQManager resumeRecord];
-}
-
-- (void)sendEndMessage {
-    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
-        // 上传停止的信息 发送给服务端
-        NSString *endMessage = @"recordEnd";
-        NSString *endData = [self configDataCommond:endMessage body:nil type:@"SOUND_COMPARE"];
-        [self sendDataToSocketService:endData];
-        self.isCompareStart = NO;
-        NSLog(@"---- send end message");
-    });
-}
-
-- (void)sendAdjustEndMessage {
-    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
-        // 上传停止的信息 发送给服务端
-        NSString *endMessage = @"recordEnd";
-        NSString *endData = [self configDataCommond:endMessage body:nil type:@"DELAY_CHECK"];
-        [self sendDataToSocketService:endData];
-        self.isDelayCheckStart = NO;
-        NSLog(@"---- send adjust end message");
-    });
-}
-
-- (void)sendOffsetTimeToService {
-    // 上传停止的信息 发送给服务端
-    NSString *offsetMessage = @"audioPlayStart";
-    NSTimeInterval micDelay = [UserDefault(@"micDelay") doubleValue];
-    NSDictionary *dic = @{@"offsetTime" : [NSNumber numberWithInteger:self.offsetTime], @"micDelay": [NSNumber numberWithInteger:micDelay]};
-    NSString *endData = [self configDataCommond:offsetMessage body:dic type:@"SOUND_COMPARE"];
-    NSLog(@"------ %@", endData);
-    [self sendDataToSocketService:endData];
-    self.isCompareStart = NO;
-}
-
-#pragma mark-------- KSAQRecordManagerDelegate
-- (void)recordInterruption {
-    NSDictionary *postParm = @{@"api" : @"cancelEvaluating",
-                               @"content" : @{@"reson":@"录制错误,请重试"}
-    };
-    [self postMessage:postParm];
-}
-
-
-
-- (void)audioRouteChange:(AUDIODEVICE_TYPE)type {
-    NSString *valueStr = @"";
-    BOOL checkIsWired = NO;
-    switch (type) {
-        case AUDIODEVICE_TYPE_HEADPHONE:
-        {
-            valueStr = @"有线耳机";
-            checkIsWired = YES;
-        }
-            break;
-        case AUDIODEVICE_TYPE_BLUETOOTH:
-        {
-            valueStr = @"蓝牙耳机";
-            checkIsWired = YES;
-        }
-            break;
-        case AUDIODEVICE_TYPE_NONE:
-        {
-            valueStr = @"";
-            checkIsWired = NO;
-        }
-            break;
-        default:
-            break;
-    }
-    
-    NSDictionary *postParm = @{@"api" : @"listenerWiredStatus",
-                               @"content" : @{@"type":valueStr,
-                                              @"checkIsWired":[NSNumber numberWithBool:checkIsWired]
-                               }
-    };
-    [self postMessage:postParm];
-}
-
-#pragma mark ------- 评测app播放
-
-- (void)recordDidStart:(NSTimeInterval)time { // ms
-    self.hasRecordMusicOffset = NO;
-    self.recordStartTime = time;
-    if (self.isDelayCheckStart) {
-        NSLog(@"---- delay - record did start %f", time);
-        // 播放音频
-        // 播放校音音频
-        dispatch_main_sync_safe(^{
-            [self.delayCheckPlayer seekToTimePlay:0];
-        });
-    }
-    else if (self.isCompareStart) {
-        NSLog(@"---- compare - record did start %f", time);
-        if (self.playerEngine == nil && _musicPlayer) {
-            dispatch_main_sync_safe(^{
-                self.musicPlayer.isMute = self.muteAccompany;
-                self.musicPlayer.rate = self.musicSpeed;
-                // 进度跳转
-                [self.musicPlayer seekToTimePlay:self.musicStartTime];
-            });
-        }
-    }
-}
-
-
-- (void)audioRecord:(KSAQRecordManager *)audioRecord didRecordAudioData:(void *)data length:(UInt32)length {
-    
-    if (self.socketManager.socketReadyState != SR_OPEN) {
-        return;
-    }
-    NSData *pushData = [[NSData alloc] initWithBytes:data length:length];
-    if (self.isCompareStart) { // 发送评测开始消息
-        dispatch_async(dispatch_get_main_queue(), ^{
-            NSDate *date = [NSDate date];
-            NSTimeInterval inteveral = [date timeIntervalSince1970];
-            double beginTime = inteveral - audioRecord.sampleTime;
-            NSDictionary *parm = @{
-                @"api" : @"recordStartTime",
-                @"content" : @{@"inteveral" : [NSNumber numberWithDouble:beginTime]}
-            };
-            [self postMessage:parm];
-        });
-        
-        NSLog(@"--------- send start message");
-        _isCompareStart = NO;
-        NSString *startMessage = @"recordStart";
-        NSString *startString = [self configDataCommond:startMessage body:nil type:@"SOUND_COMPARE"];
-        [self sendDataToSocketService:startString];
-    }
-    else if (self.isSoundCheckStart) { // 校音开始
-        
-        NSLog(@"--------- send check start message");
-        _isSoundCheckStart = NO;
-        NSString *checkStartMessage = @"start";
-        NSString *startString = [self configDataCommond:checkStartMessage body:nil type:@"PITCH_DETECTION"];
-        [self sendDataToSocketService:startString];
-    }
-    else if (self.isDelayCheckStart) {
-        NSLog(@"--------- send delay check start message");
-        _isDelayCheckStart = NO;
-        NSString *checkStartMessage = @"recordStart";
-        NSInteger frequence = self.checkPrequence;
-        NSDictionary *parm = @{@"HZ" : @(frequence)};
-        NSString *startString = [self configDataCommond:checkStartMessage body:parm type:@"DELAY_CHECK"];
-        [self sendDataToSocketService:startString];
-    }
-    //        NSLog(@"--------- send audio data length %d", length);
-    [self sendDataToSocketService:pushData];
-}
-
-- (NSString *)configDataCommond:(NSString *)commond body:(id)bodyMessage type:(NSString *)dataType {
-    NSMutableDictionary *parm = [NSMutableDictionary dictionary];
-    if (bodyMessage) {
-        [parm setValue:bodyMessage forKey:@"body"];
-    }
-    NSMutableDictionary *headerParm = [NSMutableDictionary dictionary];
-    if ([NSString isEmptyString:commond]) {
-        [headerParm setValue:@"" forKey:@"commond"];
-    }
-    else {
-        [headerParm setValue:commond forKey:@"commond"];
-    }
-    if (![NSString isEmptyString:dataType]) {
-        [headerParm setValue:dataType forKey:@"type"];
-    }
-    [headerParm setValue:@(200) forKey:@"status"];
-    [parm setValue:headerParm forKey:@"header"];
-    return [parm mj_JSONString];
-}
-
-
-- (NSString *)configDataCommond:(NSString *)commond body:(id)bodyMessage {
-    NSMutableDictionary *parm = [NSMutableDictionary dictionary];
-    if (bodyMessage) {
-        [parm setValue:bodyMessage forKey:@"body"];
-    }
-    if ([NSString isEmptyString:commond]) {
-        [parm setValue:@{@"commond":@""} forKey:@"header"];
-    }
-    else {
-        [parm setValue:@{@"commond":commond} forKey:@"header"];
-    }
-    return [parm mj_JSONString];
-}
-
-- (KSTargetWebSocketManager *)socketManager {
-    if (!_socketManager) {
-        _socketManager = [[KSTargetWebSocketManager alloc] init];
-    }
-    return _socketManager;
-}
-
-#pragma mark --- lazying
-- (UIView *)viewContainer {
-    if (!_viewContainer) {
-        _viewContainer = [[UIView alloc] init];
-    }
-    return _viewContainer;
-}
-
-- (KSVideoRecordManager *)videoRecordManager {
-    if (!_videoRecordManager) {
-        MJWeakSelf;
-        _videoRecordManager = [[KSVideoRecordManager alloc] initSessionRecordCallback:^(BOOL isSuccess, NSString * _Nullable message) {
-            if (isSuccess) {
-                [weakSelf showSuccessMessage:message];
-            }
-            else {
-                if (![NSString isEmptyString:message]) {
-                    [LOADING_MANAGER MBShowAUTOHidingInWindow:message];
-                }
-                
-            }
-        }];
-        [_videoRecordManager errorMessageCallback:^(NSDictionary * _Nonnull errorParm) {
-            [weakSelf uploadVideoRecordErrorMessage:errorParm];
-        }];
-    }
-    return _videoRecordManager;
-}
-
-- (void)uploadVideoRecordErrorMessage:(NSDictionary *)errorParm {
-    NSMutableDictionary *parm = [NSMutableDictionary dictionaryWithDictionary:errorParm];
-    [parm setValue:UserDefault(UIDKey) forKey:@"userId"];
-    [parm setValue:[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"] forKey:@"version"];
-    NSString *content = [parm mj_JSONString];
-    NSMutableDictionary *submitParm = [KSLogManager generateLogMessageWithContent:content type:@"ERROR"];
-    NSMutableArray *uploadArray = [NSMutableArray arrayWithObject:submitParm];
-    [KSNetworkingManager sysExceptionLogUpdate:KS_POST token:UserDefault(TokenKey) logArray:uploadArray success:^(NSDictionary * _Nonnull dic) {
-        if ([dic ks_integerValueForKey:@"code"] == 200) {
-            
-        }
-    } faliure:^(NSError * _Nonnull error) {
-        
-    }];
-}
-
-- (void)showSuccessMessage:(NSString *)message {
-    if (![NSString isEmptyString:message]) {
-        [LOADING_MANAGER MBShowAUTOHidingInWindow:message];
-    }
-    // 成功
-    if (self.endRecordParm) {
-        [self postMessage:self.endRecordParm];
-        self.endRecordParm = nil;
-    }
-}
-
-- (MetronomeType)getBeatTypeFromString:(NSString *)typeString {
-    if ([typeString isEqualToString:@"1/4"]) {
-        return MetronomeType1V4;
-    }
-    else if ([typeString isEqualToString:@"2/4"]) {
-        return MetronomeType2V4;
-    }
-    else if ([typeString isEqualToString:@"3/4"]) {
-        return MetronomeType3V4;
-    }
-    else if ([typeString isEqualToString:@"4/4"]) {
-        return MetronomeType4V4;
-    }
-    else if ([typeString isEqualToString:@"3/8"]) {
-        return MetronomeType3V8;
-    }
-    else if ([typeString isEqualToString:@"6/8"]) {
-        return MetronomeType6V8;
-    }
-    else {
-        return MetronomeType4V4;
-    }
-}
-
-
-#pragma mark ------- midi 播放相关
-- (void)configPlayerEngineWithSong:(NSString *)songPath reportTime:(NSInteger)reportTime {
-    self.initEngineSuccess = NO;
-    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
-        self.playerEngine = [[MidiPlayerEngine alloc] init];
-        self.playerEngine.reportTime = reportTime;
-        self.playerEngine.delegate = self;
-        [self.playerEngine configSoundFilePath:[[NSBundle mainBundle]
-                                                pathForResource:@"synthgms" ofType:@"sf2"]];
-        [self.playerEngine loadMIDIFileWithString:songPath];
-    });
-}
-
-- (void)configAudioSession {
-    self.audioSessionManager = [[KSAudioSessionManager alloc] init];
-    self.audioSessionManager.delegate = self;
-    [self.audioSessionManager configAudioSession:AUDIOCONFIG_PLAYANDRECORD];
-}
-
-#pragma mark ---- PlayerEngineDelegate
-/** 进度更新
- api: 'cloudTimeUpdae',
- content: {
- // 当前曲目id
- songID: 0,
- // 当前位置时间(ms)
- currentTime: 0
- }
- */
-/** 播放结束事件
- api: 'cloudplayed',
- content: {
- // 当前曲目id
- songID: 0,
- }
- */
-- (void)ProgressUpdated:(float)progress currentPlayTime:(MusicTimeStamp)currentPlayTime currentTime:(NSTimeInterval)currentTime {
-    
-    if (self.isPlaying == NO) {
-        return;
-    }
-    // 回调
-    NSMutableDictionary *sendParm = [NSMutableDictionary dictionary];
-    [sendParm setValue:@"cloudTimeUpdae" forKey:@"api"];
-    NSMutableDictionary *content = [NSMutableDictionary dictionary];
-    [content setValue:self.currentSongId forKey:@"songID"];
-    [content setValue:@(currentPlayTime*1000) forKey:@"currentTime"];
-    [sendParm setValue:content forKey:@"content"];
-    [self postMessage:sendParm];
-    
-}
-
-
-- (void)playEnd {
-    [self stopPlayAction];
-    NSMutableDictionary *sendParm = [NSMutableDictionary dictionary];
-    [sendParm setValue:@"cloudplayed" forKey:@"api"];
-    NSMutableDictionary *content = [NSMutableDictionary dictionary];
-    [content setValue:self.currentSongId forKey:@"songID"];
-    [sendParm setValue:content forKey:@"content"];
-    [self postMessage:sendParm];
-}
-
-- (void)initPlayerEngineSuccess:(float)totalTime {
-    self.initEngineSuccess = YES;
-    if (self.configEngineParm) {
-        NSMutableDictionary *sendParm = [NSMutableDictionary dictionaryWithDictionary:self.configEngineParm];
-        NSMutableDictionary *content = [NSMutableDictionary dictionaryWithDictionary:[sendParm ks_dictionaryValueForKey:@"content"]];
-        [content setValue:@(totalTime*1000) forKey:@"midiDuration"];
-        [sendParm setValue:content forKey:@"content"];
-        [self postMessage:sendParm];
-        self.configEngineParm = nil;
-    }
-}
-
-// 总时长
-- (void)GetMusicTotalTime:(float)time {
-    
-}
-
-
-#pragma mark ----- 播放控制
-- (void)playAction {
-    if (self.playerEngine) {
-        self.isPlaying = YES;
-        [self.playerEngine playMIDIFile];
-    }
-}
-
-- (void)stopPlayAction {
-    if (self.playerEngine) {
-        self.isPlaying = NO;
-        if ([self.playerEngine isPlayingFile]) {
-            [self.playerEngine stopPlayingMIDIFile];
-        }
-    }
-}
-
-
-#pragma mark ----- 小酷AI loading
-- (AccompanyLoadingView *)loadingView {
-    if (!_loadingView) {
-        _loadingView = [AccompanyLoadingView shareInstance];
-        MJWeakSelf;
-        [_loadingView loadingCallback:^{
-            [weakSelf backAction];
-        }];
-    }
-    return _loadingView;
-}
-
-- (void)showCustomLoading {
-    if ([self.view.subviews containsObject:self.loadingView]) {
-        return;
-    }
-    [self.view addSubview:self.loadingView];
-    [self.loadingView mas_makeConstraints:^(MASConstraintMaker *make) {
-        make.left.top.right.bottom.mas_equalTo(self.view);
-    }];
-    [self.view bringSubviewToFront:self.loadingView];
-    [self.loadingView showLoading];
-}
-
-- (void)removeCustomLoadingView {
-    [self.loadingView stopLoading];
-}
-
-#pragma mark ----- 跟音模块
-- (void)startTuner {
-    @try {
-        if (self.isTunerRuning == NO) {
-            self.isTunerRuning = YES;
-            [self.tuner start];
-        }
-    } @catch (NSException *exception) {
-        NSLog(@"----- exception --- %@", exception);
-    } @finally {
-        
-    }
-}
-
-- (void)stopTuner {
-    if (self.isTunerRuning) {
-        self.isTunerRuning = NO;
-        [self.tuner stop];
-    }
-}
-
-- (Tuner *)tuner {
-    if (!_tuner) {
-        _tuner = [[Tuner alloc] initWithThreshold:0 smoothing:0.25];
-        _tuner.delegate = self;
-    }
-    return _tuner;
-}
-
-- (void)tunerDidUpdate:(Tuner *)tuner output:(TunerOutput *)output {
-    if (output.amplitude < 0.01) {
-        
-    }
-    else {
-        // 回调频率
-        NSDictionary *parm = @{
-            @"api" : @"cloudFollowTime",
-            @"content" : @{@"frequency" : [NSNumber numberWithDouble:output.frequency]}
-        };
-        [self postMessage:parm];
-    }
-    NSLog(@"-------- %@%zd --- distance :%f frequence : %f" , output.pitch, output.octave, output.distance, output.frequency);
-    
-}
-#pragma mark ---- 保存伴奏
-- (BOOL)saveAccompanyFileWithUrl:(NSURL *)fileUrl accompanyUrl:(NSString *)accompanyUrl {
-    NSData *sourceData = [NSData dataWithContentsOfURL:fileUrl];
-    NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
-    NSString *filePath= [cachePath stringByAppendingPathComponent:@"AccompanySong"];
-    // 先创建子目录
-    NSFileManager *fileManager = [NSFileManager defaultManager];
-    if (![fileManager fileExistsAtPath:filePath]) {
-        [fileManager createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:nil];
-    }else{
-        NSLog(@"已创建文件夹");
-    }
-    NSString *fileName = [accompanyUrl getUrlFileName];
-    NSString *tempPath = [filePath stringByAppendingPathComponent:fileName];
-    BOOL success = [sourceData writeToFile:tempPath atomically:NO];
-    return success;
-}
-- (NSString *)getAccompanyFilePathWithName:(NSString *)fileName {
-    NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
-    NSString *filePath= [cachePath stringByAppendingPathComponent:@"AccompanySong"];
-    return [filePath stringByAppendingPathComponent:fileName];;
-}
-
-- (BOOL)checkSongHasSaveAccompanyWithSongUrl:(NSString *)songUrl {
-    BOOL hasSaveFile = NO;
-    NSString *fileName = [songUrl getUrlFileName];
-    NSString *filePath = [self getAccompanyFilePathWithName:fileName];
-    
-    NSFileManager *fileManager = [NSFileManager defaultManager];
-    if ([fileManager fileExistsAtPath:filePath]) {
-        hasSaveFile = YES;
-    }
-    return hasSaveFile;
-}
-
-#pragma mark ------- Audio Engine player
-- (AudioEnginePlayer *)delayCheckPlayer {
-    if (!_delayCheckPlayer) {
-        _delayCheckPlayer = [[AudioEnginePlayer alloc] init];
-        _delayCheckPlayer.delegate = self;
-    }
-    return _delayCheckPlayer;
-}
-- (AudioEnginePlayer *)musicPlayer {
-    if (!_musicPlayer) {
-        _musicPlayer = [[AudioEnginePlayer alloc] init];
-        _musicPlayer.delegate = self;
-    }
-    return _musicPlayer;
-}
-
-#pragma mark ---- Audio Engine player delegate
-- (void)enginePlayerIsReadyPlay:(AudioEnginePlayer *)player {
-    if (player == self.delayCheckPlayer) {
-        self.checkPlayerReady = YES;
-    }
-    else if (player == self.musicPlayer) {
-        self.musicPlayerReady = YES;
-    }
-    // 如果都准备好
-    if (self.musicPlayerReady && self.checkPlayerReady) {
-         [self sendPlayerReadyMsg];
-    }
-}
-
-// 播放进度
-- (void)updatePlayProgress:(NSInteger)playTime andTotalTime:(NSInteger)totalTime andProgress:(CGFloat)progress currentInterval:(NSTimeInterval)currentInterval inPlayer:(AudioEnginePlayer *)player {
-    if (player == self.delayCheckPlayer) {
-        if (playTime >= 300 && self.recordStartTime > 0 && self.hasRecordMusicOffset == NO) {
-            self.hasRecordMusicOffset = YES;
-            NSLog(@" --- check player start play time %f", currentInterval - playTime);
-            self.playerStartTime = currentInterval - playTime;
-            self.offsetTime = self.playerStartTime - self.recordStartTime;
-            NSLog(@"--------- check player offset time -- %zd", self.offsetTime);
-        }
-    }
-    // 如果未记录延迟
-    if (playTime >= (self.musicStartTime + 300) && player == self.musicPlayer && self.hasRecordMusicOffset == NO) {
-        if (self.recordStartTime > 0) {
-            self.hasRecordMusicOffset = YES;
-            NSInteger newPlayTime = (playTime - self.musicStartTime) / player.rate; // 选段
-            NSLog(@" --- music player start play time %f", currentInterval - newPlayTime);
-            self.playerStartTime = currentInterval - newPlayTime;
-            self.offsetTime = self.playerStartTime - self.recordStartTime;
-            NSLog(@"--------- music play offset time -- %zd", self.offsetTime);
-            [self sendOffsetTimeToService];
-        }
-        NSLog(@"------- record start time %f", self.recordStartTime);
-   }
-    
-    // 回调进度
-    if (player == self.musicPlayer) {
-//          NSLog(@"------ music play progress - %f", progress);
-         // 回调进度
-         NSDictionary *parm = @{
-              @"api" : @"playProgress",
-              @"content" : @{@"currentTime" : [NSNumber numberWithInteger:playTime],
-                             @"totalDuration" : [NSNumber numberWithInteger:totalTime],
-              }
-         };
-//          NSLog(@" -----music play progress  %@---- ", parm);
-         [self postMessage:parm];
-    }
-    
-}
-// 错误
-- (void)enginePlayerDidError:(AudioEnginePlayer *)player error:(NSError *)error {
-    [self stopRecordService];
-    [self stopMp3Player]; // 停止播放
-    // 播放出现问题
-    NSDictionary *postParm = @{@"api" : @"cancelEvaluating",
-                               @"content" : @{@"reson":@"播放已停止"}
-    };
-    [self postMessage:postParm];
-    
-    if (error) {
-        NSLog(@"-- error desc - %@", error.description);
-        NSMutableDictionary *parm = [NSMutableDictionary dictionary];
-        [parm setValue:UserDefault(UIDKey) forKey:@"userId"];
-        [parm setValue:@"KSCloudWebViewController_MP3Player" forKey:@"Location"];
-        [parm setValue:@(error.code) forKey:@"errorCode"];
-        [parm setValue:error.description forKey:@"errorDesc"];
-        [parm setValue:[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"] forKey:@"version"];
-        NSString *content = [parm mj_JSONString];
-        NSMutableDictionary *submitParm = [KSLogManager generateLogMessageWithContent:content type:@"ERROR"];
-        NSMutableArray *uploadArray = [NSMutableArray arrayWithObject:submitParm];
-        [KSNetworkingManager sysExceptionLogUpdate:KS_POST token:UserDefault(TokenKey) logArray:uploadArray success:^(NSDictionary * _Nonnull dic) {
-            if ([dic ks_integerValueForKey:@"code"] == 200) {
-                
-            }
-        } faliure:^(NSError * _Nonnull error) {
-            
-        }];
-    }
-}
-
-
-- (void)sendPlayerReadyMsg {
-     if (self.playerParm) {
-          [self postMessage:self.playerParm];
-     }
-}
-
-- (NSMutableArray *)delayArray {
-    if (!_delayArray) {
-        _delayArray = [NSMutableArray array];
-    }
-    return _delayArray;
-}
-
-/*
- #pragma mark - Navigation
- 
- // In a storyboard-based application, you will often want to do a little preparation before navigation
- - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
- // Get the new view controller using [segue destinationViewController].
- // Pass the selected object to the new view controller.
- }
- */
-
-@end

+ 216 - 81
KulexiuForStudent/KulexiuForStudent/Common/Base/AccompanyWebView/KSCloudWebManager.m

@@ -6,16 +6,18 @@
 //
 
 #import "KSCloudWebManager.h"
-#import <CloudWebConfig.h>
-#import <KSCloudWebViewController.h>
-#import <RecordCheckManager.h>
-#import "KSPremissionAlert.h"
+#import <KSCloudAccompanyLibrary/CloudWebConfig.h>
+#import <KSCloudAccompanyLibrary/KSCloudWebViewController.h>
+#import <KSToolsLibrary/RecordCheckManager.h>
+#import "KSCloudPremissionAlertView.h"
 #import "KSBaseWKWebViewController.h"
-#import "KSMediaMergeView.h"
-#import <UIDevice+TFDevice.h>
+#import "KSAccompanyDraftViewController.h"
+#import <KSToolsLibrary/UIDevice+TFDevice.h>
 #import "AppDelegate+AppService.h"
 #import "AccompanyLoadingView.h"
 #import "KSWebLoadRefreshView.h"
+#import "KSLogManager.h"
+#import "KSDelayCheckView.h"
 
 @interface KSCloudWebManager ()<KSCloudWebViewControllerDelegate>
 
@@ -23,6 +25,8 @@
 
 @property (nonatomic, strong) KSWebLoadRefreshView *errorView;
 
+@property (nonatomic, strong) KSDelayCheckView *checkView;
+
 @end
 
 @implementation KSCloudWebManager
@@ -37,6 +41,17 @@
     return manager;
 }
 
+- (void)clearMicDelay {
+    UserDefaultRemoveObjectForKey(@"micDelay");
+}
+
+- (instancetype)init {
+    self = [super init];
+    if (self) {
+        [self clearMicDelay];
+    }
+    return self;
+}
 
 - (void)configCloudWebSource {
     CLOUD_CONFIG.scrpit_name = SCRIPT_NAME;
@@ -48,63 +63,32 @@
     CLOUD_CONFIG.socket_host = SOCKET_URL;
     CLOUD_CONFIG.authFilePath = CERT_PATH;
     CLOUD_CONFIG.need_auth = SSL_AUTH;
-    CLOUD_CONFIG.themeColor = CLIENT_THEMECOLOR;
+    CLOUD_CONFIG.themeColor = THEMECOLOR;
     CLOUD_CONFIG.soundFilePath = [[NSBundle mainBundle] pathForResource:@"synthgms" ofType:@"sf2"];
 }
 
 - (void)showWebView:(NSDictionary *)parm fromController:(CustomNavViewController *)navCtrl {
-    // 先检测相机 再检测麦克风
-    [RecordCheckManager checkCameraPremissionAvaiableCallback:^(PREMISSIONTYPE type) {
-        
-        [self afterCheckCameraCheckMic:type parm:parm fromController:navCtrl];
-    }];
-}
-
-- (void)afterCheckCameraCheckMic:(PREMISSIONTYPE)cameraType parm:(NSDictionary *)sourceParm fromController:(CustomNavViewController *)navCtrl {
-    
-    [RecordCheckManager checkMicPermissionAvaiableCallback:^(PREMISSIONTYPE type) {
-        if (type == PREMISSIONTYPE_YES && cameraType == PREMISSIONTYPE_YES) {
-            NSDictionary *valueDic = [sourceParm ks_dictionaryValueForKey:@"content"];
-            
-            KSCloudWebViewController *ctrl = [[KSCloudWebViewController alloc] init];
-            ctrl.webViewDelegate = self;
-            ctrl.url = [valueDic ks_stringValueForKey:@"url"];
-            ctrl.parmDic = valueDic;
-            NSInteger orientation = [valueDic ks_integerValueForKey:@"orientation"];
-            BOOL isLandScape = orientation == 0 ? YES : NO;
-            ctrl.ks_landScape = isLandScape;
-            [navCtrl pushViewController:ctrl animated:YES];
-        }
-        else { //
-            
-            NSString *content = @"";
-            CHECKDEVICETYPE checkType = CHECKDEVICETYPE_BOTH;
-            if (cameraType == PREMISSIONTYPE_NO && type == PREMISSIONTYPE_NO) {
-                content = @"请开启相机和麦克风访问权限";
-                checkType = CHECKDEVICETYPE_BOTH;
-            }
-            else if (cameraType == PREMISSIONTYPE_NO && type == PREMISSIONTYPE_YES) {
-                content = @"请开启相机访问权限";
-                checkType = CHECKDEVICETYPE_CAMREA;
-            }
-            else if (cameraType == PREMISSIONTYPE_YES && type == PREMISSIONTYPE_NO) {
-                content = @"请开启麦克风访问权限";
-                checkType = CHECKDEVICETYPE_MIC;
-            }
-            [self showAlertWithMessage:content type:checkType];
-
-        }
-    }];
+    KSCloudWebViewController *ctrl = [[KSCloudWebViewController alloc] init];
+    ctrl.webViewDelegate = self;
+#pragma mark ----- 拼接token
+    NSString *url = [parm ks_stringValueForKey:@"url"];
+    NSString *sepectString = [url containsString:@"?"] ? @"&" : @"?";
+    NSString *tokenStr = UserDefault(TokenKey);
+    if (![NSString isEmptyString:tokenStr]) {
+        NSString *token = [[NSString stringWithFormat:@"Authorization=%@ %@", UserDefault(Token_type), tokenStr] stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
+        url = [NSString stringWithFormat:@"%@%@%@",url, sepectString, token];
+    }
+    ctrl.url = url;
+#pragma mark --------
+//    ctrl.url = [parm ks_stringValueForKey:@"url"];
+    ctrl.parmDic = parm;
+    NSInteger orientation = [parm ks_integerValueForKey:@"orientation"];
+    BOOL isLandScape = orientation == 0 ? YES : NO;
+    ctrl.ks_landScape = isLandScape;
+    [ctrl showCustomLoading];
+    [navCtrl pushViewController:ctrl animated:YES];
 }
 
-- (void)showAlertWithMessage:(NSString *)message type:(CHECKDEVICETYPE)deviceType {
-    [KSPremissionAlert shareInstanceDisplayImage:deviceType message:message showInView:[NSObject getKeyWindow] cancel:^{
-        
-    } confirm:^{
-        [self openSettingView];
-    }];
-    
-}
 
 - (void)openSettingView {
     [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
@@ -112,7 +96,7 @@
 
 // 回调权限
 - (void)NotiferShowPremissAlert:(NSString *)message {
-    [KSPremissionAlert shareInstanceDisplayImage:CHECKDEVICETYPE_BOTH message:message showInView:[NSObject getKeyWindow] cancel:^{
+    [KSCloudPremissionAlertView configDescMessage:message cancel:^{
         
     } confirm:^{
         [self openSettingView];
@@ -134,17 +118,9 @@
     }];
 }
 
-- (void)removeLoadHud {
-    [LOADING_MANAGER removeCustomLoading];
-}
-
 // 上传
 - (void)uploadFile:(NSString *)uploadFileUrl success:(void (^)(NSString * _Nonnull))success faliure:(void (^)(NSString * _Nonnull))faliure {
-    [LOADING_MANAGER showCancelCustomLoading:@"上传中..." cancel:^{
-        [UPLOAD_MANAGER cancelUploadCallback:^{
-            [self removeLoadHud];
-        }];
-    }];
+    [LOADING_MANAGER showCustomLoading:@"上传中..."];
     NSData *fileData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:uploadFileUrl]];
     NSString *suffix = [NSString stringWithFormat:@".%@",[uploadFileUrl pathExtension]];
     [UPLOAD_MANAGER configWithfilePath:@"/user/"];
@@ -165,6 +141,23 @@
         faliure(descMessaeg);
     }];
 }
+
+// 错误上报
+- (void)cloudPageOccourError:(NSMutableDictionary *)uploadParm {
+    [uploadParm setValue:[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"] forKey:@"version"];
+    NSString *content = [uploadParm mj_JSONString];
+    NSMutableDictionary *submitParm = [KSLogManager generateLogMessageWithContent:content type:@"ERROR"];
+    NSMutableArray *uploadArray = [NSMutableArray arrayWithObject:submitParm];
+    [KSNetworkingManager sysExceptionLogUpdate:KS_POST token:UserDefault(TokenKey) logArray:uploadArray success:^(NSDictionary * _Nonnull dic) {
+        if ([dic ks_integerValueForKey:@"code"] == 200) {
+            
+        }
+    } faliure:^(NSError * _Nonnull error) {
+        
+    }];
+}
+
+
 #pragma mark ------- KSCloudWebViewControllerDelegate
 
 - (void)openWebViewWithParm:(NSDictionary *)parm inNavController:(UINavigationController *)navController {
@@ -175,21 +168,47 @@
     BOOL isLandScape = orientation == 0 ? YES : NO;
     detailCtrl.ks_landScape = isLandScape;
 
-//    if ([parm ks_boolValueForKey:@"showLoadingAnim"]) {
-//        [detailCtrl showCoursewareLoading];
-//    }
+    if ([parm ks_boolValueForKey:@"showLoadingAnim"]) {
+        [detailCtrl showCustomLoading];
+    }
     [navController pushViewController:detailCtrl animated:YES];
 }
 
 - (void)showMergeViewWithParm:(NSDictionary *)parm displayInView:(UIView *)displayView callBackController:(KSCloudWebViewController *)controller {
-    KSMediaMergeView *mergeView = [[KSMediaMergeView alloc] init];
-    [displayView addSubview:mergeView];
-    [mergeView mas_makeConstraints:^(MASConstraintMaker *make) {
-        make.left.right.top.bottom.mas_equalTo(displayView);
-    }];
-    mergeView.recordId = [parm ks_stringValueForKey:@"recordId"];
-    mergeView.songName = [parm ks_stringValueForKey:@"title"];
-    mergeView.coverImage = [parm ks_stringValueForKey:@"coverImg"];
+    KSAccompanyDraftViewController *ctrl = [[KSAccompanyDraftViewController alloc] init];
+    ctrl.ks_landScape = YES;
+    ctrl.recordId = [parm ks_stringValueForKey:@"recordId"];
+    ctrl.songName = [parm ks_stringValueForKey:@"title"];
+    ctrl.coverImage = [parm ks_stringValueForKey:@"coverImg"];
+    if ([[parm allKeys] containsObject:@"speedRate"]) {
+        ctrl.musicSpeed = [parm ks_floatValueForKey:@"speedRate"];
+    }
+    else {
+        ctrl.musicSpeed = 1.0f;
+    }
+    if ([[parm allKeys] containsObject:@"speedRate"]) {
+        ctrl.musicSpeed = [parm ks_floatValueForKey:@"speedRate"];
+    }
+    else {
+        ctrl.musicSpeed = 1.0f;
+    }
+    
+    NSString *musicSheetId = [parm ks_stringValueForKey:@"musicSheetId"];
+    ctrl.musicSheetId = musicSheetId;
+    
+    if ([[parm allKeys] containsObject:@"musicRenderType"]) {
+        ctrl.musicRenderType = [parm ks_stringValueForKey:@"musicRenderType"];
+    }
+    else {
+        ctrl.musicRenderType = @"staff";
+    }
+    
+    if ([[parm allKeys] containsObject:@"part-index"]) {
+        ctrl.partIndex = [parm ks_integerValueForKey:@"part-index"];
+    }
+    else {
+        ctrl.partIndex = 0;
+    }
     MJWeakSelf;
     NSInteger defaultDelay = [parm ks_integerValueForKey:@"defaultDelay"];
     NSURL *videoUrl = [parm valueForKey:@"videoUrl"];
@@ -197,11 +216,20 @@
     NSURL *recordUrl = [parm valueForKey:@"recordUrl"];
     NSString *accompanyUrl = [parm ks_stringValueForKey:@"remoteBgUrl"];
     
-    [mergeView configWithVideoUrl:videoUrl bgAudioUrl:bgAudioUrl remoteBgUrl:accompanyUrl  recordUrl:recordUrl offsetTime:defaultDelay mergeCallback:^(BOOL isPublished) {
-        if (isPublished) {
+    [ctrl configWithVideoUrl:videoUrl bgAudioUrl:bgAudioUrl remoteBgUrl:accompanyUrl recordUrl:recordUrl offsetTime:defaultDelay mergeCallback:^(MERGEBACK backType) {
+        if (backType == MERGEBACK_RETRY) {
+            [weakSelf callbackRetryEvaluating:controller];
+        }
+        else if (backType == MERGEBACK_PUBLISH) {
             [weakSelf callBackMergeSuccess:controller];
         }
     }];
+
+    [controller.navigationController pushViewController:ctrl animated:NO];
+}
+
+- (void)callbackRetryEvaluating:(KSCloudWebViewController *)controller {
+    [controller retryEvaluatingMusic];
 }
 
 - (void)callBackMergeSuccess:(KSCloudWebViewController *)controller {
@@ -251,6 +279,113 @@
     _customLoading = nil;
 }
 
+#pragma mark ----- 延迟检测流程
+- (void)showDelayCheckViewDisplay:(UIView *)displayView callBackController:(KSCloudWebViewController *)controller {
+    if (_checkView) {
+        if ([displayView.subviews containsObject:self.checkView]) {
+            [self.checkView removeFromSuperview];
+            _checkView = nil;
+        }
+    }
+    
+    MJWeakSelf;
+    self.checkView = [KSDelayCheckView shareInstance];
+    [displayView addSubview:self.checkView];
+    [self.checkView mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.left.top.right.bottom.mas_equalTo(displayView);
+    }];
+    [displayView bringSubviewToFront:self.checkView];
+    [self.checkView changeCheckType:DELAYCHECK_TYPE_PREPARE];
+    [self.checkView delayCheckCallback:^(DELAYCHECK_CALLBACK action) {
+        [weakSelf delayFinishAction:action callback:controller];
+    }];
+    BOOL isConnected = [controller isSocketConnected];
+    self.checkView.isSocketConnected = isConnected;
+    if (isConnected == NO) {
+        [controller connectSocket];
+    }
+    // 检测耳机状态
+    [controller checkAudioType];
+    
+}
+
+- (void)changeDelayCheckStatus:(CHECK_DELAY_TYPE)checkStatus {
+    if (self.checkView) {
+        switch (checkStatus) {
+            case CHECK_DELAY_TYPE_START:
+            {
+                [self.checkView changeCheckType:DELAYCHECK_TYPE_ING];
+            }
+                break;
+            case CHECK_DELAY_TYPE_FAILED:
+            {
+                [self.checkView changeCheckType:DELAYCHECK_TYPE_FAIL];
+            }
+                break;
+            case CHECK_DELAY_TYPE_SUCCESS:
+            {
+                [self.checkView changeCheckType:DELAYCHECK_TYPE_SUCCESS];
+            }
+                break;
+            case CHECK_DELAY_TYPE_CANCEL:
+            {
+                [self.checkView cancelDelayCheck];
+                self.checkView = nil;
+            }
+                break;
+                
+            default:
+                break;
+        }
+    }
+    
+}
+
+- (void)displayCurrentHeadsetTypes:(NSDictionary *)parm {
+    BOOL checkIsWired = [parm ks_boolValueForKey:@"checkIsWired"];
+    if (self.checkView) {
+        if (!checkIsWired != self.checkView.isHeadsetOff) {
+            self.checkView.isHeadsetOff = !checkIsWired;
+        }
+    }
+}
+
+- (void)socketConnectedCallback {
+    if (self.checkView) {
+        self.checkView.isSocketConnected = YES;
+    }
+}
+
+- (void)delayFinishAction:(DELAYCHECK_CALLBACK)action callback:(KSCloudWebViewController *)controller {
+    switch (action) {
+        case DELAYCHECK_CALLBACK_START:
+        {
+            [controller startAppDelayCheck];
+        }
+            break;
+        case DELAYCHECK_CALLBACK_CANCEL:
+        {
+            [controller cancelAppDelayCheck];
+            self.checkView = nil;
+        }
+            break;
+        case DELAYCHECK_CALLBACK_FINISH:
+        {
+            [controller finishDelayCheck];
+            self.checkView = nil;
+        }
+            break;
+        case DELAYCHECK_CALLBACK_FAILED: // 失败
+        {
+            [controller checkDelayBreak];
+        }
+            break;
+        default:
+            break;
+    }
+}
+
+
 #pragma mark ---- error View
 - (void)notiferShowErrorViewDisplayInView:(UIView *)view callBackController:(KSCloudWebViewController *)controller {
     MJWeakSelf;
@@ -298,7 +433,7 @@
         [_errorView removeFromSuperview];
         _errorView = nil;
     }
-    [controller backAction];
+    [controller backPreViewAction];
 }
 
 #pragma mark --- web error

+ 0 - 28
KulexiuForStudent/KulexiuForStudent/Common/Base/AccompanyWebView/KSTargetWebSocketManager.h

@@ -1,28 +0,0 @@
-//
-//  KSTargetWebSocketManager.h
-//  KulexiuForStudent
-//
-//  Created by 王智 on 2024/8/13.
-//
-
-#import <Foundation/Foundation.h>
-#import <SocketRocket.h>
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface KSTargetWebSocketManager : NSObject
-
-/** 连接状态 */
-@property (nonatomic,assign) SRReadyState socketReadyState;
-@property (nonatomic,  copy) void (^didReceiveMessage)(id message);
-@property (nonatomic, copy) void (^connectionStatus)(BOOL isSuccess);
-
-
-- (void)SRWebSocketOpen;//开启连接
-- (void)SRWebSocketClose;//关闭连接
-- (void)sendData:(id)parmData;//发送数据
-- (void)registerNetworkNotifications;//监测网络状态
-
-@end
-
-NS_ASSUME_NONNULL_END

+ 0 - 285
KulexiuForStudent/KulexiuForStudent/Common/Base/AccompanyWebView/KSTargetWebSocketManager.m

@@ -1,285 +0,0 @@
-//
-//  KSTargetWebSocketManager.m
-//  KulexiuForStudent
-//
-//  Created by 王智 on 2024/8/13.
-//
-
-#import "KSTargetWebSocketManager.h"
-#import <AFNetworkReachabilityManager.h>
-
-/*自定义打印 宏*/
-#ifdef DEBUG
-#define KSNSLog(format, ...) do {                                                                          \
-fprintf(stderr, "<%s : %d> %s\n",                                           \
-[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String],  \
-__LINE__, __func__);                                                        \
-(NSLog)((format), ##__VA_ARGS__);                                           \
-fprintf(stderr, "-------\n");                                               \
-} while (0)
-
-#else
-#define KSNSLog( ...)
-
-#endif
-
-typedef NS_ENUM(NSUInteger, SocketDataType) {
-    distributeOrder,
-    cancelCall,
-    orderLost,
-    changeDeviceType,
-};
-@interface KSTargetWebSocketManager ()<SRWebSocketDelegate>
-{
-    NSTimer * heartBeat;
-    NSTimeInterval reConnectTime;
-    SocketDataType type;
-}
-@property (nonatomic,strong) SRWebSocket *socket;
-
-@end
-
-@implementation KSTargetWebSocketManager
-
-- (instancetype)init {
-    self = [super init];
-    if (self) {
-
-    }
-    return self;
-}
-
-
-- (void)registerNetworkNotifications{
-    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkChangedNote:) name:AFNetworkingReachabilityDidChangeNotification object:nil];
-}
-- (void)networkChangedNote:(NSNotification *)note{
-    
-    AFNetworkReachabilityStatus status = [note.userInfo[AFNetworkingReachabilityNotificationStatusItem] integerValue];
-    switch (status) {
-        case AFNetworkReachabilityStatusUnknown:
-            NSLog(@"网络类型:未知网络");
-            break;
-        case AFNetworkReachabilityStatusNotReachable:
-            NSLog(@"网络类型:断网");
-            break;
-        case AFNetworkReachabilityStatusReachableViaWWAN:
-            NSLog(@"网络类型:数据流量");
-            [self SRWebSocketOpen];
-            break;
-        case AFNetworkReachabilityStatusReachableViaWiFi:
-            NSLog(@"网络类型:WIFI");
-            [self SRWebSocketOpen];
-            break;
-    }
-}
-
--(void)SRWebSocketOpen {
-    //如果是同一个url return
-    if (self.socket) {
-        if (self.socket.readyState == SR_OPEN) {
-            [self handleConnectionStatus:YES];
-            return;
-        }
-        else {
-            self.socket = nil;
-        }
-    }
-    
-    /** /userId */
-    NSString *connectedUrl = [NSString stringWithFormat:@"%@/%@", SOCKET_URL, UserDefault(UIDKey)];
-    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:connectedUrl]];
-    request.allHTTPHeaderFields = @{@"Authorization":[NSString stringWithFormat:@"%@ %@", UserDefault(Token_type), UserDefault(TokenKey)]};
-    self.socket = [[SRWebSocket alloc] initWithURLRequest:
-                   request];//这里填写你服务器的地址
-    NSLog(@"请求的websocket地址:%@",self.socket.url.absoluteString);
-    self.socket.delegate = self;   //实现这个 SRWebSocketDelegate 协议
-    [self.socket open];     //open 就是直接连接了
-}
-
-- (void)SRWebSocketClose {
-    if (self.socket) {
-        [self.socket close];
-        self.socket = nil;
-        //断开连接时销毁心跳
-        [self destoryHeartBeat];
-    }
-}
-
-#pragma mark - socket delegate
-- (void)webSocketDidOpen:(SRWebSocket *)webSocket {
-    
-    //每次正常连接的时候清零重连时间
-    reConnectTime = 0;
-    
-    //开启心跳
-    [self initHeartBeat];
-    if (webSocket == self.socket) {
-        NSLog(@"************************** socket 连接成功************************** ");
-        [self handleConnectionStatus:YES];
-    }
-}
-
-- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error {
-
-    if (webSocket == self.socket) {
-        NSLog(@"************************** socket 连接失败************************** ");
-        _socket = nil;
-        //连接失败就重连
-        [self reConnect];
-    }
-}
-
-- (void)handleConnectionStatus:(BOOL)isSuccess {
-    if (self.connectionStatus) {
-        self.connectionStatus(isSuccess);
-    }
-}
-
-- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean {
-    
-    if (webSocket == self.socket) {
-        NSLog(@"************************** socket连接断开************************** ");
-        NSLog(@"被关闭连接,code:%ld,reason:%@,wasClean:%d",(long)code,reason,wasClean);
-        [self SRWebSocketClose];
-        [self handleConnectionStatus:NO];
-    }
-}
-
-/*该函数是接收服务器发送的pong消息,其中最后一个是接受pong消息的,
- 在这里就要提一下心跳包,一般情况下建立长连接都会建立一个心跳包,
- 用于每隔一段时间通知一次服务端,客户端还是在线,这个心跳包其实就是一个ping消息,
- 我的理解就是建立一个定时器,每隔十秒或者十五秒向服务端发送一个ping消息,这个消息可是是空的
- */
-
--(void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload{
-    NSString *reply = [[NSString alloc] initWithData:pongPayload encoding:NSUTF8StringEncoding];
-    NSLog(@"reply===%@",reply);
-}
-#pragma mark - 收到的回调
-- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message  {
-    
-    if (webSocket == self.socket) {
-        NSLog(@"************************** socket收到数据了************************** ");
-        NSLog(@"message:%@",message);
-        if(!message){
-            return;
-        }
-        [self handleReceivedMessage:message];
-    }
-}
-- (void)handleReceivedMessage:(id)message {
-    
-    if(self.didReceiveMessage){
-        self.didReceiveMessage(message);
-    }
-}
-
-//重连机制
-- (void)reConnect
-{
-    
-   [self SRWebSocketClose];
-    //超过一分钟就不再重连 所以只会重连5次 2^5 = 64
-    NSLog(@"reConnectTime-----%.0f",reConnectTime);
-    if (reConnectTime > 60) {
-        //您的网络状况不是很好,请检查网络后重试
-        [self handleConnectionStatus:NO];
-        return;
-    }
-   
-    
-    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(reConnectTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
-        self.socket = nil;
-        [self SRWebSocketOpen];
-        NSLog(@"重连");
-    });
-    
-    //重连时间2的指数级增长
-    if (reConnectTime == 0) {
-        reConnectTime = 2;
-    }else{
-        reConnectTime *= 2;
-    }
-    
-}
-
-//初始化心跳
-- (void)initHeartBeat
-{
-    dispatch_main_async_safe(^{
-        [self destoryHeartBeat];
-       
-        self->heartBeat = [NSTimer timerWithTimeInterval:30 target:self selector:@selector(ping) userInfo:nil repeats:YES];
-        //和服务端约定好发送什么作为心跳标识,尽可能的减小心跳包大小
-        [[NSRunLoop currentRunLoop]addTimer:self->heartBeat forMode:NSRunLoopCommonModes];
-    })
-}
-
-
-//取消心跳
-- (void)destoryHeartBeat
-{
-    dispatch_main_async_safe(^{
-        if (self->heartBeat) {
-            if ([self->heartBeat respondsToSelector:@selector(isValid)]){
-                if ([self->heartBeat isValid]){
-                    [self->heartBeat invalidate];
-                    self->heartBeat = nil;
-                }
-            }
-        }
-    })
-}
-
-//pingPong
-- (void)ping{
-    if (self.socket.readyState == SR_OPEN) {
-        [self.socket sendPing:nil error:nil];
-    }
-}
-
-- (void)sendData:(id)parmData {
-   
-    dispatch_queue_t queue =  dispatch_queue_create("ks_websocket_queue", NULL);
-    dispatch_async(queue, ^{
-        if (self.socket != nil) {
-            // 只有 SR_OPEN 开启状态才能调 send 方法啊,不然要崩
-            if (self.socket.readyState == SR_OPEN) {
-                // 发送数据
-                if ([parmData isKindOfClass:[NSString class]]) {
-                    [self.socket sendString:parmData error:nil];
-                }
-                else if ([parmData isKindOfClass:[NSData class]]) {
-                    [self.socket sendData:parmData error:nil];
-                }
-            }
-            else {
-                [self SRWebSocketClose];
-                [self handleConnectionStatus:NO];
-            }
-            /*else if (weakSelf.socket.readyState == SR_CONNECTING) {
-
-                [weakSelf reConnect];
-            } else if (weakSelf.socket.readyState == SR_CLOSING || weakSelf.socket.readyState == SR_CLOSED) {
-                // websocket 断开了,调用 reConnect 方法重连
-                NSLog(@"重连");
-                [weakSelf reConnect];
-            }*/
-        } else {
-            // 这里要看你的具体业务需求;不过一般情况下,调用发送数据还是希望能把数据发送出去,所以可以再次打开链接;不用担心这里会有多个socketopen;因为如果当前有socket存在,会停止创建哒
-            [self SRWebSocketOpen];
-            [self handleConnectionStatus:NO];
-        }
-    });
-}
-
--(SRReadyState)socketReadyState{
-    return self.socket.readyState;
-}
-
--(void)dealloc{
-    [[NSNotificationCenter defaultCenter] removeObserver:self];
-    NSLog(@"SocketRocketUtility dealloced");
-}
-@end

File diff suppressed because it is too large
+ 0 - 0
KulexiuForStudent/KulexiuForStudent/Common/Base/DelayCheck/AnimationSource/delayCheck_ meteor.json


File diff suppressed because it is too large
+ 0 - 0
KulexiuForStudent/KulexiuForStudent/Common/Base/DelayCheck/AnimationSource/delayCheck_musicAni.json


+ 16 - 0
KulexiuForStudent/KulexiuForStudent/Common/Base/DelayCheck/DelayCheckTipsView.h

@@ -0,0 +1,16 @@
+//
+//  DelayCheckTipsView.h
+//  KulexiuSchoolStudent
+//
+//  Created by 王智 on 2024/7/10.
+//
+
+#import <UIKit/UIKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface DelayCheckTipsView : UIView
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 20 - 0
KulexiuForStudent/KulexiuForStudent/Common/Base/DelayCheck/DelayCheckTipsView.m

@@ -0,0 +1,20 @@
+//
+//  DelayCheckTipsView.m
+//  KulexiuSchoolStudent
+//
+//  Created by 王智 on 2024/7/10.
+//
+
+#import "DelayCheckTipsView.h"
+
+@implementation DelayCheckTipsView
+
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 42 - 0
KulexiuForStudent/KulexiuForStudent/Common/Base/DelayCheck/DelayCheckTipsView.xib

@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina6_12" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22689"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="DelayCheckTipsView">
+            <rect key="frame" x="0.0" y="0.0" width="335" height="38"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <subviews>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9DK-V4-ePE">
+                    <rect key="frame" x="0.0" y="0.0" width="335" height="38"/>
+                    <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.69999999999999996" colorSpace="custom" customColorSpace="calibratedRGB"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="38" id="8bn-Ep-4gq"/>
+                    </constraints>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                            <real key="value" value="19"/>
+                        </userDefinedRuntimeAttribute>
+                    </userDefinedRuntimeAttributes>
+                </view>
+            </subviews>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstAttribute="trailing" secondItem="9DK-V4-ePE" secondAttribute="trailing" id="S1P-Gf-b29"/>
+                <constraint firstAttribute="bottom" secondItem="9DK-V4-ePE" secondAttribute="bottom" id="Sza-as-Rga"/>
+                <constraint firstItem="9DK-V4-ePE" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="U5d-ba-YAA"/>
+                <constraint firstItem="9DK-V4-ePE" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="brD-ND-vHq"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <point key="canvasLocation" x="-46.564885496183201" y="40.140845070422536"/>
+        </view>
+    </objects>
+</document>

+ 46 - 0
KulexiuForStudent/KulexiuForStudent/Common/Base/DelayCheck/KSDelayCheckView.h

@@ -0,0 +1,46 @@
+//
+//  KSDelayCheckView.h
+//  KulexiuSchoolStudent
+//
+//  Created by 王智 on 2024/7/8.
+//
+
+#import <UIKit/UIKit.h>
+
+
+typedef NS_ENUM(NSInteger, DELAYCHECK_TYPE) {
+    DELAYCHECK_TYPE_PREPARE = 0,  // 检测准备
+    DELAYCHECK_TYPE_ING,      // 检测中
+    DELAYCHECK_TYPE_SUCCESS,  // 检测成功
+    DELAYCHECK_TYPE_FAIL,     // 检测失败
+    DELAYCHECK_TYPE_FINISH,   // 检测结束
+};
+
+typedef NS_ENUM(NSInteger, DELAYCHECK_CALLBACK) {
+    DELAYCHECK_CALLBACK_START, // 开始检测
+    DELAYCHECK_CALLBACK_CANCEL,// 取消
+    DELAYCHECK_CALLBACK_FINISH, // 完成
+    DELAYCHECK_CALLBACK_FAILED, // 因耳机操作导致失败
+};
+
+typedef void(^DelayCheckCallback)(DELAYCHECK_CALLBACK action);
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface KSDelayCheckView : UIView
+
+@property (nonatomic, assign) BOOL isSocketConnected; // 是否socket连接上
+
+@property (nonatomic, assign) BOOL isHeadsetOff; // 是否带上耳机
+
++ (instancetype)shareInstance;
+
+- (void)changeCheckType:(DELAYCHECK_TYPE)status;
+
+- (void)delayCheckCallback:(DelayCheckCallback)callback;
+
+- (void)cancelDelayCheck;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 364 - 0
KulexiuForStudent/KulexiuForStudent/Common/Base/DelayCheck/KSDelayCheckView.m

@@ -0,0 +1,364 @@
+//
+//  KSDelayCheckView.m
+//  KulexiuSchoolStudent
+//
+//  Created by 王智 on 2024/7/8.
+//
+
+#import "KSDelayCheckView.h"
+#import <Lottie/Lottie.h>
+#import <AVFoundation/AVFoundation.h>
+#import <MediaPlayer/MediaPlayer.h>
+
+@interface KSDelayCheckView ()
+
+@property (weak, nonatomic) IBOutlet NSLayoutConstraint *shipboardWidth;
+
+@property (weak, nonatomic) IBOutlet UIImageView *shipView;
+
+@property (weak, nonatomic) IBOutlet UIImageView *tipsImage; // 提示
+
+@property (weak, nonatomic) IBOutlet UIView *mainView; // 主动画
+
+@property (nonatomic, assign) DELAYCHECK_TYPE status;
+@property (weak, nonatomic) IBOutlet NSLayoutConstraint *backButtonLeft;
+@property (weak, nonatomic) IBOutlet UIButton *retryButton;
+@property (weak, nonatomic) IBOutlet NSLayoutConstraint *retryButtonRight;
+
+@property (nonatomic, copy) DelayCheckCallback callback;
+
+@property (weak, nonatomic) IBOutlet UIView *otherTipsView;
+
+@property (weak, nonatomic) IBOutlet NSLayoutConstraint *tipsWidth;
+@property (weak, nonatomic) IBOutlet NSLayoutConstraint *tipsBottomSpace;
+
+@property (nonatomic, strong) NSTimer *timer;
+
+@property (nonatomic, assign) NSInteger timeSecond;
+
+@property (nonatomic, assign) BOOL hasSendStart;
+
+@property (nonatomic, assign) BOOL skipFirst;
+
+@property (nonatomic, assign) CGFloat systemVolume;
+
+@property (weak, nonatomic) IBOutlet UIView *musicAniBgView;
+@property (nonatomic, strong) LOTAnimationView *musicAniView; // 音符动效
+
+@property (weak, nonatomic) IBOutlet UIView *meteorAniBgView;
+@property (nonatomic, strong) LOTAnimationView *meteorAnimationView; // 流星动效
+
+@property (weak, nonatomic) IBOutlet UIImageView *rotationStar;
+
+@end
+
+@implementation KSDelayCheckView
+
+- (void)awakeFromNib {
+    [super awakeFromNib];
+    self.otherTipsView.hidden = YES;
+    [self prepareAnimation];
+    self.backButtonLeft.constant = 25;
+    self.systemVolume = [self getCurrentVolume];
+    if (IS_IPAD) {
+        self.shipboardWidth.constant = 318 * 1.2;
+        self.tipsWidth.constant = 342 * 1.2;
+        self.tipsBottomSpace.constant = 40.0f;
+    }
+    self.retryButtonRight.constant = self.tipsWidth.constant / 342 * 54;
+    
+}
+
+// 设置音量
+- (void)setCurrentVolume {
+    if (self.systemVolume < 1.0) {
+        MPMusicPlayerController *mp = [MPMusicPlayerController applicationMusicPlayer];
+        mp.volume = 1;//0为最小1为最大
+    }
+}
+
++ (instancetype)shareInstance {
+    KSDelayCheckView *view = [[[NSBundle mainBundle] loadNibNamed:@"KSDelayCheckView" owner:nil options:nil] firstObject];
+    return view;
+}
+
+- (void)delayCheckCallback:(DelayCheckCallback)callback {
+    if (callback) {
+        self.callback = callback;
+    }
+}
+
+- (void)changeCheckType:(DELAYCHECK_TYPE)status {
+    if (status == self.status) {
+        return;
+    }
+    self.status = status;
+}
+
+- (void)cancelDelayCheck {
+    [self removeCheckView];
+    if (self.callback) {
+        self.callback(DELAYCHECK_CALLBACK_CANCEL);
+    }
+}
+
+// 取消检测
+- (IBAction)cancelAction:(id)sender {
+    [self cancelDelayCheck];
+}
+
+
+- (void)stopPlayAllAnimation {
+    if (self.meteorAnimationView.isAnimationPlaying) {
+        [self.meteorAnimationView stop];
+    }
+    if (self.musicAniView.isAnimationPlaying) {
+        [self.musicAniView stop];
+    }
+    [self removeAnimation];
+    [self removeMoveAnimation];
+}
+
+- (void)finishAction {
+    [UIView animateWithDuration:0.5f delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
+        [self layoutIfNeeded];
+    } completion:^(BOOL finished) {
+        self.layer.opacity = 0.0f;
+        if (finished) {
+            [self removeCheckView];
+            if (self.callback) {
+                self.callback(DELAYCHECK_CALLBACK_FINISH);
+            }
+        }
+    }];
+}
+
+- (void)removeCheckView {
+    [self stopPlayAllAnimation];
+    [self removeFromSuperview];
+    [self.timer setFireDate:[NSDate distantPast]];
+    [_timer invalidate];
+    _timer = nil;
+}
+
+- (IBAction)retryCheck:(id)sender {
+    if (self.status == DELAYCHECK_TYPE_FAIL) {
+        [self changeCheckType:DELAYCHECK_TYPE_PREPARE];
+    }
+}
+
+- (void)prepareAnimation {
+    self.status = DELAYCHECK_TYPE_PREPARE;
+    self.musicAniView = [self configAnimation:@"delayCheck_musicAni" attachInView:self.musicAniBgView];
+    [self.musicAniView play];
+    
+    self.meteorAnimationView = [self configAnimation:@"delayCheck_ meteor" attachInView:self.meteorAniBgView];
+    [self.meteorAnimationView play];
+    
+    [self addRotationAnimation];
+    [self addMoveAnimation];
+}
+
+- (LOTAnimationView *)configAnimation:(NSString *)aniName attachInView:(UIView *)view {
+    LOTAnimationView *aniView = [LOTAnimationView animationWithFilePath:[[NSBundle mainBundle] pathForResource:aniName ofType:@"json"]];
+    aniView.loopAnimation = YES;
+    aniView.contentMode = UIViewContentModeScaleAspectFill;
+    aniView.animationSpeed = 1.0f;
+    aniView.userInteractionEnabled = YES;
+    [view addSubview:aniView];
+    [aniView mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.left.right.top.bottom.mas_equalTo(view);
+    }];
+    return aniView;
+}
+
+- (void)setStatus:(DELAYCHECK_TYPE)status {
+    _status = status;
+    switch (status) {
+        case DELAYCHECK_TYPE_PREPARE:
+        {
+            self.hasSendStart = NO;
+            self.retryButton.hidden = YES;
+            [self.tipsImage setImage:[UIImage imageNamed:@"check_prepareTips"]];
+            [self.shipView setImage:[UIImage imageNamed:@"delayCheck_airShip_prepare"]];
+            if (self.isHeadsetOff == NO) {
+                [self startTimer];
+            }
+            [self startDelayCheck];
+            
+        }
+            break;
+        case DELAYCHECK_TYPE_ING:
+        {
+            self.retryButton.hidden = YES;
+            [self.tipsImage setImage:[UIImage imageNamed:@"check_ingTips"]];
+            
+            [self hideTips];
+            [self.shipView setImage:[UIImage imageNamed:@"delayCheck_airShip_checking"]];
+        }
+            break;
+        case DELAYCHECK_TYPE_SUCCESS:
+        {
+            self.retryButton.hidden = YES;
+            [self.tipsImage setImage:[UIImage imageNamed:@"check_finishTips"]];
+            [self.shipView setImage:[UIImage imageNamed:@"delayCheck_airShip_success"]];
+            
+            @weakObj(self);
+            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+                @strongObj(self);
+                [self finishAction];
+            });
+            [self hideTips];
+        }
+            break;
+        case DELAYCHECK_TYPE_FAIL:
+        {
+            self.retryButton.hidden = NO;
+            [self.tipsImage setImage:[UIImage imageNamed:@"check_failedTips"]];
+            [self.shipView setImage:[UIImage imageNamed:@"delayCheck_airShip_failer"]];
+            [self hideTips];
+        }
+            break;
+        default:
+            break;
+    }
+}
+
+- (IBAction)tipsCloseAction:(id)sender {
+    [self hideTips];
+}
+
+- (void)hideTips {
+    self.otherTipsView.hidden = YES;
+}
+
+
+#pragma mark ----- setter
+- (void)setIsSocketConnected:(BOOL)isSocketConnected {
+    _isSocketConnected = isSocketConnected;
+    [self startDelayCheck];
+}
+
+- (void)setIsHeadsetOff:(BOOL)isHeadsetOff {
+    _isHeadsetOff = isHeadsetOff;
+    if (isHeadsetOff) {
+        [self setCurrentVolume];
+    }
+    if (self.status == DELAYCHECK_TYPE_ING && isHeadsetOff == NO) {
+        if (self.callback) {
+            self.callback(DELAYCHECK_CALLBACK_FAILED);
+        }
+    }
+    else if (self.status == DELAYCHECK_TYPE_PREPARE) {
+        if (isHeadsetOff == NO) {
+            [self startTimer];
+        }
+        else if (isHeadsetOff == YES) {
+            [self stopTimer];
+            [self hideTips];
+        }
+    }
+    [self startDelayCheck];
+}
+
+- (void)startDelayCheck {
+    NSLog(@"-----isHeadsetOff :%d, isSocketConnected : %d",_isHeadsetOff, _isSocketConnected);
+    if (self.isHeadsetOff && self.isSocketConnected) {
+        // 直接开始下一步
+        if (self.status == DELAYCHECK_TYPE_PREPARE && self.hasSendStart == NO) {
+            self.hasSendStart = YES;
+            @weakObj(self);
+            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+                @strongObj(self);
+                if (self.callback) {
+                    self.callback(DELAYCHECK_CALLBACK_START);
+                }
+            });
+        }
+    }
+}
+
+- (void)checkHeadsetTips {
+    if (self.skipFirst == NO) {
+        [self stopTimer];
+        if (self.status == DELAYCHECK_TYPE_PREPARE) {
+            self.otherTipsView.hidden = NO;
+        }
+    }
+    self.skipFirst = NO;
+}
+
+- (void)startTimer {
+    self.skipFirst = YES;
+    [self.timer setFireDate:[NSDate distantPast]];
+    NSLog(@"-------- start timer");
+}
+
+- (void)stopTimer {
+    [self.timer setFireDate:[NSDate distantFuture]];//暂停计时器
+}
+
+#pragma mark ------ timer
+- (NSTimer *)timer{
+    
+    if (!_timer) {
+        MJWeakSelf;
+        _timer = [NSTimer scheduledTimerWithTimeInterval:5.0f repeats:YES block:^(NSTimer * _Nonnull timer) {
+            [weakSelf timerAction];
+        }];
+        [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
+        [_timer setFireDate:[NSDate distantFuture]];
+    }
+    return _timer;
+}
+
+- (void)timerAction {
+    NSLog(@"-------- checkHeadsetTips");
+    [self checkHeadsetTips];
+}
+
+- (void)dealloc {
+    NSLog(@"------ delay check View delloc");
+}
+
+- (float)getCurrentVolume {
+    return [[AVAudioSession sharedInstance] outputVolume];
+}
+
+- (void)addRotationAnimation {
+    CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
+    rotationAnimation.toValue = [NSNumber numberWithFloat:M_PI *2.0];
+    rotationAnimation.duration = 8.0f;
+    rotationAnimation.cumulative = YES;
+    rotationAnimation.repeatCount = CGFLOAT_MAX;
+    [self.rotationStar.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];
+}
+
+- (void)removeAnimation {
+    [self.rotationStar.layer removeAllAnimations];
+}
+
+- (void)addMoveAnimation {
+    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
+    animation.toValue= @(12);
+    animation.duration = 1;
+    animation.removedOnCompletion = NO;
+    animation.fillMode = kCAFillModeForwards; // 保持动画完成后的状态
+    animation.repeatCount = CGFLOAT_MAX;
+    animation.autoreverses = YES;             // 动画完成后反向执行
+    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
+    [self.shipView.layer addAnimation:animation forKey:@"translationAnimation"];
+}
+- (void)removeMoveAnimation {
+    [self.shipView.layer removeAllAnimations];
+}
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+
+@end

+ 238 - 0
KulexiuForStudent/KulexiuForStudent/Common/Base/DelayCheck/KSDelayCheckView.xib

@@ -0,0 +1,238 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23094" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina6_12" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23084"/>
+        <capability name="System colors in document resources" minToolsVersion="11.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="KSDelayCheckView">
+            <rect key="frame" x="0.0" y="0.0" width="813" height="375"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <subviews>
+                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="delayCheck_bg" translatesAutoresizingMaskIntoConstraints="NO" id="13a-eT-FQo">
+                    <rect key="frame" x="0.0" y="0.0" width="813" height="375"/>
+                </imageView>
+                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="check_bg_star" translatesAutoresizingMaskIntoConstraints="NO" id="gjS-ff-gQi">
+                    <rect key="frame" x="0.0" y="0.0" width="813" height="375"/>
+                </imageView>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Guc-mv-HSz">
+                    <rect key="frame" x="0.0" y="0.0" width="813" height="375"/>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="A5z-Nq-rkg">
+                    <rect key="frame" x="39" y="69" width="258" height="161"/>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstAttribute="width" constant="258" id="6d1-el-Roo"/>
+                        <constraint firstAttribute="height" constant="161" id="s6L-ah-5zP"/>
+                    </constraints>
+                </view>
+                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="pcl-5K-Deb">
+                    <rect key="frame" x="30" y="20" width="32" height="32"/>
+                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                    <state key="normal" image="delayCheck_back"/>
+                    <connections>
+                        <action selector="cancelAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="dxn-ug-gQF"/>
+                    </connections>
+                </button>
+                <view contentMode="scaleToFill" horizontalCompressionResistancePriority="752" translatesAutoresizingMaskIntoConstraints="NO" id="pg3-Ih-STu">
+                    <rect key="frame" x="235.66666666666663" y="23.666666666666671" width="342" height="98"/>
+                    <subviews>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="check_failedTips" translatesAutoresizingMaskIntoConstraints="NO" id="RzA-6Z-Vaf">
+                            <rect key="frame" x="0.0" y="0.0" width="342" height="98"/>
+                        </imageView>
+                        <button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="yCx-o2-bWc">
+                            <rect key="frame" x="223.00000000000003" y="38" width="64.999999999999972" height="40"/>
+                            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="40" id="o5e-3D-elW"/>
+                            </constraints>
+                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <connections>
+                                <action selector="retryCheck:" destination="iN0-l3-epB" eventType="touchUpInside" id="kby-Hq-tQf"/>
+                            </connections>
+                        </button>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstAttribute="width" secondItem="pg3-Ih-STu" secondAttribute="height" multiplier="171:49" id="73N-ha-eoc"/>
+                        <constraint firstItem="RzA-6Z-Vaf" firstAttribute="leading" secondItem="pg3-Ih-STu" secondAttribute="leading" id="EA2-46-oCg"/>
+                        <constraint firstAttribute="bottom" secondItem="yCx-o2-bWc" secondAttribute="bottom" constant="20" id="Lt5-4A-5oA"/>
+                        <constraint firstItem="yCx-o2-bWc" firstAttribute="width" secondItem="RzA-6Z-Vaf" secondAttribute="width" multiplier="130/684" id="QNj-sa-OBZ"/>
+                        <constraint firstAttribute="trailing" secondItem="yCx-o2-bWc" secondAttribute="trailing" constant="54" id="kEa-Vx-hTr"/>
+                        <constraint firstAttribute="bottom" secondItem="RzA-6Z-Vaf" secondAttribute="bottom" id="leX-O1-FmL"/>
+                        <constraint firstItem="RzA-6Z-Vaf" firstAttribute="top" secondItem="pg3-Ih-STu" secondAttribute="top" id="m2b-RE-xIn"/>
+                        <constraint firstAttribute="trailing" secondItem="RzA-6Z-Vaf" secondAttribute="trailing" id="rnJ-9N-LF7"/>
+                        <constraint firstAttribute="width" constant="342" id="zTz-gE-YK1"/>
+                    </constraints>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="1N3-2i-t6U">
+                    <rect key="frame" x="247.66666666666663" y="121.66666666666669" width="318" height="332"/>
+                    <subviews>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="delayCheck_airShip_prepare" translatesAutoresizingMaskIntoConstraints="NO" id="DOi-v9-ZzR">
+                            <rect key="frame" x="0.0" y="0.0" width="318" height="332"/>
+                        </imageView>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstAttribute="width" constant="318" id="8xk-yi-a4d"/>
+                        <constraint firstItem="DOi-v9-ZzR" firstAttribute="top" secondItem="1N3-2i-t6U" secondAttribute="top" id="Hff-ix-iEG"/>
+                        <constraint firstAttribute="trailing" secondItem="DOi-v9-ZzR" secondAttribute="trailing" id="iOt-zt-HeO"/>
+                        <constraint firstItem="DOi-v9-ZzR" firstAttribute="leading" secondItem="1N3-2i-t6U" secondAttribute="leading" id="moI-be-c6f"/>
+                        <constraint firstAttribute="bottom" secondItem="DOi-v9-ZzR" secondAttribute="bottom" id="xXs-Qy-Av9"/>
+                        <constraint firstAttribute="width" secondItem="1N3-2i-t6U" secondAttribute="height" multiplier="318:332" id="y5K-xG-Akv"/>
+                    </constraints>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9H1-aj-D94">
+                    <rect key="frame" x="276" y="291" width="261" height="38"/>
+                    <subviews>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="check_tips_icon" translatesAutoresizingMaskIntoConstraints="NO" id="Xcc-tp-5MH">
+                            <rect key="frame" x="11" y="10" width="18" height="18"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="18" id="IFf-cG-Elm"/>
+                                <constraint firstAttribute="width" constant="18" id="UoI-AB-sZ4"/>
+                            </constraints>
+                        </imageView>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="请断开耳机连接噢~" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ufD-Sz-HJb">
+                            <rect key="frame" x="32.999999999999993" y="9" width="125.33333333333331" height="20"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="20" id="rdN-mQ-xxx"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                            <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="bON-kG-Aqs">
+                            <rect key="frame" x="186" y="7" width="68" height="24"/>
+                            <subviews>
+                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="我知道了" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zTY-yG-GDX">
+                                    <rect key="frame" x="10" y="4" width="48" height="17"/>
+                                    <constraints>
+                                        <constraint firstAttribute="height" constant="17" id="dla-tp-0jZ"/>
+                                    </constraints>
+                                    <fontDescription key="fontDescription" type="system" weight="medium" pointSize="12"/>
+                                    <color key="textColor" red="0.0039215686274509803" green="0.75686274509803919" blue="0.70980392156862748" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                    <nil key="highlightedColor"/>
+                                </label>
+                                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="tH2-gC-QHh">
+                                    <rect key="frame" x="0.0" y="0.0" width="68" height="24"/>
+                                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                    <connections>
+                                        <action selector="tipsCloseAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="dLe-Vx-M6i"/>
+                                    </connections>
+                                </button>
+                            </subviews>
+                            <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                            <constraints>
+                                <constraint firstItem="tH2-gC-QHh" firstAttribute="top" secondItem="bON-kG-Aqs" secondAttribute="top" id="6TQ-nN-DRj"/>
+                                <constraint firstAttribute="height" constant="24" id="7sX-WL-QFL"/>
+                                <constraint firstItem="zTY-yG-GDX" firstAttribute="centerX" secondItem="bON-kG-Aqs" secondAttribute="centerX" id="FFa-FL-cmg"/>
+                                <constraint firstAttribute="bottom" secondItem="tH2-gC-QHh" secondAttribute="bottom" id="P3P-Eh-D1J"/>
+                                <constraint firstAttribute="bottom" secondItem="zTY-yG-GDX" secondAttribute="bottom" constant="3" id="Whz-0L-R9S"/>
+                                <constraint firstItem="tH2-gC-QHh" firstAttribute="leading" secondItem="bON-kG-Aqs" secondAttribute="leading" id="YvU-vM-Nzb"/>
+                                <constraint firstAttribute="trailing" secondItem="tH2-gC-QHh" secondAttribute="trailing" id="jUN-NC-3MP"/>
+                                <constraint firstAttribute="width" constant="68" id="k2U-gB-3ad"/>
+                                <constraint firstItem="zTY-yG-GDX" firstAttribute="top" secondItem="bON-kG-Aqs" secondAttribute="top" constant="4" id="tSi-wq-MTy"/>
+                            </constraints>
+                            <userDefinedRuntimeAttributes>
+                                <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                    <real key="value" value="12"/>
+                                </userDefinedRuntimeAttribute>
+                            </userDefinedRuntimeAttributes>
+                        </view>
+                    </subviews>
+                    <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.69999999999999996" colorSpace="custom" customColorSpace="sRGB"/>
+                    <constraints>
+                        <constraint firstAttribute="trailing" secondItem="bON-kG-Aqs" secondAttribute="trailing" constant="7" id="45h-Wf-i9r"/>
+                        <constraint firstItem="bON-kG-Aqs" firstAttribute="top" secondItem="9H1-aj-D94" secondAttribute="top" constant="7" id="75c-3R-s1j"/>
+                        <constraint firstItem="Xcc-tp-5MH" firstAttribute="leading" secondItem="9H1-aj-D94" secondAttribute="leading" constant="11" id="EDJ-zM-xbF"/>
+                        <constraint firstAttribute="bottom" secondItem="bON-kG-Aqs" secondAttribute="bottom" constant="7" id="EJU-zi-GQd"/>
+                        <constraint firstItem="ufD-Sz-HJb" firstAttribute="leading" secondItem="Xcc-tp-5MH" secondAttribute="trailing" constant="4" id="FpM-hb-6ip"/>
+                        <constraint firstAttribute="width" constant="261" id="Ho2-bp-Ymz"/>
+                        <constraint firstAttribute="height" constant="38" id="LZ2-qG-FQt"/>
+                        <constraint firstAttribute="bottom" secondItem="ufD-Sz-HJb" secondAttribute="bottom" constant="9" id="UUU-e7-6Fj"/>
+                        <constraint firstItem="Xcc-tp-5MH" firstAttribute="centerY" secondItem="9H1-aj-D94" secondAttribute="centerY" id="X1A-zL-lSD"/>
+                        <constraint firstItem="ufD-Sz-HJb" firstAttribute="top" secondItem="9H1-aj-D94" secondAttribute="top" constant="9" id="xhi-Fx-vBr"/>
+                    </constraints>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                            <real key="value" value="19"/>
+                        </userDefinedRuntimeAttribute>
+                    </userDefinedRuntimeAttributes>
+                </view>
+                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="orange_star" translatesAutoresizingMaskIntoConstraints="NO" id="xx8-DD-P5k">
+                    <rect key="frame" x="0.0" y="222" width="152" height="83"/>
+                </imageView>
+                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="rotate_star" translatesAutoresizingMaskIntoConstraints="NO" id="Qqu-0i-Kit">
+                    <rect key="frame" x="695.66666666666663" y="204" width="155" height="159"/>
+                </imageView>
+            </subviews>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstAttribute="trailing" secondItem="13a-eT-FQo" secondAttribute="trailing" id="22b-4P-sSo"/>
+                <constraint firstAttribute="bottom" secondItem="9H1-aj-D94" secondAttribute="bottom" constant="46" id="2ZJ-Fn-fQ7"/>
+                <constraint firstItem="gjS-ff-gQi" firstAttribute="trailing" secondItem="13a-eT-FQo" secondAttribute="trailing" id="4U4-YI-cWv"/>
+                <constraint firstAttribute="bottom" secondItem="xx8-DD-P5k" secondAttribute="bottom" constant="70" id="AnJ-1b-i1e"/>
+                <constraint firstItem="A5z-Nq-rkg" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="39" id="EZd-PG-aee"/>
+                <constraint firstItem="13a-eT-FQo" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="Iir-tv-x6u"/>
+                <constraint firstItem="1N3-2i-t6U" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" constant="100" id="JFM-nL-thX"/>
+                <constraint firstAttribute="trailing" secondItem="Guc-mv-HSz" secondAttribute="trailing" id="NVr-98-kcE"/>
+                <constraint firstItem="1N3-2i-t6U" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="P1J-Nc-NHw"/>
+                <constraint firstItem="A5z-Nq-rkg" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="69" id="PXn-92-6Hg"/>
+                <constraint firstItem="gjS-ff-gQi" firstAttribute="top" secondItem="13a-eT-FQo" secondAttribute="top" id="QB7-s8-xnd"/>
+                <constraint firstItem="xx8-DD-P5k" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="TM1-SQ-tC1"/>
+                <constraint firstItem="pcl-5K-Deb" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="20" id="Yo1-C7-F1f"/>
+                <constraint firstItem="Guc-mv-HSz" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="b7S-Kv-1cT"/>
+                <constraint firstItem="13a-eT-FQo" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="bKZ-Tx-uIh"/>
+                <constraint firstItem="Guc-mv-HSz" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="biF-Rh-WaY"/>
+                <constraint firstAttribute="bottom" secondItem="13a-eT-FQo" secondAttribute="bottom" id="diT-dc-3Di"/>
+                <constraint firstItem="gjS-ff-gQi" firstAttribute="bottom" secondItem="13a-eT-FQo" secondAttribute="bottom" id="fWI-wl-nRN"/>
+                <constraint firstAttribute="bottom" secondItem="Qqu-0i-Kit" secondAttribute="bottom" constant="12" id="gFf-hG-x2t"/>
+                <constraint firstItem="pg3-Ih-STu" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="hAp-vt-VbP"/>
+                <constraint firstAttribute="bottom" secondItem="Guc-mv-HSz" secondAttribute="bottom" id="ixV-Df-mZS"/>
+                <constraint firstItem="1N3-2i-t6U" firstAttribute="top" secondItem="pg3-Ih-STu" secondAttribute="bottom" id="k0D-eM-DfL"/>
+                <constraint firstAttribute="trailing" secondItem="Qqu-0i-Kit" secondAttribute="centerX" constant="40" id="oRg-Zm-elj"/>
+                <constraint firstItem="gjS-ff-gQi" firstAttribute="leading" secondItem="13a-eT-FQo" secondAttribute="leading" id="pAA-GY-dPy"/>
+                <constraint firstItem="9H1-aj-D94" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="tXf-ar-C6b"/>
+                <constraint firstItem="pcl-5K-Deb" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="30" id="z0b-B2-XKR"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <connections>
+                <outlet property="backButtonLeft" destination="z0b-B2-XKR" id="mwK-Dp-cdh"/>
+                <outlet property="mainView" destination="1N3-2i-t6U" id="qk6-xp-B7v"/>
+                <outlet property="meteorAniBgView" destination="Guc-mv-HSz" id="ZxP-rp-fYi"/>
+                <outlet property="musicAniBgView" destination="A5z-Nq-rkg" id="reB-t4-qu6"/>
+                <outlet property="otherTipsView" destination="9H1-aj-D94" id="t02-3G-Dt7"/>
+                <outlet property="retryButton" destination="yCx-o2-bWc" id="VT9-95-9As"/>
+                <outlet property="retryButtonRight" destination="kEa-Vx-hTr" id="mfJ-ly-kRF"/>
+                <outlet property="rotationStar" destination="Qqu-0i-Kit" id="TaC-WD-5Bk"/>
+                <outlet property="shipView" destination="DOi-v9-ZzR" id="T99-Mq-ZQx"/>
+                <outlet property="shipboardWidth" destination="8xk-yi-a4d" id="nww-Ue-UQK"/>
+                <outlet property="tipsBottomSpace" destination="k0D-eM-DfL" id="8jl-BK-PJl"/>
+                <outlet property="tipsImage" destination="RzA-6Z-Vaf" id="BFH-sX-6TB"/>
+                <outlet property="tipsWidth" destination="zTz-gE-YK1" id="P6j-U4-cba"/>
+            </connections>
+            <point key="canvasLocation" x="556.48854961832058" y="158.09859154929578"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="check_bg_star" width="812" height="375"/>
+        <image name="check_failedTips" width="340" height="99"/>
+        <image name="check_tips_icon" width="18" height="18"/>
+        <image name="delayCheck_airShip_prepare" width="318" height="332"/>
+        <image name="delayCheck_back" width="32" height="32"/>
+        <image name="delayCheck_bg" width="813" height="376"/>
+        <image name="orange_star" width="152" height="83"/>
+        <image name="rotate_star" width="155" height="159"/>
+        <systemColor name="systemBackgroundColor">
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+    </resources>
+</document>

+ 24 - 0
KulexiuForStudent/KulexiuForStudent/Common/Base/KSCloudPremissionAlert/KSCloudPremissionAlertView.h

@@ -0,0 +1,24 @@
+//
+//  KSCloudPremissionAlertView.h
+//  KulexiuSchoolStudent
+//
+//  Created by 王智 on 2024/7/23.
+//
+
+#import <UIKit/UIKit.h>
+
+typedef void(^CloudAlertButtonCallback)(void);
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface KSCloudPremissionAlertView : UIView
+
+@property (nonatomic, assign) BOOL isShow;
+
++ (void)configDescMessage:(NSString *)descMsg cancel:(CloudAlertButtonCallback)cancel confirm:(CloudAlertButtonCallback)confirm;
+
+- (void)dismissAlertView;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 76 - 0
KulexiuForStudent/KulexiuForStudent/Common/Base/KSCloudPremissionAlert/KSCloudPremissionAlertView.m

@@ -0,0 +1,76 @@
+//
+//  KSCloudPremissionAlertView.m
+//  KulexiuSchoolStudent
+//
+//  Created by 王智 on 2024/7/23.
+//
+
+#import "KSCloudPremissionAlertView.h"
+#import <KSToolsLibrary/UIView+Animation.h>
+
+typedef NS_ENUM(NSInteger, PREMISSION_ACTION) {
+    PREMISSION_ACTION_CANCLE = 1001,
+    PREMISSION_ACTION_SURE = 1002,
+};
+@interface KSCloudPremissionAlertView ()
+
+@property (weak, nonatomic) IBOutlet UILabel *tipsMessage;
+
+@property (nonatomic, copy) CloudAlertButtonCallback cancel;
+@property (nonatomic, copy) CloudAlertButtonCallback confirm;
+
+@end
+
+@implementation KSCloudPremissionAlertView
+
+
++ (void)configDescMessage:(NSString *)descMsg cancel:(CloudAlertButtonCallback)cancel confirm:(CloudAlertButtonCallback)confirm {
+    
+    KSCloudPremissionAlertView *alertView = [[[NSBundle mainBundle] loadNibNamed:@"KSCloudPremissionAlertView" owner:self options:nil] firstObject];
+
+    NSString *content = [NSString stringWithFormat:@"请%@访问权限", descMsg];
+    NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
+    [paragraphStyle setLineSpacing:4];//调整行间距
+    paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;
+    
+    NSMutableAttributedString *attrs = [[NSMutableAttributedString alloc] initWithString:content attributes:@{NSParagraphStyleAttributeName:paragraphStyle,NSFontAttributeName:[UIFont systemFontOfSize:14.0f weight:UIFontWeightSemibold],NSForegroundColorAttributeName:HexRGB(0x080808)}];
+    [attrs addAttributes:@{NSForegroundColorAttributeName:HexRGB(0xFE445C)} range:[content rangeOfString:descMsg]];
+    alertView.tipsMessage.attributedText = attrs;
+    alertView.cancel = cancel;
+    alertView.confirm = confirm;
+    [alertView showAlert];
+}
+
+- (void)showAlert {
+    _isShow = YES;
+    UIView *displayView = [NSObject getKeyWindow];
+    [displayView addSubview:self];
+    [self mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.left.right.top.bottom.mas_equalTo(displayView);
+    }];
+    [displayView bringSubviewToFront:self];
+    [self setPopAnimation];
+}
+
+- (void)dismissAlertView {
+    _isShow = NO;
+    [self removeFromSuperview];
+}
+
+- (IBAction)buttonAction:(UIButton *)sender {
+    if (sender.tag == PREMISSION_ACTION_CANCLE) {
+        self.cancel();
+    }else {
+        self.confirm();
+    }
+    [self dismissAlertView];
+}
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 102 - 0
KulexiuForStudent/KulexiuForStudent/Common/Base/KSCloudPremissionAlert/KSCloudPremissionAlertView.xib

@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23094" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina6_12" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23084"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="KSCloudPremissionAlertView">
+            <rect key="frame" x="0.0" y="0.0" width="852" height="548"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <subviews>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="V7u-mf-NsR">
+                    <rect key="frame" x="317" y="165" width="218" height="218"/>
+                    <subviews>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="cloud_alert_icon" translatesAutoresizingMaskIntoConstraints="NO" id="yWT-bS-30V">
+                            <rect key="frame" x="0.0" y="0.0" width="218" height="218"/>
+                        </imageView>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstItem="yWT-bS-30V" firstAttribute="top" secondItem="V7u-mf-NsR" secondAttribute="top" id="58p-NK-PzB"/>
+                        <constraint firstItem="yWT-bS-30V" firstAttribute="leading" secondItem="V7u-mf-NsR" secondAttribute="leading" id="MQP-Gt-IVm"/>
+                        <constraint firstAttribute="trailing" secondItem="yWT-bS-30V" secondAttribute="trailing" id="OXA-a4-AIW"/>
+                        <constraint firstAttribute="bottom" secondItem="yWT-bS-30V" secondAttribute="bottom" id="yry-KW-V7H"/>
+                    </constraints>
+                </view>
+                <button opaque="NO" tag="1001" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="F5Z-V4-Z8b">
+                    <rect key="frame" x="336" y="335" width="84" height="38"/>
+                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                    <state key="normal" backgroundImage="cloud_premissionCancel"/>
+                    <connections>
+                        <action selector="buttonAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="eHJ-ms-AiB"/>
+                    </connections>
+                </button>
+                <button opaque="NO" tag="1002" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="k0i-7V-nRj">
+                    <rect key="frame" x="426" y="335" width="84" height="38"/>
+                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                    <state key="normal" backgroundImage="cloud_premissionSetting"/>
+                    <connections>
+                        <action selector="buttonAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="4St-uR-ejY"/>
+                    </connections>
+                </button>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="4ZX-Qg-j3r">
+                    <rect key="frame" x="495" y="155" width="161" height="68"/>
+                    <subviews>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="cloud_alert_bubble" translatesAutoresizingMaskIntoConstraints="NO" id="gGE-Gy-Tig">
+                            <rect key="frame" x="0.0" y="0.0" width="161" height="68"/>
+                        </imageView>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="请开启相机和麦克风访问权限" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sNk-Be-LI8">
+                            <rect key="frame" x="10" y="12.333333333333343" width="143" height="33.666666666666664"/>
+                            <fontDescription key="fontDescription" type="boldSystem" pointSize="14"/>
+                            <color key="textColor" red="0.031372549019607843" green="0.031372549019607843" blue="0.031372549019607843" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstAttribute="bottom" secondItem="gGE-Gy-Tig" secondAttribute="bottom" id="3Y7-Ev-Gab"/>
+                        <constraint firstItem="sNk-Be-LI8" firstAttribute="centerY" secondItem="4ZX-Qg-j3r" secondAttribute="centerY" constant="-5" id="M0B-i6-dgX"/>
+                        <constraint firstAttribute="width" constant="161" id="MQy-iI-PyV"/>
+                        <constraint firstItem="sNk-Be-LI8" firstAttribute="leading" secondItem="4ZX-Qg-j3r" secondAttribute="leading" constant="10" id="Pzw-ih-QrD"/>
+                        <constraint firstAttribute="height" constant="68" id="SgI-eK-Srb"/>
+                        <constraint firstAttribute="trailing" secondItem="sNk-Be-LI8" secondAttribute="trailing" constant="8" id="UWQ-gB-iK8"/>
+                        <constraint firstAttribute="trailing" secondItem="gGE-Gy-Tig" secondAttribute="trailing" id="VQj-3p-qXc"/>
+                        <constraint firstItem="gGE-Gy-Tig" firstAttribute="leading" secondItem="4ZX-Qg-j3r" secondAttribute="leading" id="oSY-r1-elM"/>
+                        <constraint firstItem="gGE-Gy-Tig" firstAttribute="top" secondItem="4ZX-Qg-j3r" secondAttribute="top" id="sna-2V-AEn"/>
+                    </constraints>
+                </view>
+            </subviews>
+            <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.69999999999999996" colorSpace="custom" customColorSpace="sRGB"/>
+            <constraints>
+                <constraint firstItem="4ZX-Qg-j3r" firstAttribute="leading" secondItem="V7u-mf-NsR" secondAttribute="trailing" constant="-40" id="1V5-ZS-CVo"/>
+                <constraint firstItem="k0i-7V-nRj" firstAttribute="height" secondItem="F5Z-V4-Z8b" secondAttribute="height" id="9r8-4U-K9Z"/>
+                <constraint firstItem="V7u-mf-NsR" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="BDm-Bt-HvH"/>
+                <constraint firstItem="F5Z-V4-Z8b" firstAttribute="bottom" secondItem="V7u-mf-NsR" secondAttribute="bottom" constant="-10" id="EDa-Gm-Zry"/>
+                <constraint firstItem="k0i-7V-nRj" firstAttribute="width" secondItem="F5Z-V4-Z8b" secondAttribute="width" id="FXv-Rl-1Og"/>
+                <constraint firstItem="V7u-mf-NsR" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="LaZ-34-atl"/>
+                <constraint firstItem="4ZX-Qg-j3r" firstAttribute="top" secondItem="V7u-mf-NsR" secondAttribute="top" constant="-10" id="ORX-PO-VDS"/>
+                <constraint firstItem="k0i-7V-nRj" firstAttribute="bottom" secondItem="F5Z-V4-Z8b" secondAttribute="bottom" id="Spe-ev-n3u"/>
+                <constraint firstItem="F5Z-V4-Z8b" firstAttribute="leading" secondItem="V7u-mf-NsR" secondAttribute="leading" constant="19" id="X8i-Hd-NI8"/>
+                <constraint firstItem="k0i-7V-nRj" firstAttribute="leading" secondItem="F5Z-V4-Z8b" secondAttribute="trailing" constant="6" id="gd8-gS-xiA"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <connections>
+                <outlet property="tipsMessage" destination="sNk-Be-LI8" id="BH1-k6-bBc"/>
+            </connections>
+            <point key="canvasLocation" x="38.167938931297705" y="133.80281690140845"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="cloud_alert_bubble" width="161" height="68"/>
+        <image name="cloud_alert_icon" width="218" height="218"/>
+        <image name="cloud_premissionCancel" width="84" height="38"/>
+        <image name="cloud_premissionSetting" width="84" height="38"/>
+    </resources>
+</document>

+ 4 - 0
KulexiuForStudent/KulexiuForStudent/Common/Base/WebView/KSBaseWKWebViewController.h

@@ -45,6 +45,10 @@ NS_ASSUME_NONNULL_BEGIN
 
 - (void)loadRequest;
 
+- (void)showCustomLoading;
+
+- (void)removeCustomLoadingView;
+
 + (WKProcessPool*)singleWkProcessPool;
 
 @end

+ 60 - 40
KulexiuForStudent/KulexiuForStudent/Common/Base/WebView/KSBaseWKWebViewController.m

@@ -11,7 +11,7 @@
 
 // 其他web
 #import "KSLocalWebViewController.h"
-#import "KSAccompanyWebViewController.h"
+#import "KSCloudWebManager.h"
 
 #import "AppDelegate+AppService.h"
 #import "UserInfoManager.h"
@@ -39,6 +39,8 @@
 #import "KSLogManager.h"
 #import "KSSourceDownloadAlert.h"
 #import "KSPlatformDownloadAlert.h"
+// 加载UI
+#import "AccompanyLoadingView.h"
 
 typedef NS_ENUM(NSInteger, CHOOSETYPE) {
     CHOOSETYPE_XML,
@@ -87,6 +89,8 @@ typedef NS_ENUM(NSInteger, CHOOSETYPE) {
 //文件预览(写全局变量,否则操作不成功)
 @property (nonatomic, strong) UIDocumentInteractionController *documentVC;
 
+@property (nonatomic, strong) AccompanyLoadingView *customLoading;
+
 @end
 
 @implementation KSBaseWKWebViewController
@@ -414,9 +418,13 @@ typedef NS_ENUM(NSInteger, CHOOSETYPE) {
     }
     
     else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"openAccompanyWebView"]) { // 打开伴奏
-        [RecordCheckManager checkCameraPremissionAvaiableCallback:^(PREMISSIONTYPE type) {
-            [self afterCheckCameraCheckMic:type parm:parm];
-        }];
+        NSDictionary *valueDic = [parm ks_dictionaryValueForKey:@"content"];
+        
+        NSMutableDictionary *webParm = [NSMutableDictionary dictionaryWithDictionary:valueDic];
+        NSString *url = [valueDic ks_stringValueForKey:@"url"];
+        [webParm setValue:url forKey:@"url"];
+        [CLOUDWEB_MANAGER configCloudWebSource];
+        [CLOUDWEB_MANAGER showWebView:webParm fromController:(CustomNavViewController *)self.navigationController];
     }
     else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"checkAlbum"]) { // 判断权限
         [RecordCheckManager checkPhotoLibraryPremissionAvaiableCallback:^(PREMISSIONTYPE type) {
@@ -725,6 +733,26 @@ typedef NS_ENUM(NSInteger, CHOOSETYPE) {
         [sendParm setValue:content forKey:@"content"];
         [self postMessage:sendParm];
     }
+    else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudLoading"]) { // loading
+        NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
+        BOOL showLoading = [content ks_boolValueForKey:@"show"];
+        if (showLoading) {
+            [self showCustomLoading];
+        }
+        else {
+            [self removeCustomLoadingView];
+        }
+    }
+    else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"openOutLink"]) {
+        NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
+        NSString *url = [content ks_stringValueForKey:@"url"];
+        [self openOutLink:url];
+    }
+}
+
+- (void)openOutLink:(NSString *)url {
+    // 外部浏览器打开
+    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[url getUrlEndcodeString]] options: @{} completionHandler: nil];
 }
 
 - (void)downloadFileWithUrl:(NSString *)fileUrl parm:(NSDictionary *)parm {
@@ -805,42 +833,6 @@ typedef NS_ENUM(NSInteger, CHOOSETYPE) {
     [self.navigationController presentViewController:documentPickerVC animated:YES completion:nil];
 }
 
-
-- (void)afterCheckCameraCheckMic:(PREMISSIONTYPE)cameraType parm:(NSDictionary *)sourceParm {
-    
-    [RecordCheckManager checkMicPermissionAvaiableCallback:^(PREMISSIONTYPE type) {
-        if (type == PREMISSIONTYPE_YES && cameraType == PREMISSIONTYPE_YES) {
-            NSDictionary *valueDic = [sourceParm ks_dictionaryValueForKey:@"content"];
-            KSAccompanyWebViewController *detailCtrl = [[KSAccompanyWebViewController alloc] init];
-            detailCtrl.url = [valueDic ks_stringValueForKey:@"url"];
-            detailCtrl.parmDic = valueDic;
-            NSInteger orientation = [valueDic ks_integerValueForKey:@"orientation"];
-            BOOL isLandScape = orientation == 0 ? YES : NO;
-            detailCtrl.ks_landScape = isLandScape;
-            [self postMessage:sourceParm];
-            [self.navigationController pushViewController:detailCtrl animated:YES];
-        }
-        else { //
-            
-            NSString *content = @"";
-            CHECKDEVICETYPE checkType = CHECKDEVICETYPE_BOTH;
-            if (cameraType == PREMISSIONTYPE_NO && type == PREMISSIONTYPE_NO) {
-                content = @"请开启相机和麦克风访问权限";
-                checkType = CHECKDEVICETYPE_BOTH;
-            }
-            else if (cameraType == PREMISSIONTYPE_NO && type == PREMISSIONTYPE_YES) {
-                content =  @"请开启相机访问权限";
-                checkType = CHECKDEVICETYPE_CAMREA;
-            }
-            else if (cameraType == PREMISSIONTYPE_YES && type == PREMISSIONTYPE_NO) {
-                content = @"请开启麦克风访问权限";
-                checkType = CHECKDEVICETYPE_MIC;
-            }
-            [self showAlertWithMessage:content type:checkType];
-        }
-    }];
-}
-
 - (void)downloadWithParm:(NSDictionary *)parm {
     MJWeakSelf;
     [COURSEWARE_MANAGER downloadCourseWithParm:parm callback:^(NSDictionary * _Nonnull sendParm) {
@@ -1954,6 +1946,34 @@ typedef NS_ENUM(NSInteger, CHOOSETYPE) {
         [self.errorView removeFromSuperview];
     }
 }
+
+#pragma mark ----- 小酷AI loading
+- (AccompanyLoadingView *)customLoading {
+    if (!_customLoading) {
+        _customLoading = [AccompanyLoadingView shareInstance];
+        MJWeakSelf;
+        [_customLoading loadingCallback:^{
+            [weakSelf backAction];
+        }];
+    }
+    return _customLoading;
+}
+
+- (void)showCustomLoading {
+    if ([self.view.subviews containsObject:self.customLoading]) {
+        return;
+    }
+    [self.view addSubview:self.customLoading];
+    [self.customLoading mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.left.top.right.bottom.mas_equalTo(self.view);
+    }];
+    [self.view bringSubviewToFront:self.customLoading];
+    [self.customLoading showLoading];
+}
+
+- (void)removeCustomLoadingView {
+    [self.customLoading stopLoading];
+}
 /*
 #pragma mark - Navigation
 

+ 4 - 1
KulexiuForStudent/KulexiuForStudent/Common/Define/KSDomain.h

@@ -30,7 +30,7 @@
 // 网络教室
 #define SEALCLASSHOST ([NSString stringWithFormat:@"https://%@/api-classroom",REQUEST_DOMAIN])
 // 网页
-#define WEBHOST ([NSString stringWithFormat:@"https://%@%@",REQUEST_DOMAIN,WEBPATH])
+#define WEBHOST ([NSString stringWithFormat:@"https://%@%@",REQUEST_DOMAIN, WEBPATH])
 // 机构
 #define TENANT_WEBHOST ([NSString stringWithFormat:@"https://%@%@",REQUEST_DOMAIN,TENANTPATH])
 // 评测webSocket
@@ -38,4 +38,7 @@
 // 白板
 #define WhiteBoardHostURL ([NSString stringWithFormat:@"https://%@",WHITE_BOARD])
 
+// 云教练
+#define CLOUD_URL ([NSString stringWithFormat:@"https://%@%@",ACCOMPANY_DOMAIN, WEBPATH])
+
 #endif /* KSDomain_h */

+ 7 - 1
KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSAccompanyDraftViewController.h

@@ -6,13 +6,18 @@
 //
 
 #import "KSBaseViewController.h"
+#import "KSMediaMergeView.h"
 
-typedef void(^AccompanyMergeCallback)(BOOL isPublished);
+typedef void(^AccompanyMergeCallback)(MERGEBACK backType);
 
 NS_ASSUME_NONNULL_BEGIN
 
 @interface KSAccompanyDraftViewController : KSBaseViewController
 
+@property (nonatomic, strong) NSString *musicSheetId; // 曲子ID
+
+@property (nonatomic, strong) NSString *musicRenderType; // 谱面类型
+
 @property (nonatomic, strong) NSString *recordId;
 
 @property (nonatomic, strong) NSString *songName;
@@ -23,6 +28,7 @@ NS_ASSUME_NONNULL_BEGIN
 
 @property (nonatomic, assign) float musicSpeed;
 
+@property (nonatomic, assign) NSInteger partIndex;
 
 // 偏移时间 (录制声音和伴奏声音的偏移)offsetTime 收音延迟+ 播放延迟 ms
 - (void)configWithVideoUrl:(NSURL *)videoUrl bgAudioUrl:(NSURL *)bgAudioUrl remoteBgUrl:(NSString *)remoteBgUrl recordUrl:(NSURL *)recordUrl offsetTime:(NSInteger)offsetTime mergeCallback:(AccompanyMergeCallback)callback;

+ 15 - 4
KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSAccompanyDraftViewController.m

@@ -6,9 +6,10 @@
 //
 
 #import "KSAccompanyDraftViewController.h"
-#import "KSMediaMergeView.h"
 #import "AppDelegate+AppService.h"
-#import <UIDevice+TFDevice.h>
+#import "UIDevice+TFDevice.h"
+
+
 
 @interface KSAccompanyDraftViewController ()
 
@@ -81,10 +82,15 @@
     mergeView.recordId = self.recordId;
     mergeView.desc = self.desc;
     mergeView.musicSpeed = self.musicSpeed;
+    // 新增曲目id和谱面类型
+    mergeView.musicSheetId = self.musicSheetId;
+    mergeView.musicRenderType = self.musicRenderType;
+    mergeView.partIndex = self.partIndex;
+//    partIndex
     MJWeakSelf;
-    [mergeView configWithVideoUrl:videoUrl bgAudioUrl:bgAudioUrl remoteBgUrl:remoteBgUrl recordUrl:recordUrl offsetTime:offsetTime mergeCallback:^(BOOL isPublished) {
+    [mergeView configWithVideoUrl:videoUrl bgAudioUrl:bgAudioUrl remoteBgUrl:remoteBgUrl recordUrl:recordUrl offsetTime:offsetTime mergeCallback:^(MERGEBACK type) {
         if (weakSelf.callback) {
-            weakSelf.callback(isPublished);
+            weakSelf.callback(type);
             [weakSelf backAction];
         }
     }];
@@ -100,6 +106,11 @@
     // 根据需要返回到不同页面
     [self.navigationController popViewControllerAnimated:NO];
 }
+
+- (void)dealloc {
+    NSLog(@"KSAccompanyDraftViewController dealloc");
+}
+
 /*
 #pragma mark - Navigation
 

+ 14 - 1
KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSMediaMergeView.h

@@ -7,13 +7,24 @@
 
 #import <UIKit/UIKit.h>
 
-typedef void(^MergeCallback)(BOOL isPublished);
+typedef NS_ENUM(NSInteger, MERGEBACK) {
+    MERGEBACK_CANCEL,  // 取消
+    MERGEBACK_PUBLISH, // 发布
+    MERGEBACK_RETRY,   // 重试
+};
+
+typedef void(^MergeCallback)(MERGEBACK type);
+
 typedef void(^DraftEditCallback)(void);
 
 NS_ASSUME_NONNULL_BEGIN
 
 @interface KSMediaMergeView : UIView
 
+@property (nonatomic, strong) NSString *musicSheetId; // 曲子ID
+
+@property (nonatomic, strong) NSString *musicRenderType; // 谱面类型
+
 @property (nonatomic, strong) NSString *recordId;
 
 @property (nonatomic, strong) NSString *songName;
@@ -23,6 +34,8 @@ NS_ASSUME_NONNULL_BEGIN
 @property (nonatomic, strong) NSString *desc;
 
 @property (nonatomic, assign) float musicSpeed;
+// 分轨
+@property (nonatomic, assign) NSInteger partIndex;
 
 // 偏移时间 (录制声音和伴奏声音的偏移)offsetTime 收音延迟+ 播放延迟 ms
 - (void)configWithVideoUrl:(NSURL *)videoUrl bgAudioUrl:(NSURL *)bgAudioUrl remoteBgUrl:(NSString *)remoteBgUrl recordUrl:(NSURL *)recordUrl offsetTime:(NSInteger)offsetTime mergeCallback:(MergeCallback)callback;

+ 9 - 48
KulexiuForStudent/KulexiuForStudent/Module/Chat/Controller/TXCustom/KSTXBaseChatViewController.m

@@ -12,7 +12,7 @@
 #import "KSBaseWKWebViewController.h"
 
 #import "KSEnterLiveroomManager.h"
-#import "KSAccompanyWebViewController.h"
+#import "KSCloudWebManager.h"
 
 #import <RecordCheckManager.h>
 #import "KSPremissionAlert.h"
@@ -137,53 +137,14 @@ static UIView *gCustomTopView;
     }];
 }
 - (void)showMusic:(NSString *)songId {
-    
-    [RecordCheckManager checkCameraPremissionAvaiableCallback:^(PREMISSIONTYPE type) {
-        [self afterCheckCameraCheckMic:type songId:songId];
-    }];
-}
-
-- (void)afterCheckCameraCheckMic:(PREMISSIONTYPE)cameraType songId:(NSString *)songId {
-    
-    [RecordCheckManager checkMicPermissionAvaiableCallback:^(PREMISSIONTYPE type) {
-        if (type == PREMISSIONTYPE_YES && cameraType == PREMISSIONTYPE_YES) {
-            KSAccompanyWebViewController *detailCtrl = [[KSAccompanyWebViewController alloc] init];
-            detailCtrl.url = [NSString stringWithFormat:@"%@/accompany?id=%@",hostURL, songId];
-            detailCtrl.parmDic = @{@"isOpenLight" : @(YES), @"orientation" : @(0),@"isHideTitle" : @(YES)};
-            [self.navigationController pushViewController:detailCtrl animated:YES];
-        }
-        else { //
-            
-            NSString *content = @"";
-            CHECKDEVICETYPE checkType = CHECKDEVICETYPE_BOTH;
-            if (cameraType == PREMISSIONTYPE_NO && type == PREMISSIONTYPE_NO) {
-                content = @"请开启相机和麦克风访问权限";
-                checkType = CHECKDEVICETYPE_BOTH;
-            }
-            else if (cameraType == PREMISSIONTYPE_NO && type == PREMISSIONTYPE_YES) {
-                content =  @"请开启相机访问权限";
-                checkType = CHECKDEVICETYPE_CAMREA;
-            }
-            else if (cameraType == PREMISSIONTYPE_YES && type == PREMISSIONTYPE_NO) {
-                content = @"请开启麦克风访问权限";
-                checkType = CHECKDEVICETYPE_MIC;
-            }
-            [self showAlertWithMessage:content type:checkType];
-        }
-    }];
-}
-
-- (void)showAlertWithMessage:(NSString *)message type:(CHECKDEVICETYPE)deviceType {
-    [KSPremissionAlert shareInstanceDisplayImage:deviceType message:message showInView:self.view cancel:^{
-        
-    } confirm:^{
-        [self openSettingView];
-    }];
-    
-}
-
-- (void)openSettingView {
-    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
+    NSMutableDictionary *parm = [NSMutableDictionary dictionary];
+    NSString *url = [NSString stringWithFormat:@"%@?id=%@",CLOUD_URL, songId];
+    [parm setValue:url forKey:@"url"];
+    [parm setValue:@(YES) forKey:@"isOpenLight"];
+    [parm setValue:@(0) forKey:@"orientation"];
+    [parm setValue:@(YES) forKey:@"isHideTitle"];
+    [CLOUDWEB_MANAGER configCloudWebSource];
+    [CLOUDWEB_MANAGER showWebView:parm fromController:(CustomNavViewController *)self.navigationController];
 }
 
 - (void)displayTeacherInfo:(NSString *)teacherId {

+ 11 - 1
KulexiuForStudent/KulexiuForStudent/Module/Mine/Works/Controller/KSDraftMergeViewController.m

@@ -8,7 +8,8 @@
 #import "KSDraftMergeViewController.h"
 #import "KSMediaMergeView.h"
 #import "AppDelegate+AppService.h"
-#import <UIDevice+TFDevice.h>
+#import "UIDevice+TFDevice.h"
+#import "KSAudioSessionManager.h"
 
 @interface KSDraftMergeViewController ()
 
@@ -16,6 +17,8 @@
 
 @property (nonatomic, assign) BOOL hasInitUI;
 
+@property (nonatomic, strong) KSAudioSessionManager *audioSessionManager;
+
 @end
 
 @implementation KSDraftMergeViewController
@@ -53,6 +56,12 @@
     // Do any additional setup after loading the view.
     self.ks_prefersNavigationBarHidden = YES;
     self.hasInitUI = NO;
+    [self configAudioSession];
+}
+
+- (void)configAudioSession {
+    self.audioSessionManager = [[KSAudioSessionManager alloc] init];
+    [self.audioSessionManager configAudioSession:AUDIOCONFIG_PLAYANDRECORD];
 }
 
 - (void)configUI {
@@ -63,6 +72,7 @@
     mergeView.songName = self.sourceModel.musicSheetName;
     mergeView.recordId = self.sourceModel.musicPracticeRecordId;
     mergeView.desc = self.sourceModel.desc;
+    mergeView.musicSheetId = self.sourceModel.musicSheetId;
     [self.view addSubview:mergeView];
     [mergeView mas_makeConstraints:^(MASConstraintMaker *make) {
         make.left.right.top.bottom.mas_equalTo(self.view);

Some files were not shown because too many files changed in this diff