Browse Source

Merge branch 'feature/ios_im_v2_ws' into 'develop'

ios消息功能开发

See merge request o2oa/o2oa!685
楼国栋 5 years ago
parent
commit
4b263e3c29
100 changed files with 1920 additions and 137 deletions
  1. 109 0
      o2ios/O2Platform.xcodeproj/project.pbxproj
  2. 162 0
      o2ios/O2Platform/App/IM-聊天/IMChatViewController.swift
  3. 81 0
      o2ios/O2Platform/App/IM-聊天/IMChatViewController.xib
  4. 133 0
      o2ios/O2Platform/App/IM-聊天/IMConversationListViewController.swift
  5. 63 0
      o2ios/O2Platform/App/IM-聊天/IMViewModel.swift
  6. 83 0
      o2ios/O2Platform/App/IM-聊天/Model/IMConversationInfo.swift
  7. 28 0
      o2ios/O2Platform/App/IM-聊天/O2IM.swift
  8. 133 0
      o2ios/O2Platform/App/IM-聊天/View/IMChatMessageSendViewCell.swift
  9. 84 0
      o2ios/O2Platform/App/IM-聊天/View/IMChatMessageSendViewCell.xib
  10. 131 0
      o2ios/O2Platform/App/IM-聊天/View/IMChatMessageViewCell.swift
  11. 84 0
      o2ios/O2Platform/App/IM-聊天/View/IMChatMessageViewCell.xib
  12. 98 0
      o2ios/O2Platform/App/IM-聊天/View/IMConversationItemCell.swift
  13. 93 0
      o2ios/O2Platform/App/IM-聊天/View/IMConversationItemCell.xib
  14. 65 37
      o2ios/O2Platform/App/NewAttance-考勤打卡/c/OOAttanceCheckInController.swift
  15. 70 0
      o2ios/O2Platform/App/NewAttance-考勤打卡/m/OOAttandanceModels.swift
  16. 13 1
      o2ios/O2Platform/App/NewAttance-考勤打卡/v/OOAttanceItemCell.swift
  17. 6 9
      o2ios/O2Platform/App/NewAttance-考勤打卡/v/OOAttanceItemCell.xib
  18. 15 0
      o2ios/O2Platform/App/NewAttance-考勤打卡/vm/OOAttandanceViewModel.swift
  19. 157 46
      o2ios/O2Platform/App/O2MainController.swift
  20. 29 31
      o2ios/O2Platform/App/contacts/contacts.storyboard
  21. 22 0
      o2ios/O2Platform/Assets.xcassets/all/chat_emoji.imageset/Contents.json
  22. BIN
      o2ios/O2Platform/Assets.xcassets/all/chat_emoji.imageset/chat_emoji@2x.png
  23. BIN
      o2ios/O2Platform/Assets.xcassets/all/chat_emoji.imageset/chat_emoji@3x.png
  24. 22 0
      o2ios/O2Platform/Assets.xcassets/首页/chat_bubble_incomming.imageset/Contents.json
  25. BIN
      o2ios/O2Platform/Assets.xcassets/首页/chat_bubble_incomming.imageset/chat_bubble_incomming@2x.png
  26. BIN
      o2ios/O2Platform/Assets.xcassets/首页/chat_bubble_incomming.imageset/chat_bubble_incomming@3x.png
  27. 22 0
      o2ios/O2Platform/Assets.xcassets/首页/chat_bubble_outgoing.imageset/Contents.json
  28. BIN
      o2ios/O2Platform/Assets.xcassets/首页/chat_bubble_outgoing.imageset/chat_bubble_outgoing@2x.png
  29. BIN
      o2ios/O2Platform/Assets.xcassets/首页/chat_bubble_outgoing.imageset/chat_bubble_outgoing@3x.png
  30. 17 8
      o2ios/O2Platform/Extension/Bundle+Extension.swift
  31. 16 4
      o2ios/O2Platform/Extension/Date+Extension.swift
  32. 2 0
      o2ios/O2Platform/Extension/Notification+Extension.swift
  33. 7 0
      o2ios/O2Platform/Extension/UIDeviceExtensions.swift
  34. 33 0
      o2ios/O2Platform/Extension/UIView+Extension.swift
  35. 7 0
      o2ios/O2Platform/Framework/O2API/AttendanceAPI/OOAttendanceAPI.swift
  36. 79 0
      o2ios/O2Platform/Framework/O2API/Communicate/CommunicateAPI.swift
  37. 56 0
      o2ios/O2Platform/Manager/O2WebsocketManager.swift
  38. 0 1
      o2ios/O2Platform/O2Platform-Bridging-Header.h
  39. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/Info.plist
  40. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_01.png
  41. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_02.png
  42. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_03.png
  43. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_04.png
  44. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_05.png
  45. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_06.png
  46. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_07.png
  47. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_08.png
  48. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_09.png
  49. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_10.png
  50. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_11.png
  51. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_12.png
  52. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_13.png
  53. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_14.png
  54. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_15.png
  55. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_16.png
  56. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_17.png
  57. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_18.png
  58. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_19.png
  59. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_20.png
  60. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_21.png
  61. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_22.png
  62. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_23.png
  63. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_24.png
  64. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_25.png
  65. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_26.png
  66. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_27.png
  67. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_28.png
  68. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_29.png
  69. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_30.png
  70. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_31.png
  71. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_32.png
  72. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_33.png
  73. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_34.png
  74. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_35.png
  75. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_36.png
  76. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_37.png
  77. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_38.png
  78. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_39.png
  79. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_40.png
  80. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_41.png
  81. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_42.png
  82. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_43.png
  83. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_44.png
  84. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_45.png
  85. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_46.png
  86. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_47.png
  87. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_48.png
  88. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_49.png
  89. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_50.png
  90. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_51.png
  91. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_52.png
  92. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_53.png
  93. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_54.png
  94. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_55.png
  95. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_56.png
  96. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_57.png
  97. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_58.png
  98. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_59.png
  99. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_60.png
  100. BIN
      o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_61.png

+ 109 - 0
o2ios/O2Platform.xcodeproj/project.pbxproj

@@ -56,6 +56,14 @@
 		B108F402229E34D400778050 /* LBXScanViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B108F3FB229E34D300778050 /* LBXScanViewController.swift */; };
 		B10A2F56233DDA990011CE3D /* AdSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B10A2F55233DDA990011CE3D /* AdSupport.framework */; };
 		B10A2F58233DDAE10011CE3D /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B10A2F57233DDAE10011CE3D /* Security.framework */; };
+		B1173A5B2488C546005075F0 /* IMConversationListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1173A5A2488C546005075F0 /* IMConversationListViewController.swift */; };
+		B1173A5E2488C5DF005075F0 /* CommunicateAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1173A5D2488C5DF005075F0 /* CommunicateAPI.swift */; };
+		B1173A602488C82F005075F0 /* IMViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1173A5F2488C82F005075F0 /* IMViewModel.swift */; };
+		B1173A632488C8EA005075F0 /* IMConversationInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1173A622488C8EA005075F0 /* IMConversationInfo.swift */; };
+		B1173A672488CD5B005075F0 /* IMConversationItemCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1173A652488CD5B005075F0 /* IMConversationItemCell.swift */; };
+		B1173A682488CD5B005075F0 /* IMConversationItemCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1173A662488CD5B005075F0 /* IMConversationItemCell.xib */; };
+		B1173A6A2488D4AD005075F0 /* O2IM.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1173A692488D4AD005075F0 /* O2IM.swift */; };
+		B1173B5E2489F48C005075F0 /* O2Emoji.bundle in Resources */ = {isa = PBXBuildFile; fileRef = B1173B2C2489F48C005075F0 /* O2Emoji.bundle */; };
 		B1298E50236692AB006E9236 /* CloudFileListBaseController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1298E4F236692AB006E9236 /* CloudFileListBaseController.swift */; };
 		B1298E7C23669BA2006E9236 /* CloudFileTypeListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1298E7B23669BA2006E9236 /* CloudFileTypeListController.swift */; };
 		B1298E7E2366AE4C006E9236 /* CloudFileImageCollectionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1298E7D2366AE4C006E9236 /* CloudFileImageCollectionController.swift */; };
@@ -111,6 +119,12 @@
 		B13A0120236935BC00025F3B /* O2CloudFileInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B13A011F236935BC00025F3B /* O2CloudFileInfo.swift */; };
 		B142B081230FB56400E7D127 /* MimeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B142B080230FB56400E7D127 /* MimeType.swift */; };
 		B142B083230FB58B00E7D127 /* Swime.swift in Sources */ = {isa = PBXBuildFile; fileRef = B142B082230FB58B00E7D127 /* Swime.swift */; };
+		B1489B1C248E0F4D009EE9FD /* IMChatViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1489B1A248E0F4D009EE9FD /* IMChatViewController.swift */; };
+		B1489B1D248E0F4D009EE9FD /* IMChatViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1489B1B248E0F4D009EE9FD /* IMChatViewController.xib */; };
+		B1489B51248E192D009EE9FD /* IMChatMessageViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1489B4F248E192D009EE9FD /* IMChatMessageViewCell.swift */; };
+		B1489B52248E192D009EE9FD /* IMChatMessageViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1489B50248E192D009EE9FD /* IMChatMessageViewCell.xib */; };
+		B1489BFE2490BE51009EE9FD /* IMChatMessageSendViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1489BFC2490BE51009EE9FD /* IMChatMessageSendViewCell.swift */; };
+		B1489BFF2490BE51009EE9FD /* IMChatMessageSendViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1489BFD2490BE51009EE9FD /* IMChatMessageSendViewCell.xib */; };
 		B14B339F2356EB1500442968 /* CloudFileViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14B339E2356EB1500442968 /* CloudFileViewModel.swift */; };
 		B14E07532301137F00AE85A0 /* ContactPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14E07522301137F00AE85A0 /* ContactPickerViewController.swift */; };
 		B14E07862301418400AE85A0 /* ContactUnitPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14E07852301418400AE85A0 /* ContactUnitPickerViewController.swift */; };
@@ -118,6 +132,7 @@
 		B14E078A230141AC00AE85A0 /* ContactGroupPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14E0789230141AC00AE85A0 /* ContactGroupPickerViewController.swift */; };
 		B14E078C230141BE00AE85A0 /* ContactPersonPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14E078B230141BE00AE85A0 /* ContactPersonPickerViewController.swift */; };
 		B14E07C523025C6A00AE85A0 /* OOContactExpressAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14E07C423025C6A00AE85A0 /* OOContactExpressAPI.swift */; };
+		B14E0C0C2484F1F0008AF6AE /* O2WebsocketManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14E0C0B2484F1F0008AF6AE /* O2WebsocketManager.swift */; };
 		B1534E3F21F712EA00CC8C35 /* O2DemoAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1534E3E21F712EA00CC8C35 /* O2DemoAlertView.swift */; };
 		B158E95E215DD3F500AB2727 /* AIConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = B158E95D215DD3F500AB2727 /* AIConstants.swift */; };
 		B15D26EC235EF7480092F8B8 /* genstrings.sh in Resources */ = {isa = PBXBuildFile; fileRef = B15D26EB235EF7480092F8B8 /* genstrings.sh */; };
@@ -1211,6 +1226,13 @@
 			remoteGlobalIDString = 183276F1FBCA05D9F840C64172C84A44;
 			remoteInfo = YHPopupView;
 		};
+		B1173A582488C3DD005075F0 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = E4E7755421017DB4006ED7FC /* Pods.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 891B2270823847ED23F2ECFC28F935EC;
+			remoteInfo = Starscream;
+		};
 		B14A2292224CBC2E00745E0A /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
 			containerPortal = E4E7755421017DB4006ED7FC /* Pods.xcodeproj */;
@@ -1358,6 +1380,14 @@
 		B108F3FB229E34D300778050 /* LBXScanViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LBXScanViewController.swift; sourceTree = "<group>"; };
 		B10A2F55233DDA990011CE3D /* AdSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdSupport.framework; path = System/Library/Frameworks/AdSupport.framework; sourceTree = SDKROOT; };
 		B10A2F57233DDAE10011CE3D /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
+		B1173A5A2488C546005075F0 /* IMConversationListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMConversationListViewController.swift; sourceTree = "<group>"; };
+		B1173A5D2488C5DF005075F0 /* CommunicateAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommunicateAPI.swift; sourceTree = "<group>"; };
+		B1173A5F2488C82F005075F0 /* IMViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMViewModel.swift; sourceTree = "<group>"; };
+		B1173A622488C8EA005075F0 /* IMConversationInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMConversationInfo.swift; sourceTree = "<group>"; };
+		B1173A652488CD5B005075F0 /* IMConversationItemCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMConversationItemCell.swift; sourceTree = "<group>"; };
+		B1173A662488CD5B005075F0 /* IMConversationItemCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = IMConversationItemCell.xib; sourceTree = "<group>"; };
+		B1173A692488D4AD005075F0 /* O2IM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = O2IM.swift; sourceTree = "<group>"; };
+		B1173B2C2489F48C005075F0 /* O2Emoji.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = O2Emoji.bundle; sourceTree = "<group>"; };
 		B1298E4F236692AB006E9236 /* CloudFileListBaseController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudFileListBaseController.swift; sourceTree = "<group>"; };
 		B1298E7B23669BA2006E9236 /* CloudFileTypeListController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudFileTypeListController.swift; sourceTree = "<group>"; };
 		B1298E7D2366AE4C006E9236 /* CloudFileImageCollectionController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudFileImageCollectionController.swift; sourceTree = "<group>"; };
@@ -1409,6 +1439,12 @@
 		B13A011F236935BC00025F3B /* O2CloudFileInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = O2CloudFileInfo.swift; sourceTree = "<group>"; };
 		B142B080230FB56400E7D127 /* MimeType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MimeType.swift; sourceTree = "<group>"; };
 		B142B082230FB58B00E7D127 /* Swime.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Swime.swift; sourceTree = "<group>"; };
+		B1489B1A248E0F4D009EE9FD /* IMChatViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMChatViewController.swift; sourceTree = "<group>"; };
+		B1489B1B248E0F4D009EE9FD /* IMChatViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = IMChatViewController.xib; sourceTree = "<group>"; };
+		B1489B4F248E192D009EE9FD /* IMChatMessageViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMChatMessageViewCell.swift; sourceTree = "<group>"; };
+		B1489B50248E192D009EE9FD /* IMChatMessageViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = IMChatMessageViewCell.xib; sourceTree = "<group>"; };
+		B1489BFC2490BE51009EE9FD /* IMChatMessageSendViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMChatMessageSendViewCell.swift; sourceTree = "<group>"; };
+		B1489BFD2490BE51009EE9FD /* IMChatMessageSendViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = IMChatMessageSendViewCell.xib; sourceTree = "<group>"; };
 		B14B339E2356EB1500442968 /* CloudFileViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudFileViewModel.swift; sourceTree = "<group>"; };
 		B14E07522301137F00AE85A0 /* ContactPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactPickerViewController.swift; sourceTree = "<group>"; };
 		B14E07852301418400AE85A0 /* ContactUnitPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactUnitPickerViewController.swift; sourceTree = "<group>"; };
@@ -1416,6 +1452,7 @@
 		B14E0789230141AC00AE85A0 /* ContactGroupPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactGroupPickerViewController.swift; sourceTree = "<group>"; };
 		B14E078B230141BE00AE85A0 /* ContactPersonPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactPersonPickerViewController.swift; sourceTree = "<group>"; };
 		B14E07C423025C6A00AE85A0 /* OOContactExpressAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OOContactExpressAPI.swift; sourceTree = "<group>"; };
+		B14E0C0B2484F1F0008AF6AE /* O2WebsocketManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = O2WebsocketManager.swift; sourceTree = "<group>"; };
 		B1534E3E21F712EA00CC8C35 /* O2DemoAlertView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = O2DemoAlertView.swift; sourceTree = "<group>"; };
 		B158E95D215DD3F500AB2727 /* AIConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AIConstants.swift; sourceTree = "<group>"; };
 		B15D26BF235EF0850092F8B8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/CloudFile.storyboard; sourceTree = "<group>"; };
@@ -2514,6 +2551,7 @@
 				B10AC5D721058DAE00179587 /* RxSwift.framework */,
 				B10AC5D921058DAE00179587 /* SDWebImage.framework */,
 				B10AC5DF21058DAE00179587 /* SnapKit.framework */,
+				B1173A592488C3DD005075F0 /* Starscream.framework */,
 				B10AC5E321058DAE00179587 /* SwiftValidator.framework */,
 				B10AC5E521058DAE00179587 /* SwiftyJSON.framework */,
 				B10AC5E721058DAE00179587 /* SwiftyTimer.framework */,
@@ -2524,6 +2562,49 @@
 			name = Products;
 			sourceTree = "<group>";
 		};
+		B1173A262488C3DD005075F0 /* IM-聊天 */ = {
+			isa = PBXGroup;
+			children = (
+				B1173A642488CD37005075F0 /* View */,
+				B1173A612488C8D3005075F0 /* Model */,
+				B1173A5A2488C546005075F0 /* IMConversationListViewController.swift */,
+				B1173A5F2488C82F005075F0 /* IMViewModel.swift */,
+				B1173A692488D4AD005075F0 /* O2IM.swift */,
+				B1489B1A248E0F4D009EE9FD /* IMChatViewController.swift */,
+				B1489B1B248E0F4D009EE9FD /* IMChatViewController.xib */,
+			);
+			path = "IM-聊天";
+			sourceTree = "<group>";
+		};
+		B1173A5C2488C568005075F0 /* Communicate */ = {
+			isa = PBXGroup;
+			children = (
+				B1173A5D2488C5DF005075F0 /* CommunicateAPI.swift */,
+			);
+			path = Communicate;
+			sourceTree = "<group>";
+		};
+		B1173A612488C8D3005075F0 /* Model */ = {
+			isa = PBXGroup;
+			children = (
+				B1173A622488C8EA005075F0 /* IMConversationInfo.swift */,
+			);
+			path = Model;
+			sourceTree = "<group>";
+		};
+		B1173A642488CD37005075F0 /* View */ = {
+			isa = PBXGroup;
+			children = (
+				B1173A652488CD5B005075F0 /* IMConversationItemCell.swift */,
+				B1173A662488CD5B005075F0 /* IMConversationItemCell.xib */,
+				B1489B4F248E192D009EE9FD /* IMChatMessageViewCell.swift */,
+				B1489B50248E192D009EE9FD /* IMChatMessageViewCell.xib */,
+				B1489BFC2490BE51009EE9FD /* IMChatMessageSendViewCell.swift */,
+				B1489BFD2490BE51009EE9FD /* IMChatMessageSendViewCell.xib */,
+			);
+			path = View;
+			sourceTree = "<group>";
+		};
 		B12FD1502283CDF400E636BA /* theme */ = {
 			isa = PBXGroup;
 			children = (
@@ -2785,6 +2866,7 @@
 		B1F79C44233B5F9B004D0AEE /* Resources */ = {
 			isa = PBXGroup;
 			children = (
+				B1173B2C2489F48C005075F0 /* O2Emoji.bundle */,
 				B19776E7217D9D810019F3A8 /* beep.wav */,
 				E4B888231D9D48F1002E1A46 /* images */,
 				E4B888291D9D48F1002E1A46 /* 隶变体.ttf */,
@@ -2838,6 +2920,7 @@
 				B13A011023692A8B00025F3B /* O2CloudFileManager.swift */,
 				B1E0BAA02378FC01001D741F /* O2JPushManager.swift */,
 				B1ABAA0B237E498C0027EC48 /* O2VersionManager.swift */,
+				B14E0C0B2484F1F0008AF6AE /* O2WebsocketManager.swift */,
 			);
 			path = Manager;
 			sourceTree = "<group>";
@@ -2869,6 +2952,7 @@
 				E45CD4211DFE503C008F99AD /* Scan-二维码扫码 */,
 				E4C24C22208D7EDE00E426B0 /* Contact-通讯录 */,
 				E4B8875E1D9D48F1002E1A46 /* contacts */,
+				B1173A262488C3DD005075F0 /* IM-聊天 */,
 				E428AF3720A95D3800D964B9 /* NewAttance-考勤打卡 */,
 				E40E246E20B7DA3C009F8BE7 /* meeting-会议 */,
 				B19091FD2107145F009A7906 /* Calendar-日程管理 */,
@@ -3405,6 +3489,7 @@
 		E4B69721207602840062F6E8 /* O2API */ = {
 			isa = PBXGroup;
 			children = (
+				B1173A5C2488C568005075F0 /* Communicate */,
 				B18FFF3323756B24001B2887 /* JpushAPI */,
 				B107453721A52B9A0015F1B2 /* PersonalAPI */,
 				B1B6A8F7217710A400C10F3C /* FaceRecognizeAPI */,
@@ -5294,6 +5379,13 @@
 			remoteRef = B10AC5F021058DAE00179587 /* PBXContainerItemProxy */;
 			sourceTree = BUILT_PRODUCTS_DIR;
 		};
+		B1173A592488C3DD005075F0 /* Starscream.framework */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.framework;
+			path = Starscream.framework;
+			remoteRef = B1173A582488C3DD005075F0 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
 		B14A2293224CBC2E00745E0A /* IQKeyboardManagerSwift.framework */ = {
 			isa = PBXReferenceProxy;
 			fileType = wrapper.framework;
@@ -5384,6 +5476,7 @@
 				E4C24B4920844F3C00E426B0 /* JCMessageImageCollectionViewCell.xib in Resources */,
 				E4C24C1820844F5200E426B0 /* yh_image_picked@2x.png in Resources */,
 				E45755A61E0BA72E00EC44F4 /* qrcode_scan_part_net.png in Resources */,
+				B1489B52248E192D009EE9FD /* IMChatMessageViewCell.xib in Resources */,
 				E46E6C721DD41F5D00AB7561 /* ZSSfonts@2x.png in Resources */,
 				E46E6C6E1DD41F5D00AB7561 /* ZSScenterjustify@2x.png in Resources */,
 				B1298E822366B2E7006E9236 /* CFImageViewCell.xib in Resources */,
@@ -5398,12 +5491,14 @@
 				E4B8871E1D9D477A002E1A46 /* Assets.xcassets in Resources */,
 				E46E6CA51DD41F5D00AB7561 /* ZSSsuperscript.png in Resources */,
 				E4C24BC020844F3C00E426B0 /* 定位2.png in Resources */,
+				B1173A682488CD5B005075F0 /* IMConversationItemCell.xib in Resources */,
 				B197771D217D9D810019F3A8 /* beep.wav in Resources */,
 				E40E24C920B7DA3C009F8BE7 /* OOMeetingPersonTableViewCell.xib in Resources */,
 				E4B2321C20B3E94E0082F30A /* OOAttanceCheckinPromptView.xib in Resources */,
 				E4C24B6120844F3C00E426B0 /* addressCode in Resources */,
 				E4B697D920764A2D0062F6E8 /* myshareEditorByPerson.json in Resources */,
 				E457559D1E0BA72E00EC44F4 /* qrcode_scan_btn_flash_down@2x.png in Resources */,
+				B1489B1D248E0F4D009EE9FD /* IMChatViewController.xib in Resources */,
 				E4C24C1A20844F5200E426B0 /* emoticons.bundle in Resources */,
 				E4B888E11D9D48F1002E1A46 /* contacts.storyboard in Resources */,
 				E45755A51E0BA72E00EC44F4 /* qrcode_scan_light_green@2x.png in Resources */,
@@ -5504,6 +5599,7 @@
 				E46E6CAF1DD41F5D00AB7561 /* ZSSunlink.png in Resources */,
 				E40E24C320B7DA3C009F8BE7 /* OOMeetingInforItemCell.xib in Resources */,
 				E46E6C811DD41F5D00AB7561 /* ZSShorizontalrule.png in Resources */,
+				B1173B5E2489F48C005075F0 /* O2Emoji.bundle in Resources */,
 				E46E6C7F1DD41F5D00AB7561 /* ZSSh6.png in Resources */,
 				E46E6C9C1DD41F5D00AB7561 /* ZSSquicklink@2x.png in Resources */,
 				E46E6C901DD41F5D00AB7561 /* ZSSleftjustify@2x.png in Resources */,
@@ -5519,6 +5615,7 @@
 				E4B889071D9D4C7E002E1A46 /* podfile in Resources */,
 				E46E6C841DD41F5D00AB7561 /* ZSSimage@2x.png in Resources */,
 				E4B697012075DE5F0062F6E8 /* OOGuidePageController.xib in Resources */,
+				B1489BFF2490BE51009EE9FD /* IMChatMessageSendViewCell.xib in Resources */,
 				E4D23110209C40F600837868 /* OOPersonCollectionViewCell.xib in Resources */,
 				B167F7672362C60200F182B8 /* CloudFileMoveFolderCell.xib in Resources */,
 				E46E6CAD1DD41F5D00AB7561 /* ZSSundo.png in Resources */,
@@ -5630,6 +5727,7 @@
 				"${BUILT_PRODUCTS_DIR}/RxSwift/RxSwift.framework",
 				"${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
 				"${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework",
+				"${BUILT_PRODUCTS_DIR}/Starscream/Starscream.framework",
 				"${BUILT_PRODUCTS_DIR}/SwiftValidator/SwiftValidator.framework",
 				"${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework",
 				"${BUILT_PRODUCTS_DIR}/SwiftyTimer/SwiftyTimer.framework",
@@ -5672,6 +5770,7 @@
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxSwift.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SnapKit.framework",
+				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Starscream.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftValidator.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyJSON.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyTimer.framework",
@@ -5849,6 +5948,7 @@
 				E4B888711D9D48F1002E1A46 /* ContactSearchViewController.swift in Sources */,
 				B130A7852281597000282AD1 /* DeviceManagerViewModel.swift in Sources */,
 				E40E24BF20B7DA3C009F8BE7 /* OOMeetingRoomDeviceListView.swift in Sources */,
+				B1173A632488C8EA005075F0 /* IMConversationInfo.swift in Sources */,
 				E4A748D51EC15EF100163F58 /* TaskAttachmentPreviewController.swift in Sources */,
 				E4B888A51D9D48F1002E1A46 /* MJRefreshBackStateFooter.m in Sources */,
 				E4B8885E1D9D48F1002E1A46 /* O2CollectionView.swift in Sources */,
@@ -5858,9 +5958,11 @@
 				E4D2310F209C40F600837868 /* OOPersonCollectionViewCell.swift in Sources */,
 				E4C24B4A20844F3C00E426B0 /* JCMessageImageCollectionViewCell.swift in Sources */,
 				E4C24B6920844F3C00E426B0 /* DLSlideView.m in Sources */,
+				B1173A672488CD5B005075F0 /* IMConversationItemCell.swift in Sources */,
 				B1534E3F21F712EA00CC8C35 /* O2DemoAlertView.swift in Sources */,
 				E4C24B7F20844F3C00E426B0 /* ImageFileCell.swift in Sources */,
 				B165CD602242093500373B66 /* AlertViewController.swift in Sources */,
+				B1173A5E2488C5DF005075F0 /* CommunicateAPI.swift in Sources */,
 				E441994A206C8A100050AE0C /* OORegisterTableView.swift in Sources */,
 				E40E24A920B7DA3C009F8BE7 /* OOMeetingCreateViewModel.swift in Sources */,
 				E40C41ED1E7F7C0D00568805 /* ZoneSubCategoryViewController.swift in Sources */,
@@ -5896,6 +5998,7 @@
 				B1DE856623603408003C36E2 /* DispatchQueue+Extension.swift in Sources */,
 				E4B69772207630240062F6E8 /* String+Extenstion.swift in Sources */,
 				B175007A233C6908003DA7B9 /* BlockLongPress.swift in Sources */,
+				B1173A5B2488C546005075F0 /* IMConversationListViewController.swift in Sources */,
 				E4B69734207602A40062F6E8 /* OOAPIContext.swift in Sources */,
 				09E02E881F16319600579887 /* DiskFetcher.swift in Sources */,
 				E4C24B5520844F3C00E426B0 /* SAIToolboxInputView.swift in Sources */,
@@ -5903,6 +6006,7 @@
 				E4C24B5320844F3C00E426B0 /* SAIInputView.swift in Sources */,
 				E40E24CF20B7DA3C009F8BE7 /* OOFormDateItemView.swift in Sources */,
 				B1FBA01D230A3AB500A90722 /* O2JsApiBizUtil.swift in Sources */,
+				B1489B51248E192D009EE9FD /* IMChatMessageViewCell.swift in Sources */,
 				E4B69776207630240062F6E8 /* UIView+Extension.swift in Sources */,
 				E4C24BB420844F3C00E426B0 /* JCMessageContentViewType.swift in Sources */,
 				E4C24B9420844F3C00E426B0 /* JCGroupSettingCell.swift in Sources */,
@@ -6004,6 +6108,7 @@
 				B1EE2CCE2281729400842F48 /* QDatePicker.swift in Sources */,
 				E4B6975C20762EA00062F6E8 /* OOUIDownButtonTextField.swift in Sources */,
 				E4C24C0720844F4400E426B0 /* JCSearchFriendViewController.swift in Sources */,
+				B1173A602488C82F005075F0 /* IMViewModel.swift in Sources */,
 				E46E6CBB1DD41F5D00AB7561 /* HRBrightnessCursor.m in Sources */,
 				E4B697E920764A2D0062F6E8 /* OOWorkAPI.swift in Sources */,
 				E40E24C720B7DA3C009F8BE7 /* OOFormSegueItemView.swift in Sources */,
@@ -6282,6 +6387,7 @@
 				E4B888A41D9D48F1002E1A46 /* MJRefreshBackNormalFooter.m in Sources */,
 				E4B2320F20B2A9F60082F30A /* OOAttandanceWorkPlaceController.swift in Sources */,
 				E46E6CB91DD41F5D00AB7561 /* CYRTextView.m in Sources */,
+				B1489B1C248E0F4D009EE9FD /* IMChatViewController.swift in Sources */,
 				B175007C233C6908003DA7B9 /* BlockTap.swift in Sources */,
 				E4C24B5020844F3C00E426B0 /* SAIInputAccessoryViewLayout.swift in Sources */,
 				E4AA17151EE158E70030D9AB /* LogListViewController.swift in Sources */,
@@ -6371,6 +6477,7 @@
 				E4C24B8D20844F3C00E426B0 /* JCMessageLocationContentView.swift in Sources */,
 				E4CDB8851E7CFB9C008E788D /* ZLNormalNavViewController.swift in Sources */,
 				B1DA305F2282754500669418 /* QCalendarPicker.swift in Sources */,
+				B14E0C0C2484F1F0008AF6AE /* O2WebsocketManager.swift in Sources */,
 				E46E6CBC1DD41F5D00AB7561 /* HRCgUtil.m in Sources */,
 				E496E89E207314B100C68EEE /* NavView.swift in Sources */,
 				E4C24BD720844F3C00E426B0 /* JCMyInfoViewController.swift in Sources */,
@@ -6395,6 +6502,7 @@
 				E4C24BD920844F3C00E426B0 /* JCFeedbackViewController.swift in Sources */,
 				E4C24C4C208D7EDE00E426B0 /* OOCDLCell.swift in Sources */,
 				B158E95E215DD3F500AB2727 /* AIConstants.swift in Sources */,
+				B1489BFE2490BE51009EE9FD /* IMChatMessageSendViewCell.swift in Sources */,
 				E4BF45D71E74FB0C008B52F0 /* ImageSlidesShowView.swift in Sources */,
 				E4C24B5820844F3C00E426B0 /* SAIInputItem.swift in Sources */,
 				E40E24DF20B7E66E009F8BE7 /* O2BBSCreatorView.swift in Sources */,
@@ -6417,6 +6525,7 @@
 				E4C24B8C20844F3C00E426B0 /* JCMessageNoticeContentView.swift in Sources */,
 				E4C24C3E208D7EDE00E426B0 /* OOContactViewModel.swift in Sources */,
 				E4D2312E20A29E7500837868 /* OOAppMainCollectionHeaderView.swift in Sources */,
+				B1173A6A2488D4AD005075F0 /* O2IM.swift in Sources */,
 				E4C24B5220844F3C00E426B0 /* SAIToolboxItemView.swift in Sources */,
 				E4C24B3C20844F3C00E426B0 /* Date+JChat.swift in Sources */,
 				E4C24C0120844F4400E426B0 /* JCUserInfoCell.swift in Sources */,

+ 162 - 0
o2ios/O2Platform/App/IM-聊天/IMChatViewController.swift

@@ -0,0 +1,162 @@
+//
+//  IMChatViewController.swift
+//  O2Platform
+//
+//  Created by FancyLou on 2020/6/8.
+//  Copyright © 2020 zoneland. All rights reserved.
+//
+
+import UIKit
+import CocoaLumberjack
+import O2OA_Auth_SDK
+
+class IMChatViewController: UIViewController {
+
+    // MARK: - IBOutlet
+    //消息列表
+    @IBOutlet weak var tableView: UITableView!
+    //消息输入框
+    @IBOutlet weak var messageInputView: UITextField!
+    //底部工具栏的高度约束
+    @IBOutlet weak var bottomBarHeightConstraint: NSLayoutConstraint!
+    //底部工具栏
+    @IBOutlet weak var bottomBar: UIView!
+
+    private lazy var viewModel: IMViewModel = {
+        return IMViewModel()
+    }()
+
+    // MARK: - properties
+    var conversation: IMConversationInfo? = nil
+    private var chatMessageList: [IMMessageInfo] = []
+    private var page = 1
+    private var isShowEmoji = false
+    private var bottomBarHeight = 64
+
+
+    // MARK: - functions
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        self.tableView.delegate = self
+        self.tableView.dataSource = self
+        self.tableView.register(UINib(nibName: "IMChatMessageViewCell", bundle: nil), forCellReuseIdentifier: "IMChatMessageViewCell")
+        self.tableView.register(UINib(nibName: "IMChatMessageSendViewCell", bundle: nil), forCellReuseIdentifier: "IMChatMessageSendViewCell")
+        self.tableView.separatorStyle = .none
+        self.tableView.rowHeight = UITableView.automaticDimension
+        self.tableView.estimatedRowHeight = 144
+        self.messageInputView.delegate = self
+
+        //底部安全距离 老机型没有
+        self.bottomBarHeight = Int(iPhoneX ? 64 + IPHONEX_BOTTOM_SAFE_HEIGHT: 64)
+        self.bottomBarHeightConstraint.constant = self.bottomBarHeight.toCGFloat
+        self.bottomBar.topBorder(width: 1, borderColor: base_gray_color.alpha(0.5))
+        self.messageInputView.backgroundColor = base_gray_color
+
+        //标题
+        if let c = self.conversation {
+            var person = ""
+            c.personList?.forEach({ (p) in
+                if  p != O2AuthSDK.shared.myInfo()?.distinguishedName {
+                    person = p
+                }
+            })
+            if !person.isEmpty {
+                self.title = person.split("@").first ?? ""
+            }
+        }
+        //获取聊天数据
+        self.loadMsgList(page: page)
+    }
+
+    //获取消息
+    private func loadMsgList(page: Int) {
+        if let c = self.conversation, let id = c.id {
+            self.viewModel.myMsgPageList(page: page, conversationId: id).then { (list) in
+                self.chatMessageList = list
+                DispatchQueue.main.async {
+                    self.tableView.reloadData()
+                    if self.chatMessageList.count > 0 {
+                        self.tableView.scrollToRow(at: IndexPath(row: self.chatMessageList.count-1, section: 0), at: .bottom, animated: true)
+                    }
+                }
+            }
+        } else {
+            self.showError(title: "参数错误!!!")
+        }
+    }
+
+
+    // MARK: - IBAction
+    //点击表情按钮
+    @IBAction func clickEmojiBtn(_ sender: UIButton) {
+        self.isShowEmoji.toggle()
+        self.view.endEditing(true)
+        if self.isShowEmoji {
+            self.bottomBarHeightConstraint.constant = self.bottomBarHeight.toCGFloat + 128
+        } else {
+            self.bottomBarHeightConstraint.constant = self.bottomBarHeight.toCGFloat
+
+        }
+        self.view.layoutIfNeeded()
+    }
+
+
+
+}
+// MARK: - tableview delegate
+extension IMChatViewController: UITableViewDelegate, UITableViewDataSource {
+
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return self.chatMessageList.count
+    }
+
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let msg = self.chatMessageList[indexPath.row]
+        if msg.createPerson == O2AuthSDK.shared.myInfo()?.distinguishedName { //发送者
+            if let cell = tableView.dequeueReusableCell(withIdentifier: "IMChatMessageSendViewCell", for: indexPath) as? IMChatMessageSendViewCell {
+                cell.setContent(item: self.chatMessageList[indexPath.row])
+                return cell
+            }
+        }else {
+            if let cell = tableView.dequeueReusableCell(withIdentifier: "IMChatMessageViewCell", for: indexPath) as? IMChatMessageViewCell {
+                cell.setContent(item: self.chatMessageList[indexPath.row])
+                return cell
+            }
+        }
+        return UITableViewCell()
+    }
+    
+    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        
+        tableView.deselectRow(at: indexPath, animated: false)
+    }
+//    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
+//        guard let c = cell as? IMChatMessageViewCell else {
+//            return
+//        }
+//        //todo
+//        c.setContent(item: self.chatMessageList[indexPath.row])
+//    }
+
+
+}
+
+// MARK: - textField delegate
+extension IMChatViewController: UITextFieldDelegate {
+    func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
+        DDLogDebug("准备开始输入......")
+        closeEmoji()
+        return true
+    }
+
+    private func closeEmoji() {
+        self.isShowEmoji = false
+        self.bottomBarHeightConstraint.constant = 64
+        self.view.layoutIfNeeded()
+    }
+
+    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
+        DDLogDebug("回车。。。。")
+        return true
+    }
+}

+ 81 - 0
o2ios/O2Platform/App/IM-聊天/IMChatViewController.xib

@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina6_1" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="IMChatViewController" customModule="O2Platform" customModuleProvider="target">
+            <connections>
+                <outlet property="bottomBar" destination="sDg-us-Ed9" id="J1d-XZ-5Rt"/>
+                <outlet property="bottomBarHeightConstraint" destination="qiu-7O-AwO" id="LWR-Ov-DMW"/>
+                <outlet property="messageInputView" destination="8BW-XG-rBx" id="A8J-9I-MjT"/>
+                <outlet property="tableView" destination="ZIb-3p-hnq" id="heR-v3-iUh"/>
+                <outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
+            </connections>
+        </placeholder>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
+            <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+            <subviews>
+                <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="ZIb-3p-hnq">
+                    <rect key="frame" x="0.0" y="44" width="414" height="754"/>
+                    <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
+                    <color key="sectionIndexBackgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                </tableView>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="sDg-us-Ed9">
+                    <rect key="frame" x="0.0" y="798" width="414" height="98"/>
+                    <subviews>
+                        <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="请输入消息..." textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="8BW-XG-rBx">
+                            <rect key="frame" x="14" y="12" width="328" height="36"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="36" id="qe1-5v-UaD"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                            <textInputTraits key="textInputTraits" returnKeyType="send"/>
+                        </textField>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="oTD-nU-xkE">
+                            <rect key="frame" x="366" y="12" width="36" height="36"/>
+                            <constraints>
+                                <constraint firstAttribute="width" constant="36" id="AzT-Gm-fHl"/>
+                                <constraint firstAttribute="height" constant="36" id="PqN-1z-eCd"/>
+                            </constraints>
+                            <state key="normal" image="chat_emoji"/>
+                            <connections>
+                                <action selector="clickEmojiBtn:" destination="-1" eventType="touchUpInside" id="hZk-xk-g8t"/>
+                            </connections>
+                        </button>
+                    </subviews>
+                    <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstAttribute="trailing" secondItem="oTD-nU-xkE" secondAttribute="trailing" constant="12" id="BtK-YW-2fb"/>
+                        <constraint firstItem="oTD-nU-xkE" firstAttribute="top" secondItem="sDg-us-Ed9" secondAttribute="top" constant="12" id="JKE-h8-gll"/>
+                        <constraint firstItem="oTD-nU-xkE" firstAttribute="leading" secondItem="8BW-XG-rBx" secondAttribute="trailing" constant="24" id="Mzm-dM-gqy"/>
+                        <constraint firstItem="8BW-XG-rBx" firstAttribute="top" secondItem="sDg-us-Ed9" secondAttribute="top" constant="12" id="hnf-bY-DYH"/>
+                        <constraint firstItem="8BW-XG-rBx" firstAttribute="leading" secondItem="sDg-us-Ed9" secondAttribute="leading" constant="14" id="kAO-OU-1mN"/>
+                        <constraint firstAttribute="height" constant="98" id="qiu-7O-AwO"/>
+                    </constraints>
+                </view>
+            </subviews>
+            <color key="backgroundColor" systemColor="systemGray6Color" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+            <constraints>
+                <constraint firstItem="ZIb-3p-hnq" firstAttribute="top" secondItem="fnl-2z-Ty3" secondAttribute="top" id="7kr-oh-NvU"/>
+                <constraint firstAttribute="trailing" secondItem="sDg-us-Ed9" secondAttribute="trailing" id="SXj-Vg-GIC"/>
+                <constraint firstAttribute="bottom" secondItem="sDg-us-Ed9" secondAttribute="bottom" id="ZjF-u3-yyV"/>
+                <constraint firstItem="ZIb-3p-hnq" firstAttribute="leading" secondItem="fnl-2z-Ty3" secondAttribute="leading" id="d3C-Up-hv7"/>
+                <constraint firstItem="ZIb-3p-hnq" firstAttribute="trailing" secondItem="fnl-2z-Ty3" secondAttribute="trailing" id="igh-CL-C1S"/>
+                <constraint firstItem="sDg-us-Ed9" firstAttribute="top" secondItem="ZIb-3p-hnq" secondAttribute="bottom" id="rTN-Zh-8i6"/>
+                <constraint firstItem="sDg-us-Ed9" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" id="rqK-P9-wyk"/>
+            </constraints>
+            <viewLayoutGuide key="safeArea" id="fnl-2z-Ty3"/>
+            <point key="canvasLocation" x="131.8840579710145" y="138.61607142857142"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="chat_emoji" width="24" height="24"/>
+    </resources>
+</document>

+ 133 - 0
o2ios/O2Platform/App/IM-聊天/IMConversationListViewController.swift

@@ -0,0 +1,133 @@
+//
+//  IMConversationListViewController.swift
+//  O2Platform
+//
+//  Created by FancyLou on 2020/6/4.
+//  Copyright © 2020 zoneland. All rights reserved.
+//
+
+import UIKit
+import CocoaLumberjack
+
+class IMConversationListViewController: UIViewController {
+
+    fileprivate lazy var tableview: UITableView = {
+        var tableview = UITableView(frame: CGRect(x: 0, y: 0, width: self.view.width, height: self.view.height))
+        tableview.delegate = self
+        tableview.dataSource = self
+        tableview.backgroundColor = UIColor(netHex: 0xe8edf3)
+        tableview.register(UINib(nibName: "IMConversationItemCell", bundle: nil), forCellReuseIdentifier: "IMConversationItemCell")
+        tableview.separatorStyle = .none
+        return tableview
+    }()
+
+    fileprivate lazy var emptyView: UIView = {
+        let view = UIView(frame: CGRect(x: 0, y: 36, width: self.view.width, height: self.view.height - 36))
+        view.isHidden = true
+        view.backgroundColor = .white
+        let tips = UILabel()
+        tips.text = "暂无会话"
+        tips.textColor = UIColor(netHex: 0x666666)
+        tips.sizeToFit()
+        tips.center = CGPoint(x: view.centerX, y: view.height / 2 - 60)
+        view.addSubview(tips)
+        return view
+    }()
+
+    private lazy var viewModel: IMViewModel = {
+        return IMViewModel()
+    }()
+
+    private var conversationList: [IMConversationInfo] = []
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        view.addSubview(tableview)
+        view.addSubview(emptyView)
+        
+        NotificationCenter.default.addObserver(self, selector: #selector(receiveMessageFromWs(notice:)), name: OONotification.websocket.notificationName, object: nil)
+        
+         
+    }
+    
+    override func viewWillAppear(_ animated: Bool) {
+        getConversationList()
+    }
+
+    func getConversationList() {
+        viewModel.myConversationList().then { (list) in
+            self.conversationList = list
+            DispatchQueue.main.async {
+                if self.conversationList.count > 0 {
+                    self.emptyView.isHidden = true
+                } else {
+                    self.emptyView.isHidden = false
+                }
+                self.tableview.reloadData()
+            }
+
+        }.catch { (err) in
+            DispatchQueue.main.async { self.emptyView.isHidden = false }
+        }
+    }
+
+    //接收websocket消息
+    @objc private func receiveMessageFromWs(notice: Notification) {
+        DDLogDebug("接收到websocket im 消息")
+        if let message = notice.object as? IMMessageInfo {
+            if self.conversationList.contains(where: { (info) -> Bool in
+                return info.id == message.conversationId
+            }) {
+                DDLogDebug("有对应的会话 刷新列表")
+                var newList: [IMConversationInfo]  = []
+                self.conversationList.forEach { (info) in
+                    if message.conversationId != nil && info.id == message.conversationId {
+                        info.lastMessage = message
+                        info.unreadNumber = (info.unreadNumber ?? 0) + 1
+                    }
+                    newList.append(info)
+                }
+                self.conversationList = newList
+                DispatchQueue.main.async {
+                    self.tableview.reloadData()
+                }
+            }else {
+                DDLogDebug("没有对应的会话 重新获取会话列表")
+                self.getConversationList()
+            }
+        }else {
+            DDLogError("不正确的消息类型。。。")
+        }
+    }
+    
+    
+
+}
+
+// MARK: - tableview delegate
+extension IMConversationListViewController: UITableViewDelegate, UITableViewDataSource {
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return self.conversationList.count
+    }
+
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        return tableView.dequeueReusableCell(withIdentifier: "IMConversationItemCell", for: indexPath)
+    }
+    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
+        guard let c = cell as? IMConversationItemCell else {
+            return
+        }
+        c.bindConversation(conversation: self.conversationList[indexPath.row])
+    }
+    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+        return 64
+    }
+    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        DDLogDebug("点击了 row \(indexPath.row)")
+        let chatView = IMChatViewController()
+        chatView.conversation = self.conversationList[indexPath.row]
+        self.navigationController?.pushViewController(chatView, animated: true)
+    }
+    //todo can edit
+
+}

+ 63 - 0
o2ios/O2Platform/App/IM-聊天/IMViewModel.swift

@@ -0,0 +1,63 @@
+//
+//  IMViewModel.swift
+//  O2Platform
+//
+//  Created by FancyLou on 2020/6/4.
+//  Copyright © 2020 zoneland. All rights reserved.
+//
+
+import Promises
+
+
+class IMViewModel: NSObject {
+    override init() {
+        super.init()
+    }
+    
+    
+    let communicateAPI = OOMoyaProvider<CommunicateAPI>()
+}
+
+extension IMViewModel {
+    //查询会话列表
+    func myConversationList() -> Promise<[IMConversationInfo]> {
+        return Promise { fulfill, reject in
+            self.communicateAPI.request(.myConversationList, completion: { result in
+                let response = OOResult<BaseModelClass<[IMConversationInfo]>>(result)
+                if response.isResultSuccess() {
+                    if let list = response.model?.data {
+                        fulfill(list)
+                    }else {
+                        reject(OOAppError.apiEmptyResultError)
+                    }
+                }else {
+                    reject(response.error!)
+                }
+            })
+        }
+    }
+    //查询消息列表
+    func myMsgPageList(page: Int, conversationId: String) -> Promise<[IMMessageInfo]> {
+        return Promise { fulfill, reject in
+            self.communicateAPI.request(.msgListByPaging(page, 40, conversationId), completion: { result in
+                let response = OOResult<BaseModelClass<[IMMessageInfo]>>(result)
+                if response.isResultSuccess() {
+                    if let list = response.model?.data {
+                        //列表翻转
+                        let rList = list.sorted { (f, s) -> Bool in
+                            if let ft = f.createTime, let st = s.createTime {
+                                return ft.toDate(formatter: "yyyy-MM-dd HH:mm:ss") < st.toDate(formatter: "yyyy-MM-dd HH:mm:ss")
+                            }
+                            return true
+                        }
+                        fulfill(rList)
+                    }else {
+                        reject(OOAppError.apiEmptyResultError)
+                    }
+                }else {
+                    reject(response.error!)
+                }
+            })
+        }
+    }
+}

+ 83 - 0
o2ios/O2Platform/App/IM-聊天/Model/IMConversationInfo.swift

@@ -0,0 +1,83 @@
+//
+//  IMConversation.swift
+//  O2Platform
+//
+//  Created by FancyLou on 2020/6/4.
+//  Copyright © 2020 zoneland. All rights reserved.
+//
+import HandyJSON
+
+
+class IMConversationInfo: NSObject, DataModel {
+    @objc var id: String?
+    @objc var title: String?
+    @objc var type: String? //会话类型 单人 、 群.
+    @objc var personList: [String]?
+    @objc var adminPerson: String?
+    @objc var note: String?
+
+    @objc var lastMessageTime: String?
+    @objc var createTime: String?
+    @objc var updateTime: String?
+    var unreadNumber: Int?
+    var isTop: Bool?
+
+    @objc var lastMessage: IMMessageInfo?
+
+    required override init() { }
+
+    func mapping(mapper: HelpingMapper) {
+
+    }
+}
+
+
+class IMMessageRequestForm: NSObject, DataModel {
+
+    @objc var conversationId: String?
+
+    required override init() { }
+
+    func mapping(mapper: HelpingMapper) {
+
+    }
+}
+
+class IMMessageInfo: NSObject, DataModel {
+    @objc var id: String?
+    @objc var conversationId: String?
+    @objc var body: String?
+    @objc var createPerson: String?
+    @objc var createTime: String?
+    @objc var updateTime: String?
+
+    required override init() { }
+
+    func mapping(mapper: HelpingMapper) {
+
+    }
+}
+
+class IMMessageBodyInfo: NSObject, DataModel {
+    @objc var id: String?
+    @objc var type: String?
+    @objc var body: String?
+
+
+    required override init() { }
+
+    func mapping(mapper: HelpingMapper) {
+
+    }
+}
+
+//websocket 消息对象
+class WsMessage: NSObject, DataModel {
+    @objc var type: String? //im_create
+    @objc var body: IMMessageInfo? //这个对象只有 type=im_create的时候才是这个对象
+    required override init() { }
+
+    func mapping(mapper: HelpingMapper) {
+
+    }
+}

+ 28 - 0
o2ios/O2Platform/App/IM-聊天/O2IM.swift

@@ -0,0 +1,28 @@
+//
+//  O2IM.swift
+//  O2Platform
+//
+//  Created by FancyLou on 2020/6/4.
+//  Copyright © 2020 zoneland. All rights reserved.
+//
+
+import Foundation
+
+//心跳消息
+let o2_im_ws_heartbeat = "heartbeat"
+
+
+let o2_im_conversation_type_single = "single"
+
+let o2_im_msg_type_text = "text"
+let o2_im_msg_type_emoji = "emoji"
+
+
+//表情的字符串转化为O2Emoji.bundle里面的图片路径 [01] -> im_emotion_01
+func o2ImEmojiPath(emojiBody: String) -> String {
+    if emojiBody.length == 4 {
+        let s = emojiBody.subString(from: 1, to: 3)
+        return "im_emotion_\(s)"
+    }
+    return ""
+}

+ 133 - 0
o2ios/O2Platform/App/IM-聊天/View/IMChatMessageSendViewCell.swift

@@ -0,0 +1,133 @@
+//
+//  IMChatMessageSendViewCell.swift
+//  O2Platform
+//
+//  Created by FancyLou on 2020/6/10.
+//  Copyright © 2020 zoneland. All rights reserved.
+//
+
+import UIKit
+
+class IMChatMessageSendViewCell: UITableViewCell {
+    @IBOutlet weak var timeLabel: UILabel!
+    @IBOutlet weak var avatarImageView: UIImageView!
+    @IBOutlet weak var nameLabel: UILabel!
+     
+    @IBOutlet weak var messageBackgroundView: UIView!
+    
+    @IBOutlet weak var messageBgWidth: NSLayoutConstraint!
+    @IBOutlet weak var messageBgHeight: NSLayoutConstraint!
+    
+    
+    
+    override func awakeFromNib() {
+        super.awakeFromNib()
+    }
+
+    override func setSelected(_ selected: Bool, animated: Bool) {
+        super.setSelected(selected, animated: animated)
+    }
+    
+    func setContent(item: IMMessageInfo) {
+        //time
+        if let time = item.createTime {
+            let date = time.toDate(formatter: "yyyy-MM-dd HH:mm:ss")
+            self.timeLabel.text = date.friendlyTime()
+        }
+        //name avatart
+        if let person = item.createPerson {
+            let urlstr = AppDelegate.o2Collect.generateURLWithAppContextKey(ContactContext.contactsContextKeyV2, query: ContactContext.personIconByNameQueryV2, parameter: ["##name##":person as AnyObject], generateTime: false)
+            if let u = URL(string: urlstr!) {
+                self.avatarImageView.hnk_setImageFromURL(u)
+            }else {
+                self.avatarImageView.image = UIImage(named: "icon_men")
+            }
+            //姓名
+            self.nameLabel.text = person.split("@").first ?? ""
+        }else {
+            self.avatarImageView.image = UIImage(named: "icon_men")
+            self.nameLabel.text = ""
+        }
+        self.messageBackgroundView.removeSubviews()
+        if let jsonBody = item.body, let body = parseJson(msg: jsonBody) {
+            if body.type == o2_im_msg_type_emoji {
+                emojiMsgRender(emoji: body.body!)
+            }else {
+                textMsgRender(msg: body.body!)
+            }
+        }
+    }
+    
+    
+    private func emojiMsgRender(emoji: String) {
+        let emojiSize = 36
+        let width = CGFloat(emojiSize + 20)
+        let height = CGFloat(emojiSize + 20)
+        self.messageBgWidth.constant = width
+        self.messageBgHeight.constant = height
+        //背景图片
+        let bgImg = UIImageView(frame: CGRect(x: 0, y: 0, width: width, height: height))
+        let insets = UIEdgeInsets(top: 28, left: 5, bottom: 5, right: 10); // 上、左、下、右
+        var bubble = UIImage(named: "chat_bubble_outgoing")
+        bubble = bubble?.resizableImage(withCapInsets: insets, resizingMode: .stretch)
+        bgImg.image = bubble
+        self.messageBackgroundView.addSubview(bgImg)
+        //表情图
+        let emojiImage = UIImageView(frame: CGRect(x: 0, y: 0, width: emojiSize, height: emojiSize))
+        let bundle = Bundle().o2EmojiBundle(anyClass: IMChatMessageSendViewCell.self)
+        let path = o2ImEmojiPath(emojiBody: emoji)
+        emojiImage.image = UIImage(named: path, in: bundle, compatibleWith: nil)
+        emojiImage.translatesAutoresizingMaskIntoConstraints = false
+        self.messageBackgroundView.addSubview(emojiImage)
+        let top = NSLayoutConstraint(item: emojiImage, attribute: .top, relatedBy: .equal, toItem: emojiImage.superview!, attribute: .top, multiplier: 1, constant: 10)
+        let bottom = NSLayoutConstraint(item: emojiImage.superview! , attribute: .bottom, relatedBy: .equal, toItem: emojiImage, attribute: .bottom, multiplier: 1, constant: 10)
+        let left = NSLayoutConstraint(item: emojiImage, attribute: .leading, relatedBy: .equal, toItem: emojiImage.superview!, attribute: .leading, multiplier: 1, constant: 10)
+        let right = NSLayoutConstraint(item: emojiImage.superview!, attribute: .trailing, relatedBy: .equal, toItem: emojiImage, attribute: .trailing, multiplier: 1, constant: 10)
+        NSLayoutConstraint.activate([top, bottom, left, right])
+    }
+    
+    private func textMsgRender(msg: String) {
+        let size = calTextSize(str: msg)
+        self.messageBgWidth.constant = size.width + 20
+        self.messageBgHeight.constant = size.height + 20
+        //背景图片
+        let bgImg = UIImageView(frame: CGRect(x: 0, y: 0, width: size.width + 20, height: size.height + 20))
+        let insets = UIEdgeInsets(top: 28, left: 5, bottom: 5, right: 10); // 上、左、下、右
+        var bubble = UIImage(named: "chat_bubble_outgoing")
+        bubble = bubble?.resizableImage(withCapInsets: insets, resizingMode: .stretch)
+        bgImg.image = bubble
+        self.messageBackgroundView.addSubview(bgImg)
+        //文字
+        let label = generateMessagelabel(str: msg, size: size)
+        label.translatesAutoresizingMaskIntoConstraints = false
+        self.messageBackgroundView.addSubview(label)
+        let top = NSLayoutConstraint(item: label, attribute: .top, relatedBy: .equal, toItem: label.superview!, attribute: .top, multiplier: 1, constant: 10)
+//        let bottom = NSLayoutConstraint(item: label.superview! , attribute: .bottom, relatedBy: .equal, toItem: label, attribute: .bottom, multiplier: 1, constant: 10)
+        let left = NSLayoutConstraint(item: label, attribute: .leading, relatedBy: .equal, toItem: label.superview!, attribute: .leading, multiplier: 1, constant: 10)
+        let right = NSLayoutConstraint(item: label.superview!, attribute: .trailing, relatedBy: .equal, toItem: label, attribute: .trailing, multiplier: 1, constant: 10)
+        NSLayoutConstraint.activate([top, left, right])
+    }
+    
+    private func generateMessagelabel(str: String, size: CGSize) -> UILabel {
+        let label = UILabel(frame: CGRect(x: 0, y: 0, width: size.width, height: size.height))
+        label.text = str
+        label.font = UIFont.systemFont(ofSize: 16)
+        label.numberOfLines = 0
+        label.lineBreakMode = .byCharWrapping
+        label.preferredMaxLayoutWidth = size.width
+        
+        return label
+    }
+    
+    
+    private func calTextSize(str: String) -> CGSize {
+        let size = CGSize(width: 176, height: CGFloat(MAXFLOAT))
+        return str.boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16)], context: nil).size
+    }
+    
+    //解析json为消息对象
+    private func parseJson(msg: String) -> IMMessageBodyInfo? {
+        return IMMessageBodyInfo.deserialize(from: msg)
+    }
+    
+}

+ 84 - 0
o2ios/O2Platform/App/IM-聊天/View/IMChatMessageSendViewCell.xib

@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina6_1" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.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"/>
+        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="129" id="KGk-i7-Jjw" customClass="IMChatMessageSendViewCell" customModule="O2Platform" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="320" height="129"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" ambiguous="YES" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
+                <rect key="frame" x="0.0" y="0.0" width="320" height="129"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="06-09 13:02" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="k0L-ig-bhB">
+                        <rect key="frame" x="14" y="5" width="292" height="18"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="18" id="MJ9-1n-RP4"/>
+                        </constraints>
+                        <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                        <color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                    <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dRS-Ex-V2s">
+                        <rect key="frame" x="258" y="31" width="48" height="48"/>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="48" id="ALp-AJ-k89"/>
+                            <constraint firstAttribute="height" constant="48" id="U0b-CA-lXO"/>
+                        </constraints>
+                        <userDefinedRuntimeAttributes>
+                            <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                <real key="value" value="24"/>
+                            </userDefinedRuntimeAttribute>
+                        </userDefinedRuntimeAttributes>
+                    </imageView>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="姓名" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="O0d-aO-DEf">
+                        <rect key="frame" x="219" y="31" width="29" height="24"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="24" id="hV4-hf-Q8a"/>
+                        </constraints>
+                        <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                        <nil key="textColor"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                    <view contentMode="scaleToFill" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="73U-Yu-hhb">
+                        <rect key="frame" x="72" y="63" width="176" height="42"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="42" id="9m9-a5-elu"/>
+                            <constraint firstAttribute="width" constant="176" id="B2b-ep-gRb"/>
+                        </constraints>
+                    </view>
+                </subviews>
+                <color key="backgroundColor" red="0.95294117649999999" green="0.95294117649999999" blue="0.95294117649999999" alpha="1" colorSpace="calibratedRGB"/>
+                <constraints>
+                    <constraint firstItem="k0L-ig-bhB" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="5" id="59J-ml-PYh"/>
+                    <constraint firstItem="73U-Yu-hhb" firstAttribute="top" secondItem="O0d-aO-DEf" secondAttribute="bottom" constant="8" id="6MU-oN-E2m"/>
+                    <constraint firstItem="k0L-ig-bhB" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="14" id="6QF-aq-QM6"/>
+                    <constraint firstItem="dRS-Ex-V2s" firstAttribute="top" secondItem="k0L-ig-bhB" secondAttribute="bottom" constant="8" id="JfT-ce-huE"/>
+                    <constraint firstItem="dRS-Ex-V2s" firstAttribute="leading" secondItem="73U-Yu-hhb" secondAttribute="trailing" constant="10" id="Njy-Bc-KtC"/>
+                    <constraint firstItem="O0d-aO-DEf" firstAttribute="top" secondItem="k0L-ig-bhB" secondAttribute="bottom" constant="8" id="VFs-xs-kta"/>
+                    <constraint firstAttribute="trailing" secondItem="dRS-Ex-V2s" secondAttribute="trailing" constant="14" id="gmx-Y3-81p"/>
+                    <constraint firstAttribute="bottomMargin" secondItem="73U-Yu-hhb" secondAttribute="bottom" constant="10" id="j9W-5A-2om"/>
+                    <constraint firstItem="dRS-Ex-V2s" firstAttribute="leading" secondItem="O0d-aO-DEf" secondAttribute="trailing" constant="10" id="ngR-6P-5QW"/>
+                    <constraint firstAttribute="trailing" secondItem="k0L-ig-bhB" secondAttribute="trailing" constant="14" id="y21-uR-MhX"/>
+                </constraints>
+            </tableViewCellContentView>
+            <viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
+            <connections>
+                <outlet property="avatarImageView" destination="dRS-Ex-V2s" id="Els-ei-27g"/>
+                <outlet property="messageBackgroundView" destination="73U-Yu-hhb" id="JOP-eY-itl"/>
+                <outlet property="messageBgHeight" destination="9m9-a5-elu" id="8Ly-ju-XTf"/>
+                <outlet property="messageBgWidth" destination="B2b-ep-gRb" id="FSo-Zq-jds"/>
+                <outlet property="nameLabel" destination="O0d-aO-DEf" id="Mqt-5X-zmf"/>
+                <outlet property="timeLabel" destination="k0L-ig-bhB" id="ZJg-EA-Jw1"/>
+            </connections>
+            <point key="canvasLocation" x="57.971014492753625" y="111.49553571428571"/>
+        </tableViewCell>
+    </objects>
+</document>

+ 131 - 0
o2ios/O2Platform/App/IM-聊天/View/IMChatMessageViewCell.swift

@@ -0,0 +1,131 @@
+//
+//  IMChatMessageViewCell.swift
+//  O2Platform
+//
+//  Created by FancyLou on 2020/6/8.
+//  Copyright © 2020 zoneland. All rights reserved.
+//
+
+import UIKit
+import CocoaLumberjack
+
+class IMChatMessageViewCell: UITableViewCell {
+
+    @IBOutlet weak var avatarImage: UIImageView!
+    @IBOutlet weak var titleLabel: UILabel!
+    @IBOutlet weak var timeLabel: UILabel!
+    @IBOutlet weak var messageBackgroundView: UIView!
+    @IBOutlet weak var messageBackgroundWidth: NSLayoutConstraint!
+    @IBOutlet weak var messageBackgroundHeight: NSLayoutConstraint!
+    private let messageWidth = 176
+    
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        
+    }
+
+    override func setSelected(_ selected: Bool, animated: Bool) {
+        super.setSelected(selected, animated: animated)
+        // Configure the view for the selected state
+    }
+    
+    func setContent(item: IMMessageInfo) {
+        //time
+        if let time = item.createTime {
+            let date = time.toDate(formatter: "yyyy-MM-dd HH:mm:ss")
+            self.timeLabel.text = date.friendlyTime()
+        }
+        //name avatart
+        if let person = item.createPerson {
+            let urlstr = AppDelegate.o2Collect.generateURLWithAppContextKey(ContactContext.contactsContextKeyV2, query: ContactContext.personIconByNameQueryV2, parameter: ["##name##":person as AnyObject], generateTime: false)
+            if let u = URL(string: urlstr!) {
+                self.avatarImage.hnk_setImageFromURL(u)
+            }else {
+                self.avatarImage.image = UIImage(named: "icon_men")
+            }
+            //姓名
+            self.titleLabel.text = person.split("@").first ?? ""
+        }else {
+            self.avatarImage.image = UIImage(named: "icon_men")
+            self.titleLabel.text = ""
+        }
+        self.messageBackgroundView.removeSubviews()
+        if let jsonBody = item.body, let body = parseJson(msg: jsonBody) {
+            if body.type == o2_im_msg_type_emoji {
+                emojiMsgRender(emoji: body.body!)
+            }else {
+                textMsgRender(msg: body.body!)
+            }
+        }
+    }
+    
+    private func emojiMsgRender(emoji: String) {
+        let emojiSize = 36
+        let width = CGFloat(emojiSize + 20)
+        let height = CGFloat(emojiSize + 20)
+        self.messageBackgroundWidth.constant = width
+        self.messageBackgroundHeight.constant = height
+        //背景图片
+        let bgImg = UIImageView(frame: CGRect(x: 0, y: 0, width: width, height: height))
+        let insets = UIEdgeInsets(top: 28, left: 10, bottom: 5, right: 5); // 上、左、下、右
+        var bubble = UIImage(named: "chat_bubble_incomming")
+        bubble = bubble?.resizableImage(withCapInsets: insets, resizingMode: .stretch)
+        bgImg.image = bubble
+        self.messageBackgroundView.addSubview(bgImg)
+        //表情图
+        let emojiImage = UIImageView(frame: CGRect(x: 0, y: 0, width: emojiSize, height: emojiSize))
+        let bundle = Bundle().o2EmojiBundle(anyClass: IMChatMessageViewCell.self)
+        let path = o2ImEmojiPath(emojiBody: emoji)
+        emojiImage.image = UIImage(named: path, in: bundle, compatibleWith: nil)
+        emojiImage.translatesAutoresizingMaskIntoConstraints = false
+        self.messageBackgroundView.addSubview(emojiImage)
+        let top = NSLayoutConstraint(item: emojiImage, attribute: .top, relatedBy: .equal, toItem: emojiImage.superview!, attribute: .top, multiplier: 1, constant: 10)
+        let bottom = NSLayoutConstraint(item: emojiImage.superview! , attribute: .bottom, relatedBy: .equal, toItem: emojiImage, attribute: .bottom, multiplier: 1, constant: 10)
+        let left = NSLayoutConstraint(item: emojiImage, attribute: .leading, relatedBy: .equal, toItem: emojiImage.superview!, attribute: .leading, multiplier: 1, constant: 10)
+        let right = NSLayoutConstraint(item: emojiImage.superview!, attribute: .trailing, relatedBy: .equal, toItem: emojiImage, attribute: .trailing, multiplier: 1, constant: 10)
+        NSLayoutConstraint.activate([top, bottom, left, right])
+    }
+    
+    private func textMsgRender(msg: String) {
+        let size = calTextSize(str: msg)
+        self.messageBackgroundWidth.constant = size.width + 20
+        self.messageBackgroundHeight.constant = size.height + 20
+        //背景图片
+        let bgImg = UIImageView(frame: CGRect(x: 0, y: 0, width: size.width + 20, height: size.height + 20))
+        let insets = UIEdgeInsets(top: 28, left: 10, bottom: 5, right: 5); // 上、左、下、右
+        var bubble = UIImage(named: "chat_bubble_incomming")
+        bubble = bubble?.resizableImage(withCapInsets: insets, resizingMode: .stretch)
+        bgImg.image = bubble
+        self.messageBackgroundView.addSubview(bgImg)
+        //文字
+        let label = generateMessagelabel(str: msg, size: size)
+        label.translatesAutoresizingMaskIntoConstraints = false
+        self.messageBackgroundView.addSubview(label)
+        let top = NSLayoutConstraint(item: label, attribute: .top, relatedBy: .equal, toItem: label.superview!, attribute: .top, multiplier: 1, constant: 10)
+//        let bottom = NSLayoutConstraint(item: label.superview! , attribute: .bottom, relatedBy: .equal, toItem: label, attribute: .bottom, multiplier: 1, constant: 10)
+        let left = NSLayoutConstraint(item: label, attribute: .leading, relatedBy: .equal, toItem: label.superview!, attribute: .leading, multiplier: 1, constant: 10)
+        let right = NSLayoutConstraint(item: label.superview!, attribute: .trailing, relatedBy: .equal, toItem: label, attribute: .trailing, multiplier: 1, constant: 10)
+        NSLayoutConstraint.activate([top, left, right])
+    }
+    
+    private func generateMessagelabel(str: String, size: CGSize) -> UILabel {
+        let label = UILabel(frame: CGRect(x: 0, y: 0, width: size.width, height: size.height))
+        label.text = str
+        label.font = UIFont.systemFont(ofSize: 16)
+        label.numberOfLines = 0
+        label.lineBreakMode = .byCharWrapping
+        label.preferredMaxLayoutWidth = size.width
+        return label
+    }
+    
+    
+    private func calTextSize(str: String) -> CGSize {
+        let size = CGSize(width: messageWidth.toCGFloat, height: CGFloat(MAXFLOAT))
+        return str.boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16)], context: nil).size
+    }
+    
+    //解析json为消息对象
+    private func parseJson(msg: String) -> IMMessageBodyInfo? {
+        return IMMessageBodyInfo.deserialize(from: msg)
+    }
+}

+ 84 - 0
o2ios/O2Platform/App/IM-聊天/View/IMChatMessageViewCell.xib

@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina6_1" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.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"/>
+        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="132" id="KGk-i7-Jjw" customClass="IMChatMessageViewCell" customModule="O2Platform" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="320" height="132"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" ambiguous="YES" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
+                <rect key="frame" x="0.0" y="0.0" width="320" height="132"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Mov-YP-dlA">
+                        <rect key="frame" x="14" y="31" width="48" height="48"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="48" id="gOa-23-wjh"/>
+                            <constraint firstAttribute="width" constant="48" id="hMb-yE-x70"/>
+                        </constraints>
+                        <userDefinedRuntimeAttributes>
+                            <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                <real key="value" value="24"/>
+                            </userDefinedRuntimeAttribute>
+                        </userDefinedRuntimeAttributes>
+                    </imageView>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="06-09 13:02" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fy3-Pu-FHB">
+                        <rect key="frame" x="14" y="5" width="292" height="18"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="18" id="c57-b5-RQY"/>
+                        </constraints>
+                        <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                        <color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="姓名" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2r2-Sl-HFm">
+                        <rect key="frame" x="72" y="31" width="29" height="24"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="24" id="4T7-qw-JEB"/>
+                        </constraints>
+                        <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                        <nil key="textColor"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jxc-0S-MrS">
+                        <rect key="frame" x="72" y="63" width="176" height="42"/>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="176" id="0aZ-iT-9wn"/>
+                            <constraint firstAttribute="height" constant="42" id="ah6-qa-1tp"/>
+                        </constraints>
+                    </view>
+                </subviews>
+                <color key="backgroundColor" red="0.95294117649999999" green="0.95294117649999999" blue="0.95294117649999999" alpha="1" colorSpace="calibratedRGB"/>
+                <constraints>
+                    <constraint firstItem="Mov-YP-dlA" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="14" id="0dz-GG-NKV"/>
+                    <constraint firstItem="fy3-Pu-FHB" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="14" id="33C-sl-5B1"/>
+                    <constraint firstItem="jxc-0S-MrS" firstAttribute="leading" secondItem="Mov-YP-dlA" secondAttribute="trailing" constant="10" id="3AB-04-3Ot"/>
+                    <constraint firstAttribute="trailing" secondItem="fy3-Pu-FHB" secondAttribute="trailing" constant="14" id="AQO-Yx-K4D"/>
+                    <constraint firstItem="Mov-YP-dlA" firstAttribute="top" secondItem="fy3-Pu-FHB" secondAttribute="bottom" constant="8" id="Faw-qG-7Fy"/>
+                    <constraint firstItem="2r2-Sl-HFm" firstAttribute="top" secondItem="fy3-Pu-FHB" secondAttribute="bottom" constant="8" id="GtC-yx-sjT"/>
+                    <constraint firstItem="2r2-Sl-HFm" firstAttribute="leading" secondItem="Mov-YP-dlA" secondAttribute="trailing" constant="10" id="JYE-0i-s5S"/>
+                    <constraint firstAttribute="bottomMargin" secondItem="jxc-0S-MrS" secondAttribute="bottom" constant="10" id="Tpo-I1-lKY"/>
+                    <constraint firstItem="fy3-Pu-FHB" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="5" id="VVF-dB-vnY"/>
+                    <constraint firstItem="jxc-0S-MrS" firstAttribute="top" secondItem="2r2-Sl-HFm" secondAttribute="bottom" constant="8" id="gYS-LW-7T4"/>
+                </constraints>
+            </tableViewCellContentView>
+            <viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
+            <connections>
+                <outlet property="avatarImage" destination="Mov-YP-dlA" id="6a1-n4-fXG"/>
+                <outlet property="messageBackgroundHeight" destination="ah6-qa-1tp" id="hJ4-Xt-3Op"/>
+                <outlet property="messageBackgroundView" destination="jxc-0S-MrS" id="qTW-TG-fpN"/>
+                <outlet property="messageBackgroundWidth" destination="0aZ-iT-9wn" id="5UI-71-ADV"/>
+                <outlet property="timeLabel" destination="fy3-Pu-FHB" id="gvR-ts-fTh"/>
+                <outlet property="titleLabel" destination="2r2-Sl-HFm" id="kIP-0d-kYX"/>
+            </connections>
+            <point key="canvasLocation" x="131.8840579710145" y="94.419642857142847"/>
+        </tableViewCell>
+    </objects>
+</document>

+ 98 - 0
o2ios/O2Platform/App/IM-聊天/View/IMConversationItemCell.swift

@@ -0,0 +1,98 @@
+//
+//  IMConversationItemCell.swift
+//  O2Platform
+//
+//  Created by FancyLou on 2020/6/4.
+//  Copyright © 2020 zoneland. All rights reserved.
+//
+
+import UIKit
+import O2OA_Auth_SDK
+import CocoaLumberjack
+
+class IMConversationItemCell: UITableViewCell {
+
+    @IBOutlet weak var avatarImg: UIImageView!
+    @IBOutlet weak var nameLabel: UILabel!
+    @IBOutlet weak var timeLabel: UILabel!
+    @IBOutlet weak var messageLabel: UILabel!
+    
+    @IBOutlet weak var emojiImg: UIImageView!
+    
+    
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        // Initialization code
+    }
+
+    override func setSelected(_ selected: Bool, animated: Bool) {
+        super.setSelected(selected, animated: animated)
+
+        // Configure the view for the selected state
+    }
+    
+    func bindConversation(conversation: IMConversationInfo) {
+        //avatar name
+        if conversation.type == o2_im_conversation_type_single {
+            var person = ""
+            conversation.personList?.forEach({ (p) in
+                if  p != O2AuthSDK.shared.myInfo()?.distinguishedName {
+                    person = p
+                }
+            })
+            if person != "" {
+                //头像
+                let urlstr = AppDelegate.o2Collect.generateURLWithAppContextKey(ContactContext.contactsContextKeyV2, query: ContactContext.personIconByNameQueryV2, parameter: ["##name##":person as AnyObject], generateTime: false)
+                DDLogDebug("头像url \(String(describing: urlstr))")
+                if let u = URL(string: urlstr!) {
+                    self.avatarImg.hnk_setImageFromURL(u)
+                }else {
+                    DDLogError("错误, 没有生成头像url")
+                    self.avatarImg.image = UIImage(named: "icon_men")
+                }
+                //姓名
+                self.nameLabel.text = person.split("@").first ?? ""
+            }else {
+                self.avatarImg.image = UIImage(named: "icon_men")
+                self.nameLabel.text = ""
+            }
+        }else {//todo 群组头像 ?
+            self.nameLabel.text = conversation.title
+        }
+        //time
+        if let time = conversation.lastMessage?.createTime {
+            DDLogDebug("time: \(time)")
+            let date = time.toDate(formatter: "yyyy-MM-dd HH:mm:ss")
+            DDLogDebug("date \(date.description)")
+            self.timeLabel.text = date.friendlyTime()
+        }
+        // message
+        if let msgBody = conversation.lastMessage?.body, let body = parseJson(msg: msgBody) {
+            if body.type == o2_im_msg_type_text {
+                self.messageLabel.text = body.body
+                self.messageLabel.isHidden = false
+                self.emojiImg.isHidden = true
+            }else if body.type == o2_im_msg_type_emoji {
+                self.messageLabel.isHidden = true
+                self.emojiImg.isHidden = false
+//                self.emojiImg.image = UIImage(named: "setting_myCRM")
+                //todo emoji表情导入
+                let bundle = Bundle().o2EmojiBundle(anyClass: IMConversationItemCell.self)
+                let path = o2ImEmojiPath(emojiBody: body.body!)
+                DDLogDebug("path: \(path)")
+                self.emojiImg.image = UIImage(named: path, in: bundle, compatibleWith: nil)
+            }else {
+                self.messageLabel.isHidden = true
+                self.emojiImg.isHidden = true
+            }
+        }
+        
+    }
+    
+    private func parseJson(msg: String) -> IMMessageBodyInfo? {
+        return IMMessageBodyInfo.deserialize(from: msg)
+    }
+    
+    
+    
+}

+ 93 - 0
o2ios/O2Platform/App/IM-聊天/View/IMConversationItemCell.xib

@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina6_1" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.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"/>
+        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="64" id="KGk-i7-Jjw" customClass="IMConversationItemCell" customModule="O2Platform" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="320" height="64"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
+                <rect key="frame" x="0.0" y="0.0" width="320" height="64"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="i51-Sc-dds">
+                        <rect key="frame" x="10" y="10" width="44" height="44"/>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="44" id="0Cn-m9-oMp"/>
+                            <constraint firstAttribute="height" constant="44" id="Eds-PW-EC8"/>
+                        </constraints>
+                        <userDefinedRuntimeAttributes>
+                            <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                <real key="value" value="22"/>
+                            </userDefinedRuntimeAttribute>
+                        </userDefinedRuntimeAttributes>
+                    </imageView>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="姓名" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bOp-mm-qCN">
+                        <rect key="frame" x="66" y="10" width="154" height="21"/>
+                        <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                        <nil key="textColor"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="几分钟前" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="URQ-HD-MDc">
+                        <rect key="frame" x="257" y="10" width="58" height="17"/>
+                        <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                        <color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="这里是消息" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="I3j-lM-VtV" customClass="IMCon">
+                        <rect key="frame" x="70" y="37" width="71.5" height="17"/>
+                        <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                        <color key="textColor" systemColor="systemGrayColor" red="0.5568627451" green="0.5568627451" blue="0.57647058819999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                    <imageView hidden="YES" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="pgV-nP-wwV">
+                        <rect key="frame" x="70" y="34" width="22" height="22"/>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="22" id="GMk-WU-WAB"/>
+                            <constraint firstAttribute="height" constant="22" id="wqz-nd-Maf"/>
+                        </constraints>
+                    </imageView>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ckc-YE-j5q">
+                        <rect key="frame" x="66" y="63" width="254" height="1"/>
+                        <color key="backgroundColor" systemColor="opaqueSeparatorColor" red="0.77647058820000003" green="0.77647058820000003" blue="0.7843137255" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="1" id="szG-5G-RRm"/>
+                        </constraints>
+                    </view>
+                </subviews>
+                <constraints>
+                    <constraint firstAttribute="bottom" secondItem="I3j-lM-VtV" secondAttribute="bottom" constant="10" id="0mD-L5-TRo"/>
+                    <constraint firstAttribute="trailing" secondItem="ckc-YE-j5q" secondAttribute="trailing" id="2LG-5s-Yui"/>
+                    <constraint firstItem="pgV-nP-wwV" firstAttribute="leading" secondItem="i51-Sc-dds" secondAttribute="trailing" constant="16" id="4Ao-s1-kAM"/>
+                    <constraint firstItem="I3j-lM-VtV" firstAttribute="leading" secondItem="i51-Sc-dds" secondAttribute="trailing" constant="16" id="BFD-a2-hUs"/>
+                    <constraint firstItem="URQ-HD-MDc" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="10" id="IES-nF-jpE"/>
+                    <constraint firstAttribute="trailing" secondItem="URQ-HD-MDc" secondAttribute="trailing" constant="5" id="Jeh-u4-Tnd"/>
+                    <constraint firstItem="bOp-mm-qCN" firstAttribute="leading" secondItem="i51-Sc-dds" secondAttribute="trailing" constant="12" id="RCn-dh-ggD"/>
+                    <constraint firstItem="i51-Sc-dds" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="10" id="Z9Q-tH-mWi"/>
+                    <constraint firstAttribute="bottom" secondItem="i51-Sc-dds" secondAttribute="bottom" constant="10" id="Zew-6c-hWd"/>
+                    <constraint firstItem="bOp-mm-qCN" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="10" id="iUq-ik-5sX"/>
+                    <constraint firstAttribute="bottom" secondItem="ckc-YE-j5q" secondAttribute="bottom" id="nX2-Nw-uiy"/>
+                    <constraint firstItem="ckc-YE-j5q" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="66" id="t0M-PG-f03"/>
+                    <constraint firstAttribute="bottom" secondItem="pgV-nP-wwV" secondAttribute="bottom" constant="8" id="tXP-Lu-4aH"/>
+                    <constraint firstItem="i51-Sc-dds" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="10" id="xep-37-rIl"/>
+                </constraints>
+            </tableViewCellContentView>
+            <viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
+            <connections>
+                <outlet property="avatarImg" destination="i51-Sc-dds" id="k5V-7P-msX"/>
+                <outlet property="emojiImg" destination="pgV-nP-wwV" id="RRM-lj-KXN"/>
+                <outlet property="messageLabel" destination="I3j-lM-VtV" id="6z4-xX-xTv"/>
+                <outlet property="nameLabel" destination="bOp-mm-qCN" id="iqM-q2-qcC"/>
+                <outlet property="timeLabel" destination="URQ-HD-MDc" id="NbV-D2-ATA"/>
+            </connections>
+            <point key="canvasLocation" x="-65.217391304347828" y="-31.473214285714285"/>
+        </tableViewCell>
+    </objects>
+</document>

+ 65 - 37
o2ios/O2Platform/App/NewAttance-考勤打卡/c/OOAttanceCheckInController.swift

@@ -19,7 +19,8 @@ class OOAttanceCheckInController: UITableViewController {
     
     var checkinForm:OOAttandanceMobileCheckinForm = OOAttandanceMobileCheckinForm()
     
-    var myButton:UIButton!
+    var myButton:UIButton?
+    var feature : OOAttandanceFeature?
     
     private lazy var headerView:OOAttanceHeaderView = {
        let view = Bundle.main.loadNibNamed("OOAttanceHeaderView", owner: self, options: nil)?.first as! OOAttanceHeaderView
@@ -38,7 +39,7 @@ class OOAttanceCheckInController: UITableViewController {
         headerView.startBMKMapViewService()
         NotificationCenter.default.addObserver(self, selector: #selector(locationReceive(_:)), name: OONotification.location.notificationName, object: nil)
         if myButton != nil {
-            myButton.isHidden = false
+            myButton?.isHidden = false
         }
     }
     
@@ -47,7 +48,7 @@ class OOAttanceCheckInController: UITableViewController {
         headerView.stopBMKMapViewService()
         NotificationCenter.default.removeObserver(self)
         if myButton != nil {
-            myButton.isHidden = true
+            myButton?.isHidden = true
         }
     }
     
@@ -70,30 +71,37 @@ class OOAttanceCheckInController: UITableViewController {
         tableView.register(UINib.init(nibName: "OOAttanceItemCell", bundle: nil), forCellReuseIdentifier: "OOAttanceItemCell")
         
         getCurrentCheckinList()
-        self.perform(#selector(createButton), with: nil, afterDelay: 0)
+        getMyRecords()
+//        self.perform(#selector(createButton), with: nil, afterDelay: 0)
     }
     
     
-    @objc func closeWindow() {
-        self.tabBarController?.navigationController?.dismiss(animated: true, completion: nil)
-    }
+//    @objc func closeWindow() {
+//        self.tabBarController?.navigationController?.dismiss(animated: true, completion: nil)
+//    }
     
-    @objc private func createButton() {
+    //创建打卡按钮
+    @objc private func createButton(feature: OOAttandanceFeature) {
         let window = UIApplication.shared.windows[0]
         myButton = UIButton(type: .custom)
-        myButton.frame = CGRect(x: kScreenW - 90, y: kScreenH - 150, width: 70, height: 70)
-        myButton.setTitle("打卡", for: .normal)
-        myButton.setTitle("打卡", for: .disabled)
-        myButton.titleLabel?.font = UIFont(name: "PingFangSC-Medium", size: 18.0)!
-        myButton.theme_backgroundColor = ThemeColorPicker(keyPath: "Base.base_color")
-        myButton.setBackgroundColor(UIColor.gray, forState: .disabled)
-        myButton.isEnabled = false
-        myButton.layer.cornerRadius = 35
-        myButton.layer.masksToBounds = true
-        myButton.addTarget(self, action: #selector(postCheckinButton(_:)), for: .touchUpInside)
-        window.addSubview(myButton)
-        let pan = UIPanGestureRecognizer(target: self, action: #selector(changePostion(_:)))
-        myButton.addGestureRecognizer(pan)
+        myButton?.frame = CGRect(x: kScreenW - 90, y: kScreenH - 150, width: 70, height: 70)
+        myButton?.setTitle("打卡", for: .normal)
+        myButton?.setTitle("打卡", for: .disabled)
+        myButton?.titleLabel?.font = UIFont(name: "PingFangSC-Medium", size: 14.0)!
+        myButton?.theme_backgroundColor = ThemeColorPicker(keyPath: "Base.base_color")
+        myButton?.setBackgroundColor(UIColor.gray, forState: .disabled)
+        myButton?.isEnabled = false
+        myButton?.layer.cornerRadius = 35
+        myButton?.layer.masksToBounds = true
+        myButton?.addTarget(self, action: #selector(postCheckinButton(_:)), for: .touchUpInside)
+        window.addSubview(myButton!)
+    }
+    //删除打卡按钮
+    private func removeButton() {
+        if myButton != nil {
+            myButton?.removeFromSuperview()
+            myButton = nil
+        }
     }
     
     @objc private func locationReceive(_ notification:Notification){
@@ -102,27 +110,27 @@ class OOAttanceCheckInController: UITableViewController {
             checkinForm.desc = result.sematicDescription
             checkinForm.longitude = String(result.location.longitude)
             checkinForm.latitude = String(result.location.latitude)
-            
             checkinForm.empNo = O2AuthSDK.shared.myInfo()?.employee
             checkinForm.empName = O2AuthSDK.shared.myInfo()?.name
             let currenDate = Date()
             checkinForm.recordDateString = currenDate.toString("yyyy-MM-dd")
             checkinForm.signTime = currenDate.toString("HH:mm:ss")
-            
-            checkinForm.optMachineType = UIDevice.deviceModel()
+            checkinForm.optMachineType = UIDevice.deviceModelReadable()
             checkinForm.optSystemName = "\(UIDevice.systemName()) \(UIDevice.systemVersion())"
-            // button enable
-            myButton.isEnabled = true
+            // 打卡按钮启用
+            myButton?.isEnabled = true
             headerView.addSubview(promptView)
             DDLogDebug("checkForm set completed")
         }else{
-            myButton.isEnabled = false
-            promptView.removeSubviews()
+            //打卡按钮禁用
+            myButton?.isEnabled = false
+            promptView.removeFromSuperview()
         }
     }
     
     @objc private func postCheckinButton(_ sender:UIButton){
         MBProgressHUD_JChat.showMessage(message: "打卡中...", toView: self.view)
+        checkinForm.checkin_type = self.feature?.checkinType ?? ""
         viewModel.postMyCheckin(checkinForm) { (result) in
             MBProgressHUD_JChat.hide(forView: self.view, animated: true)
             switch result {
@@ -130,6 +138,7 @@ class OOAttanceCheckInController: UITableViewController {
                 DispatchQueue.main.async {
                     MBProgressHUD_JChat.show(text:"打卡成功", view: self.view)
                     self.getCurrentCheckinList()
+                    self.getMyRecords()
                 }
                 break
             case .fail(let errorMessage):
@@ -143,9 +152,9 @@ class OOAttanceCheckInController: UITableViewController {
         }
     }
     
-    @objc private func changePostion(_ pan:UIPanGestureRecognizer){
-        
-    }
+//    @objc private func changePostion(_ pan:UIPanGestureRecognizer){
+//
+//    }
     
     func getWorkPlace() {
         viewModel.getLocationWorkPlace { (myResult) in
@@ -165,6 +174,27 @@ class OOAttanceCheckInController: UITableViewController {
         }
     }
     
+    func getMyRecords() {
+        viewModel.listMyRecords { (result) in
+            switch result {
+            case .ok(let record):
+                let model = record as? OOMyAttandanceRecords
+                if let feature = model?.feature, feature.signSeq ?? -1 > 0 {
+                    self.feature = feature
+                    self.createButton(feature: feature)
+                }else {
+                    self.removeButton()
+                }
+                break
+            case .fail(let err):
+                DDLogError(err)
+                break
+            default:
+                break
+            }
+        }
+    }
+    
     func getCurrentCheckinList() {
         var model = CommonPageModel()
         model.pageSize = 200
@@ -194,9 +224,7 @@ class OOAttanceCheckInController: UITableViewController {
         }
     }
     
-    func postCheckIn() {
-        
-    }
+     
 
     override func didReceiveMemoryWarning() {
         super.didReceiveMemoryWarning()
@@ -252,7 +280,7 @@ class OOAttanceCheckInController: UITableViewController {
     }
     
     
-//    deinit {
-//         headerView.stopBMKMapViewService()
-//    }
+    deinit {
+         DDLogDebug("deinit 这里是checkin controller 。。。。。。。。。")
+    }
 }

+ 70 - 0
o2ios/O2Platform/App/NewAttance-考勤打卡/m/OOAttandanceModels.swift

@@ -46,6 +46,7 @@ class OOAttandanceMobileDetail:NSObject,DataModel {
     @objc var optMachineType:String? // 操作设备类别:手机品牌|PAD|PC|其他
     @objc var optSystemName:String?  // 操作设备类别:Mac|Windows|IOS|Android|其他
     var recordStatus:Int?  //记录状态:0-未分析 1-已分析
+    @objc var checkin_type: String? // 打卡类型 上午上班打卡 下午下班打卡
     
     required override init() {
         
@@ -57,6 +58,73 @@ class OOAttandanceMobileDetail:NSObject,DataModel {
 
 }
 
+// MARK: - 打卡班次对象
+class OOAttandanceScheduleSetting: NSObject, DataModel {
+    /**
+     "id": "7c89ddfe-7e69-40ce-9908-d699081aa660",
+                "topUnitName": "浙江兰德纵横网络技术股份有限公司@1@U",
+                "unitName": "移动开发组@320494093@U",
+                "unitOu": "移动开发组@320494093@U",
+                "onDutyTime": "09:00",
+                "offDutyTime": "17:00",
+                "signProxy": 0,
+                "lateStartTime": "9:05",
+                "createTime": "2020-05-27 09:19:16",
+                "updateTime": "2020-05-27 09:19:16"
+     */
+    @objc var id: String?
+    @objc var topUnitName: String?
+    @objc var unitName: String?
+    @objc var unitOu: String?
+    @objc var onDutyTime: String?
+    @objc var offDutyTime: String?
+    var signProxy: Int?
+    @objc var lateStartTime: String?
+    @objc var createTime: String?
+    @objc var updateTime: String?
+    
+    required override init() {
+        
+    }
+    
+}
+
+// MARK: - 当前用户当天打卡功能
+class OOAttandanceFeature: NSObject, DataModel {
+    /**
+     "signSeq": 1,
+     "signDate": "2020-06-02",
+     "signTime": "09:00",
+     "checkinType": "上午上班打卡"
+     */
+    @objc var signDate: String?
+    @objc var signTime: String?
+    @objc var checkinType: String?
+    var signSeq: Int? //第几次打卡 -1就不能打卡了
+    
+    required override init() {
+        
+    }
+    
+}
+
+// MARK: - istMyRecords 登录者当天的所有移动打卡信息记录 排版情况等
+class OOMyAttandanceRecords: NSObject, DataModel {
+    /**
+     {
+         "records": [],
+         "scheduleSetting": {},
+         "feature": {}
+     },
+     */
+    @objc var records:[OOAttandanceMobileDetail]?
+    @objc var scheduleSetting:OOAttandanceScheduleSetting?
+    @objc var feature: OOAttandanceFeature?
+    
+    required override init() {
+        
+    }
+}
 
 // MARK:- 提交打卡数据FormBean
 class OOAttandanceMobileCheckinForm:NSObject,DataModel {
@@ -83,6 +151,8 @@ class OOAttandanceMobileCheckinForm:NSObject,DataModel {
     
     @objc var optSystemName:String? //操作设备类别:Mac|Windows|IOS|Android|其他, 可以为空
     
+    @objc var checkin_type: String? //上午上班打卡 下午下班打卡 。。。。 对应OOAttandanceFeature里面的checkinType
+    
     required override init() {
         
     }

+ 13 - 1
o2ios/O2Platform/App/NewAttance-考勤打卡/v/OOAttanceItemCell.swift

@@ -35,7 +35,19 @@ class OOAttanceItemCell: UITableViewCell,Configurable {
         guard let model = item as? OOAttandanceMobileDetail else {
             return
         }
-        checkTimeLabel.text = "打卡时间:\(model.recordDateString ?? "") \(model.signTime ?? "")"
+        var showTime = ""
+        if let time = model.signTime {
+            if time.length > 5 {
+                showTime = time.subString(from: 0, to: 5)
+            }else {
+                showTime = time
+            }
+        }
+        if let type = model.checkin_type {
+            checkTimeLabel.text = "\(type): \(showTime)"
+        }else {
+            checkTimeLabel.text = "打开时间: \(showTime)"
+        }
         checkLocationLabel.text = "\(model.recordAddress ?? "")"
     }
     

+ 6 - 9
o2ios/O2Platform/App/NewAttance-考勤打卡/v/OOAttanceItemCell.xib

@@ -1,12 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
-    <device id="retina4_7" orientation="portrait">
-        <adaptation id="fullscreen"/>
-    </device>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
-        <capability name="Constraints to layout margins" minToolsVersion="6.0"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
         <capability name="Safe area layout guides" minToolsVersion="9.0"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
@@ -17,7 +14,7 @@
             <rect key="frame" x="0.0" y="0.0" width="320" height="68"/>
             <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
             <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
-                <rect key="frame" x="0.0" y="0.0" width="320" height="67.5"/>
+                <rect key="frame" x="0.0" y="0.0" width="320" height="68"/>
                 <autoresizingMask key="autoresizingMask"/>
                 <subviews>
                     <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="打卡时间:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="RNb-B2-SgD">
@@ -58,7 +55,7 @@
                         </constraints>
                     </imageView>
                     <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="地点" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5EJ-nc-2M4">
-                        <rect key="frame" x="57" y="32" width="247" height="21"/>
+                        <rect key="frame" x="57" y="32" width="248" height="21"/>
                         <constraints>
                             <constraint firstAttribute="height" constant="21" id="CV2-av-WsC"/>
                         </constraints>
@@ -66,7 +63,7 @@
                         <color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
                         <nil key="highlightedColor"/>
                     </label>
-                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="pic_normal" translatesAutoresizingMaskIntoConstraints="NO" id="ZwF-rR-gyd">
+                    <imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="pic_normal" translatesAutoresizingMaskIntoConstraints="NO" id="ZwF-rR-gyd">
                         <rect key="frame" x="254" y="10" width="50" height="18"/>
                         <constraints>
                             <constraint firstAttribute="height" constant="18" id="1J7-7C-1jg"/>

+ 15 - 0
o2ios/O2Platform/App/NewAttance-考勤打卡/vm/OOAttandanceViewModel.swift

@@ -39,6 +39,21 @@ final class OOAttandanceViewModel: NSObject {
 }
 
 extension OOAttandanceViewModel{
+    
+    // MARK: -
+    func listMyRecords(_ completedBlock:@escaping CallbackBlockDefine) {
+        ooAttanceAPI.request(.listMyRecord) { response in
+            let myResult = OOResult<BaseModelClass<OOMyAttandanceRecords>>(response)
+            if myResult.isResultSuccess() {
+                let records = myResult.model?.data
+                completedBlock(.ok(records))
+            }else {
+                let errorMessage = myResult.error?.errorDescription ?? ""
+                completedBlock(.fail(errorMessage))
+            }
+        }
+    }
+    
     // MARK:- 读取配置的打卡位置
     func getLocationWorkPlace(_ completedBlock:@escaping CallbackBlockDefine) {
         ooAttanceAPI.request(.myWorkplace) { (responseResult) in

+ 157 - 46
o2ios/O2Platform/App/O2MainController.swift

@@ -9,11 +9,12 @@
 import UIKit
 import CocoaLumberjack
 import O2OA_Auth_SDK
+import Starscream
 
 class O2MainController: UITabBarController, UITabBarControllerDelegate {
-    
-    static var tabBarVC:O2MainController!
-    
+
+    static var tabBarVC: O2MainController!
+
     static func genernateVC() -> O2MainController {
 //        guard let vc = tabBarVC else {
 //            tabBarVC = O2MainController()
@@ -22,21 +23,21 @@ class O2MainController: UITabBarController, UITabBarControllerDelegate {
 //        return vc
         return O2MainController()
     }
-    
-    private var currentIndex:Int = 0
+
+    private var currentIndex: Int = 0
     // demo服务器弹出公告
     private var demoAlertView = O2DemoAlertView()
-    private let viewModel:OOLoginViewModel = {
+    private let viewModel: OOLoginViewModel = {
         return OOLoginViewModel()
     }()
-    
-    
+
+
     override func viewDidLoad() {
         super.viewDidLoad()
         if UIDevice.deviceModelReadable() != "Simulator" {
             self.checkAppVersion()
         }
-        
+
         self.delegate = self
         _initControllers()
         selectedIndex = 2
@@ -46,12 +47,19 @@ class O2MainController: UITabBarController, UITabBarControllerDelegate {
             //处理内部直连的时候推送的设备绑定
             O2JPushManager.shared.o2JPushBind()
         }
+        //连接websocket
+        self._startWebsocket()
     }
-    
+
+    deinit {
+        //关闭websocket
+        self._stopWebsocket()
+    }
+
     override func viewDidAppear(_ animated: Bool) {
         // 判断是否 第一次安装 是否是连接的demo服务器
         if let unit = O2AuthSDK.shared.bindUnit() {
-            if "demo.o2oa.net" == unit.centerHost || "demo.o2oa.io" == unit.centerHost || "demo.o2server.io" == unit.centerHost {
+            if "demo.o2oa.net" == unit.centerHost || "demo.o2oa.io" == unit.centerHost || "demo.o2server.io" == unit.centerHost || "sample.o2oa.net" == unit.centerHost {
                 let tag = AppConfigSettings.shared.demoAlertTag
                 if !tag {
                     demoAlertView.showFallDown()
@@ -60,7 +68,7 @@ class O2MainController: UITabBarController, UITabBarControllerDelegate {
             }
         }
     }
-    
+
     //MARK: -- delegate
     func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
 //        if currentIndex == 2 && tabBarController.selectedIndex == 2 {
@@ -78,20 +86,21 @@ class O2MainController: UITabBarController, UITabBarControllerDelegate {
 //        }
         self.currentIndex = tabBarController.selectedIndex
     }
-    
+
     private func _initControllers() {
         //消息
         let conversationVC = JCConversationListViewController()
-        conversationVC.title  = "消息"
+//        let conversationVC = IMConversationListViewController()
+        conversationVC.title = "消息"
         let messages = ZLNavigationController(rootViewController: conversationVC)
-        
-        messages.tabBarItem = UITabBarItem(title: "消息", image:UIImage(named: "icon_news_nor"), selectedImage: O2ThemeManager.image(for: "Icon.icon_news_pre"))
-        
+
+        messages.tabBarItem = UITabBarItem(title: "消息", image: UIImage(named: "icon_news_nor"), selectedImage: O2ThemeManager.image(for: "Icon.icon_news_pre"))
+
         //通讯录
         let addressVC = OOTabBarHelper.getVC(storyboardName: "contacts", vcName: nil)
         let address = ZLNavigationController(rootViewController: addressVC)
-        address.tabBarItem = UITabBarItem(title: "通讯录", image:UIImage(named: "icon_address_g"), selectedImage: O2ThemeManager.image(for: "Icon.icon_address_list_pro"))
-        
+        address.tabBarItem = UITabBarItem(title: "通讯录", image: UIImage(named: "icon_address_g"), selectedImage: O2ThemeManager.image(for: "Icon.icon_address_list_pro"))
+
         // main
         let mainVC = mainController()
         mainVC.tabBarItem = UITabBarItem(title: nil, image: UIImage(named: "icon_zhuye_nor"), selectedImage: O2ThemeManager.image(for: "Icon.icon_zhuye_pre"))
@@ -106,17 +115,17 @@ class O2MainController: UITabBarController, UITabBarControllerDelegate {
         //应用
         let appsVC = OOTabBarHelper.getVC(storyboardName: "apps", vcName: nil)
         let apps = ZLNavigationController(rootViewController: appsVC)
-        apps.tabBarItem = UITabBarItem(title: "应用", image:UIImage(named: "icon_yingyong"), selectedImage: O2ThemeManager.image(for: "Icon.icon_yingyong_pro"))
-        
+        apps.tabBarItem = UITabBarItem(title: "应用", image: UIImage(named: "icon_yingyong"), selectedImage: O2ThemeManager.image(for: "Icon.icon_yingyong_pro"))
+
         //设置
         let settingsVC = OOTabBarHelper.getVC(storyboardName: "setting", vcName: nil)
-        let settings =   ZLNavigationController(rootViewController: settingsVC)
-        settings.tabBarItem = UITabBarItem(title: "设置", image:UIImage(named: "setting_normal"), selectedImage: O2ThemeManager.image(for: "Icon.setting_selected"))
-        
+        let settings = ZLNavigationController(rootViewController: settingsVC)
+        settings.tabBarItem = UITabBarItem(title: "设置", image: UIImage(named: "setting_normal"), selectedImage: O2ThemeManager.image(for: "Icon.setting_selected"))
+
         self.viewControllers = [messages, address, mainVC, apps, settings]
-        
+
     }
-    
+
     private func mainController() -> UIViewController {
         let appid = O2AuthSDK.shared.customStyle()?.indexPortal
         let indexType = O2AuthSDK.shared.customStyle()?.indexType ?? "default"
@@ -128,39 +137,39 @@ class O2MainController: UITabBarController, UITabBarControllerDelegate {
                 mail.isIndexShow = true
                 let nav = ZLNavigationController(rootViewController: mail)
                 return nav
-            }else {
+            } else {
                 let nav = ZLNavigationController(rootViewController: destVC)
                 return nav
             }
-        }else{
+        } else {
             let destVC = OOTabBarHelper.getVC(storyboardName: "task", vcName: nil)
             let nav = ZLNavigationController(rootViewController: destVC)
             return nav
         }
     }
-    
+
     private func _loginIM() {
         viewModel.registerIM().then { (result) in
             self.viewModel.loginIM().then({ (result) in
                 Log.debug(message: "IM登陆完成")
             })
-            }.catch { (imError) in
-                let error = imError as! OOLoginError
-                switch error {
-                case .imRegisterFail(let myErr):
-                    Log.debug(message: myErr.errorDescription!)
-                    self.viewModel.loginIM().then({ (result) in
-                        Log.debug(message: "IM登陆完成")
-                    }).catch({ (loginError) in
-                        Log.error(message: "im Login Error \(loginError)")
-                    })
-                    break
-                default:
-                    break
-                }
+        }.catch { (imError) in
+            let error = imError as! OOLoginError
+            switch error {
+            case .imRegisterFail(let myErr):
+                Log.debug(message: myErr.errorDescription!)
+                self.viewModel.loginIM().then({ (result) in
+                    Log.debug(message: "IM登陆完成")
+                }).catch({ (loginError) in
+                    Log.error(message: "im Login Error \(loginError)")
+                })
+                break
+            default:
+                break
+            }
         }
     }
-    
+
     private func checkAppVersion() {
         O2VersionManager.shared.checkAppUpdate { (info, error) in
             if let iosInfo = info {
@@ -175,10 +184,112 @@ class O2MainController: UITabBarController, UITabBarControllerDelegate {
                 alertController.addAction(cancelAction)
                 alertController.addAction(okAction)
                 UIApplication.shared.keyWindow?.rootViewController?.present(alertController, animated: true, completion: nil)
-            }else {
+            } else {
                 DDLogInfo("没有版本更新:\(error ?? "")")
             }
         }
     }
-    
+
+
+
+    // MARK: - websocket
+    private var timer: Timer?
+    private var isWsOpen = false
+
+    private func _startWebsocket() {
+        DDLogDebug("启动websocket连接。。。。。。")
+        let url = AppDelegate.o2Collect.generateWebsocketURL()
+        DDLogDebug("这个是wsurl :\(url)")
+        O2WebsocketManager.instance.startConnect(wsUrl: url, delegate: self)
+    }
+
+    private func _stopWebsocket() {
+        DDLogDebug("关闭websocket连接。。。。。。")
+        self.stopTiming()
+        O2WebsocketManager.instance.closeConnect()
+    }
+
+    private func startTiming() {
+        DDLogDebug("开启定时器 。。。。。。")
+        if timer != nil {
+            timer?.invalidate()
+            timer = nil
+        }
+        timer = Timer.scheduledTimer(timeInterval: 30, target: self, selector: #selector(sendHeartbeatMsg), userInfo: nil, repeats: true)
+        timer?.fire()
+    }
+    private func stopTiming() {
+        DDLogDebug("关闭定时器 。。。。。。")
+        if timer != nil {
+            timer?.invalidate()
+            timer = nil
+        }
+    }
+    //发送心跳
+    @objc private func sendHeartbeatMsg() {
+        if isWsOpen {
+            O2WebsocketManager.instance.send(msg: o2_im_ws_heartbeat)
+        } else { //重新启动
+            _startWebsocket()
+        }
+    }
+
+}
+
+extension O2MainController: WebSocketDelegate {
+
+
+    func didReceive(event: WebSocketEvent, client: WebSocket) {
+        switch event {
+        case .text(let text):
+            if text != o2_im_ws_heartbeat { //忽略心跳消息
+                DDLogDebug("接收的ws消息:\(text)")
+                //判断type im消息就发送通知
+                do {
+                    if let dicArr = try JSONSerialization.jsonObject(with: String(text).data(using: .utf8)!, options: .allowFragments) as? [String: AnyObject] {
+                        if let type = dicArr["type"] as? String, type == "im_create" {
+                            if let messageInfo = WsMessage.deserialize(from: text) {
+                                DDLogDebug("接收到im消息 发送通知。。")
+                                NotificationCenter.post(customeNotification: OONotification.websocket, object: messageInfo.body)
+                            }
+                        }
+                    }
+                } catch {  }
+            }
+            break
+        case .connected(let headers):
+            DDLogDebug("websocket is connected: \(headers)")
+            isWsOpen = true
+            self.startTiming()
+            break
+        case .disconnected(let reason, let code):
+            DDLogDebug("websocket is disconnected: \(reason) with code: \(code)")
+            isWsOpen = false
+            break
+        case .binary(let data):
+            DDLogDebug("Received binary data: \(data.count)")
+            break
+        case .ping(_):
+            break
+        case .pong(_):
+            break
+        case .viablityChanged(_):
+            DDLogDebug("websocket viablityChanged")
+            break
+        case .reconnectSuggested(_):
+            DDLogDebug("websocket reconnectSuggested")
+            break
+        case .cancelled:
+            DDLogDebug("websocket is canceled")
+            isWsOpen = false
+            break
+        case .error(let error):
+            DDLogError("websocket is error, \(String(describing: error?.localizedDescription))")
+            isWsOpen = false
+            break
+        }
+    }
+
+
 }
+

+ 29 - 31
o2ios/O2Platform/App/contacts/contacts.storyboard

@@ -1,11 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="iDJ-yD-7GA">
-    <device id="retina4_7" orientation="portrait">
-        <adaptation id="fullscreen"/>
-    </device>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="iDJ-yD-7GA">
+    <device id="retina4_7" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
     <scenes>
@@ -22,7 +20,7 @@
                                 <rect key="frame" x="0.0" y="55.5" width="375" height="50"/>
                                 <autoresizingMask key="autoresizingMask"/>
                                 <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="EyC-x7-vmj" id="9oF-MQ-3Ei">
-                                    <rect key="frame" x="0.0" y="0.0" width="341" height="49.5"/>
+                                    <rect key="frame" x="0.0" y="0.0" width="348" height="50"/>
                                     <autoresizingMask key="autoresizingMask"/>
                                     <subviews>
                                         <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ptf-Ml-OFP">
@@ -56,7 +54,7 @@
                                 <rect key="frame" x="0.0" y="105.5" width="375" height="50"/>
                                 <autoresizingMask key="autoresizingMask"/>
                                 <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="b8p-Kf-TPW" id="9SB-Vc-9hf">
-                                    <rect key="frame" x="0.0" y="0.0" width="341" height="49.5"/>
+                                    <rect key="frame" x="0.0" y="0.0" width="348" height="50"/>
                                     <autoresizingMask key="autoresizingMask"/>
                                     <subviews>
                                         <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cZm-m3-L0j">
@@ -110,7 +108,7 @@
             <objects>
                 <tableViewController storyboardIdentifier="ContactPersonInfoV2" title="个人详细信息" useStoryboardIdentifierAsRestorationIdentifier="YES" id="3YZ-RH-20k" customClass="ContactPersonInfoV2ViewController" customModule="O2Platform" customModuleProvider="target" sceneMemberID="viewController">
                     <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="vqn-wS-u18">
-                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="647"/>
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                         <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
                         <view key="tableHeaderView" contentMode="scaleToFill" id="iVq-2P-h4E">
@@ -228,7 +226,7 @@
                             </constraints>
                         </view>
                         <view key="tableFooterView" contentMode="scaleToFill" id="Btm-sH-ehp">
-                            <rect key="frame" x="0.0" y="402" width="375" height="44"/>
+                            <rect key="frame" x="0.0" y="430" width="375" height="44"/>
                             <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                             <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
                         </view>
@@ -237,7 +235,7 @@
                                 <rect key="frame" x="0.0" y="358" width="375" height="44"/>
                                 <autoresizingMask key="autoresizingMask"/>
                                 <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="uPu-jr-Eup" id="7cS-du-cCp">
-                                    <rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
+                                    <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
                                     <autoresizingMask key="autoresizingMask"/>
                                     <subviews>
                                         <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="名称" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ay2-QP-fxz">
@@ -298,7 +296,7 @@
             <objects>
                 <tableViewController storyboardIdentifier="companyDept" title="公司部门" useStoryboardIdentifierAsRestorationIdentifier="YES" id="AmI-yp-X5H" customClass="ContactCompanyDeptController" customModule="O2Platform" customModuleProvider="target" sceneMemberID="viewController">
                     <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="50" sectionHeaderHeight="18" sectionFooterHeight="18" id="RsD-Jy-sNh">
-                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="647"/>
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                         <color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
                         <prototypes>
@@ -306,11 +304,11 @@
                                 <rect key="frame" x="0.0" y="55.5" width="375" height="50"/>
                                 <autoresizingMask key="autoresizingMask"/>
                                 <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="7bY-dM-eEp" id="mz8-vc-d4J">
-                                    <rect key="frame" x="0.0" y="0.0" width="341" height="49.5"/>
+                                    <rect key="frame" x="0.0" y="0.0" width="348" height="50"/>
                                     <autoresizingMask key="autoresizingMask"/>
                                     <subviews>
                                         <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Prg-Tx-oEu">
-                                            <rect key="frame" x="16" y="12" width="30" height="25.5"/>
+                                            <rect key="frame" x="16" y="12" width="30" height="26"/>
                                             <constraints>
                                                 <constraint firstAttribute="width" constant="30" id="spI-VN-QSv"/>
                                             </constraints>
@@ -357,7 +355,7 @@
             <objects>
                 <tableViewController id="P8m-JR-nYf" customClass="ContactSearchViewController" customModule="O2Platform" customModuleProvider="target" sceneMemberID="viewController">
                     <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="50" sectionHeaderHeight="18" sectionFooterHeight="18" id="U3c-0i-9CP">
-                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="647"/>
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                         <color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
                         <prototypes>
@@ -365,7 +363,7 @@
                                 <rect key="frame" x="0.0" y="55.5" width="375" height="50"/>
                                 <autoresizingMask key="autoresizingMask"/>
                                 <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="j4U-nO-WUz" id="xGE-eP-qH3">
-                                    <rect key="frame" x="0.0" y="0.0" width="341" height="49.5"/>
+                                    <rect key="frame" x="0.0" y="0.0" width="348" height="50"/>
                                     <autoresizingMask key="autoresizingMask"/>
                                     <subviews>
                                         <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="spM-02-Z2O">
@@ -415,7 +413,7 @@
             <objects>
                 <tableViewController title="我的群组" id="LsC-tt-HCK" customClass="ContactGroupPersonController" customModule="O2Platform" customModuleProvider="target" sceneMemberID="viewController">
                     <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="QdT-78-ZZu">
-                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="647"/>
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                         <color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
                         <prototypes>
@@ -423,11 +421,11 @@
                                 <rect key="frame" x="0.0" y="55.5" width="375" height="44"/>
                                 <autoresizingMask key="autoresizingMask"/>
                                 <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="VH3-gE-Egt" id="r7U-bZ-8Ro">
-                                    <rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
+                                    <rect key="frame" x="0.0" y="0.0" width="348" height="44"/>
                                     <autoresizingMask key="autoresizingMask"/>
                                     <subviews>
                                         <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="2Yy-Gs-zHR">
-                                            <rect key="frame" x="16" y="10" width="30" height="23.5"/>
+                                            <rect key="frame" x="16" y="10" width="30" height="24"/>
                                             <constraints>
                                                 <constraint firstAttribute="width" constant="30" id="TbG-w1-qGK"/>
                                             </constraints>
@@ -477,7 +475,7 @@
                         <viewControllerLayoutGuide type="bottom" id="MLL-Af-ftg"/>
                     </layoutGuides>
                     <view key="view" contentMode="scaleToFill" id="miS-ZM-vE9">
-                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="647"/>
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                         <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                     </view>
@@ -491,7 +489,7 @@
             <objects>
                 <tableViewController storyboardIdentifier="DeptPerson" title="部门个人" useStoryboardIdentifierAsRestorationIdentifier="YES" id="rtm-Ih-lnV" customClass="ContactDeptPersonController" customModule="O2Platform" customModuleProvider="target" sceneMemberID="viewController">
                     <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="50" sectionHeaderHeight="18" sectionFooterHeight="18" id="JaW-sK-m2q">
-                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="647"/>
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                         <color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
                         <view key="tableFooterView" contentMode="scaleToFill" id="885-HT-Hf5">
@@ -504,11 +502,11 @@
                                 <rect key="frame" x="0.0" y="55.5" width="375" height="50"/>
                                 <autoresizingMask key="autoresizingMask"/>
                                 <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="eVD-9I-9Fp" id="KbZ-H9-Myg">
-                                    <rect key="frame" x="0.0" y="0.0" width="375" height="49.5"/>
+                                    <rect key="frame" x="0.0" y="0.0" width="375" height="50"/>
                                     <autoresizingMask key="autoresizingMask"/>
                                     <subviews>
                                         <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="UbD-xq-ZqX">
-                                            <rect key="frame" x="10" y="0.0" width="355" height="49.5"/>
+                                            <rect key="frame" x="10" y="0.0" width="355" height="50"/>
                                         </scrollView>
                                     </subviews>
                                     <constraints>
@@ -526,7 +524,7 @@
                                 <rect key="frame" x="0.0" y="105.5" width="375" height="50"/>
                                 <autoresizingMask key="autoresizingMask"/>
                                 <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Tw8-69-DnB" id="gPk-m5-2mE">
-                                    <rect key="frame" x="0.0" y="0.0" width="341" height="49.5"/>
+                                    <rect key="frame" x="0.0" y="0.0" width="348" height="50"/>
                                     <autoresizingMask key="autoresizingMask"/>
                                     <subviews>
                                         <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Nq8-LU-vKm">
@@ -560,7 +558,7 @@
                                 <rect key="frame" x="0.0" y="155.5" width="375" height="50"/>
                                 <autoresizingMask key="autoresizingMask"/>
                                 <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="vd8-tW-njc" id="Zue-nP-fAf">
-                                    <rect key="frame" x="0.0" y="0.0" width="341" height="49.5"/>
+                                    <rect key="frame" x="0.0" y="0.0" width="348" height="50"/>
                                     <autoresizingMask key="autoresizingMask"/>
                                     <subviews>
                                         <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Zkg-Zp-guU">
@@ -611,7 +609,7 @@
                 <navigationController automaticallyAdjustsScrollViewInsets="NO" id="cvC-Xd-NbA" customClass="ZLNavigationController" customModule="O2Platform" customModuleProvider="target" sceneMemberID="viewController">
                     <toolbarItems/>
                     <navigationBar key="navigationBar" contentMode="scaleToFill" id="Ih6-yy-4wj">
-                        <rect key="frame" x="0.0" y="20" width="375" height="44"/>
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="56"/>
                         <autoresizingMask key="autoresizingMask"/>
                     </navigationBar>
                     <nil name="viewControllers"/>
@@ -624,6 +622,12 @@
             <point key="canvasLocation" x="-767" y="1340"/>
         </scene>
     </scenes>
+    <inferredMetricsTieBreakers>
+        <segue reference="ou3-1r-djP"/>
+        <segue reference="JCQ-fv-wZ7"/>
+        <segue reference="5hh-6u-Olo"/>
+        <segue reference="EGH-4l-bNL"/>
+    </inferredMetricsTieBreakers>
     <resources>
         <image name="Shapes" width="25" height="25"/>
         <image name="icon_boy_1" width="50" height="50"/>
@@ -633,10 +637,4 @@
         <image name="personDefaultIcon" width="24" height="24"/>
         <image name="pic_beijing1" width="375" height="230"/>
     </resources>
-    <inferredMetricsTieBreakers>
-        <segue reference="Sy2-Dk-jgV"/>
-        <segue reference="aMD-xA-Tv2"/>
-        <segue reference="maa-Lm-CVS"/>
-        <segue reference="EGH-4l-bNL"/>
-    </inferredMetricsTieBreakers>
 </document>

+ 22 - 0
o2ios/O2Platform/Assets.xcassets/all/chat_emoji.imageset/Contents.json

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

BIN
o2ios/O2Platform/Assets.xcassets/all/chat_emoji.imageset/chat_emoji@2x.png


BIN
o2ios/O2Platform/Assets.xcassets/all/chat_emoji.imageset/chat_emoji@3x.png


+ 22 - 0
o2ios/O2Platform/Assets.xcassets/首页/chat_bubble_incomming.imageset/Contents.json

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

BIN
o2ios/O2Platform/Assets.xcassets/首页/chat_bubble_incomming.imageset/chat_bubble_incomming@2x.png


BIN
o2ios/O2Platform/Assets.xcassets/首页/chat_bubble_incomming.imageset/chat_bubble_incomming@3x.png


+ 22 - 0
o2ios/O2Platform/Assets.xcassets/首页/chat_bubble_outgoing.imageset/Contents.json

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

BIN
o2ios/O2Platform/Assets.xcassets/首页/chat_bubble_outgoing.imageset/chat_bubble_outgoing@2x.png


BIN
o2ios/O2Platform/Assets.xcassets/首页/chat_bubble_outgoing.imageset/chat_bubble_outgoing@3x.png


+ 17 - 8
o2ios/O2Platform/Extension/Bundle+Extension.swift

@@ -15,33 +15,42 @@ import Foundation
 //private  let _bundle:UnsafePointer<Void> =  unsafeBitCast(0,to: UnsafePointer<Void>.self)
 
 class BundleEx: Bundle {
-    
+
     override func localizedString(forKey key: String, value: String?, table tableName: String?) -> String {
         if let bundle = languageBundle() {
             return bundle.localizedString(forKey: key, value: value, table: tableName)
-        }else{
+        } else {
             return super.localizedString(forKey: key, value: value, table: tableName)
         }
     }
-    
-    
+
+
 }
 
-extension Bundle{
+extension Bundle {
 //    private struct Static {
 //        static let onceToken: Static = { Static() }()
 //    }
 //
-    func onLanguage(){
+    func onLanguage() {
         //替换NSBundle.mainBundle()为自定义的BundleEx
         DispatchQueue.once(token: "language") {
             object_setClass(Bundle.main, BundleEx.self)
         }
     }
-    
+
     //当前语言的bundle
-    func languageBundle() -> Bundle?{
+    func languageBundle() -> Bundle? {
         return Languager.standardLanguager().currentLanguageBundle
     }
+
+    //o2 表情包读取
+    func o2EmojiBundle(anyClass: AnyClass) -> Bundle {
+        var bundle: Bundle = Bundle.main
+        if let resource = Bundle(for: anyClass.self).path(forResource: "O2Emoji", ofType: "bundle") {
+            bundle = Bundle(path: resource) ?? Bundle.main
+        }
+        return bundle
+    }
 }
 

+ 16 - 4
o2ios/O2Platform/Extension/Date+Extension.swift

@@ -211,6 +211,13 @@ extension Date {
         return self.year == date.year
     }
     
+    /**间隔天数
+     */
+    func betweenDays(_ date: Date) -> Int {
+        let components = Calendar.current.dateComponents([.day], from: self, to: date)
+        return abs(components.day ?? 0)
+    }
+    
     func haveSameYearAndMonth(_ date: Date) -> Bool {
         return self.haveSameYear(date) && self.month == date.month
     }
@@ -285,11 +292,16 @@ extension Date {
                 returnTimeString = "\(gap)天前"
             }
         }else if now.haveSameYear(self) {
-            let gap = now.month - self.month
-            if gap < 4 {
-                returnTimeString = "\(gap)个月前"
+            let days = now.betweenDays(self)
+            if days > 30 {
+                let gap = now.month - self.month
+                if gap < 4 {
+                    returnTimeString = "\(gap)个月前"
+                }else {
+                    returnTimeString = self.formatterDate(formatter: "yyyy-MM-dd")
+                }
             }else {
-                returnTimeString = self.formatterDate(formatter: "yyyy-MM-dd")
+               returnTimeString = "\(days)天前"
             }
         }else {
             returnTimeString = self.formatterDate(formatter: "yyyy-MM-dd")

+ 2 - 0
o2ios/O2Platform/Extension/Notification+Extension.swift

@@ -22,6 +22,8 @@ enum OONotification:String {
     //重载门户webview
     case reloadPortal
     
+    case websocket
+    
     
     
     var stringValue:String {

+ 7 - 0
o2ios/O2Platform/Extension/UIDeviceExtensions.swift

@@ -29,6 +29,13 @@ private let DeviceList = [
     /* iPhone 8 */        "iPhone10,1": "iPhone 8", "iPhone10,4": "iPhone 8",
     /* iPhone 8 Plus */   "iPhone10,2": "iPhone 8 Plus", "iPhone10,5": "iPhone 8 Plus",
     /* iPhone X */        "iPhone10,3": "iPhone X", "iPhone10,6": "iPhone X",
+    /* iPhone XS */       "iPhone11,2": "iPhone XS",
+    /* iPhone XS Max */   "iPhone11,4": "iPhone XS Max", "iPhone11,6": "iPhone XS Max",
+    /* iPhone XR */       "iPhone11,8": "iPhone XR",
+    /* iPhone 11 */       "iPhone12,1": "iPhone 11",
+    /* iPhone 11 Pro*/    "iPhone12,3": "iPhone 11 Pro",
+    /* iPhone 11 Pro Max*/"iPhone12,5": "iPhone 11 Pro Max",
+    /* iPhone SE 2*/      "iPhone12,8": "iPhone SE 2",
 
     /* iPad 2 */          "iPad2,1": "iPad 2", "iPad2,2": "iPad 2", "iPad2,3": "iPad 2", "iPad2,4": "iPad 2",
     /* iPad 3 */          "iPad3,1": "iPad 3", "iPad3,2": "iPad 3", "iPad3,3": "iPad 3",

+ 33 - 0
o2ios/O2Platform/Extension/UIView+Extension.swift

@@ -171,6 +171,39 @@ extension UIView {
         self.layer.cornerRadius = radius
         self.layer.masksToBounds = true
     }
+    
+    //画线
+    private func drawBorder(rect:CGRect,color:UIColor){
+        let line = UIBezierPath(rect: rect)
+        let lineShape = CAShapeLayer()
+        lineShape.path = line.cgPath
+        lineShape.fillColor = color.cgColor
+        self.layer.addSublayer(lineShape)
+    }
+    
+    //设置右边框
+    public func rightBorder(width:CGFloat,borderColor:UIColor){
+        let rect = CGRect(x: 0, y: self.frame.size.width - width, width: width, height: self.frame.size.height)
+        drawBorder(rect: rect, color: borderColor)
+    }
+    //设置左边框
+    public func leftBorder(width:CGFloat,borderColor:UIColor){
+        let rect = CGRect(x: 0, y: 0, width: width, height: self.frame.size.height)
+        drawBorder(rect: rect, color: borderColor)
+    }
+    //设置上边框
+    public func topBorder(width:CGFloat,borderColor:UIColor){
+        let rect = CGRect(x: 0, y: 0, width: self.frame.size.width, height: width)
+        drawBorder(rect: rect, color: borderColor)
+    }
+    //设置底边框
+    public func buttomBorder(width:CGFloat,borderColor:UIColor){
+        let rect = CGRect(x: 0, y: self.frame.size.height-width, width: self.frame.size.width, height: width)
+        drawBorder(rect: rect, color: borderColor)
+    }
+
+
+
 
 }
 

+ 7 - 0
o2ios/O2Platform/Framework/O2API/AttendanceAPI/OOAttendanceAPI.swift

@@ -22,6 +22,7 @@ enum OOAttendanceAPI {
     case checkinCycle(String,String) //考勤周期
     case checkinTotalForMonth(OOAttandanceTotalBean) //考勤统计
     case checkinAnalyze(OOAttandanceTotalBean) //考勤分析
+    case listMyRecord //当前用户当前的打卡情况和班次
 }
 
 // MARK:- 上下文实现
@@ -66,6 +67,8 @@ extension OOAttendanceAPI:TargetType {
             return "/jaxrs/attendancedetail/filter/list"
         case .checkinAnalyze(let bean):
             return "/jaxrs/statisticshow/person/\(bean.q_empName!)/\(bean.q_year!)/\(bean.q_month!)"
+        case .listMyRecord:
+            return "/jaxrs/attendancedetail/mobile/my"
         
         }
     }
@@ -90,6 +93,8 @@ extension OOAttendanceAPI:TargetType {
             return .put
         case .checkinAnalyze(_):
             return .get
+        case .listMyRecord:
+            return .get
         }
     }
     
@@ -117,6 +122,8 @@ extension OOAttendanceAPI:TargetType {
             return .requestParameters(parameters: bean.toJSON() ?? [:], encoding: JSONEncoding.default)
         case .checkinAnalyze(_):
             return .requestPlain
+        case .listMyRecord:
+            return .requestPlain
         }
     }
     

+ 79 - 0
o2ios/O2Platform/Framework/O2API/Communicate/CommunicateAPI.swift

@@ -0,0 +1,79 @@
+//
+//  CommunicateAPI.swift
+//  O2Platform
+//
+//  Created by FancyLou on 2020/6/4.
+//  Copyright © 2020 zoneland. All rights reserved.
+//
+
+import Moya
+import O2OA_Auth_SDK
+
+// MARK: - 消息模块
+
+enum CommunicateAPI {
+    case myConversationList
+    case msgListByPaging(Int, Int, String)
+    
+    
+}
+
+extension CommunicateAPI: OOAPIContextCapable {
+    var apiContextKey: String {
+           return "x_message_assemble_communicate"
+       }
+}
+
+// 是否需要xtoken
+extension CommunicateAPI: OOAccessTokenAuthorizable {
+    var shouldAuthorize: Bool {
+        return true
+    }
+}
+
+extension CommunicateAPI: TargetType {
+    var baseURL: URL {
+        let model  = O2AuthSDK.shared.centerServerInfo()?.assembles?["x_message_assemble_communicate"]
+        let baseURLString = "\(model?.httpProtocol ?? "http")://\(model?.host ?? ""):\(model?.port ?? 0)\(model?.context ?? "")"
+        return URL(string: baseURLString)!
+    }
+    
+    
+    var path: String {
+        switch self {
+        case .myConversationList:
+            return "/jaxrs/im/conversation/list/my"
+        case .msgListByPaging(let page, let size, _):
+            return "/jaxrs/im/msg/list/\(page)/size/\(size)"
+        }
+    }
+    
+    var method: Moya.Method {
+        switch self {
+        case .myConversationList:
+            return .get
+        case .msgListByPaging(_, _, _):
+            return .post
+        }
+    }
+    
+    var sampleData: Data {
+        return "".data(using: String.Encoding.utf8)!
+    }
+    
+    var task: Task {
+        switch self {
+        case .myConversationList:
+            return .requestPlain
+        case .msgListByPaging(_, _, let conversationId):
+            let form = IMMessageRequestForm()
+            form.conversationId = conversationId
+            return .requestParameters(parameters: form.toJSON()!, encoding: JSONEncoding.default)
+        }
+    }
+    
+    var headers: [String : String]? {
+        return nil
+    }
+   
+}

+ 56 - 0
o2ios/O2Platform/Manager/O2WebsocketManager.swift

@@ -0,0 +1,56 @@
+//
+//  O2WebsocketManager.swift
+//  O2Platform
+//
+//  Created by FancyLou on 2020/6/1.
+//  Copyright © 2020 zoneland. All rights reserved.
+//
+
+import Foundation
+import Starscream
+import CocoaLumberjack
+
+
+class O2WebsocketManager {
+
+    static let instance: O2WebsocketManager = {
+        return O2WebsocketManager()
+    }()
+
+    private init() { }
+
+    private var socket: WebSocket?
+
+
+    // wsUrl: ws://xxx.o2oa.net:20020/x_message_assemble_communicate/ws/collaboration?x-token=xxxxxxx
+    //开启连接
+    func startConnect(wsUrl: String, delegate: WebSocketDelegate) {
+        let request = URLRequest(url: URL(string: wsUrl)!)
+        socket = WebSocket(request: request)
+        socket?.delegate = delegate
+        socket?.connect()
+    }
+
+    //发送消息
+    func send(msg: String) {
+        guard let s = socket else {
+            DDLogError("socket 为空 还未启动 无法发送消息")
+            return
+        }
+        s.write(string: msg)
+    }
+
+    //关闭连接
+    func closeConnect() {
+        guard let s = socket else {
+            DDLogError("socket 为空 无需关闭")
+            return
+        }
+        s.disconnect(closeCode: CloseCode.goingAway.rawValue)
+        socket = nil
+    }
+
+}
+
+
+

+ 0 - 1
o2ios/O2Platform/O2Platform-Bridging-Header.h

@@ -44,4 +44,3 @@
 // flutter
 #import "GeneratedPluginRegistrant.h"
 
-

BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/Info.plist


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_01.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_02.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_03.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_04.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_05.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_06.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_07.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_08.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_09.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_10.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_11.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_12.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_13.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_14.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_15.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_16.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_17.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_18.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_19.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_20.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_21.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_22.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_23.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_24.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_25.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_26.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_27.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_28.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_29.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_30.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_31.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_32.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_33.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_34.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_35.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_36.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_37.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_38.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_39.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_40.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_41.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_42.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_43.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_44.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_45.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_46.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_47.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_48.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_49.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_50.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_51.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_52.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_53.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_54.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_55.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_56.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_57.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_58.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_59.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_60.png


BIN
o2ios/O2Platform/Resources/O2Emoji.bundle/im_emotion_61.png


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