├── .gitignore ├── LICENSE ├── README.md ├── TestIMDemo.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata └── TestIMDemo ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets ├── AppIcon.appiconset │ └── Contents.json ├── Contents.json ├── chatListCell │ ├── Contents.json │ └── chatListCellHead.imageset │ │ ├── Contents.json │ │ ├── chatListCellHead.png │ │ └── chatListCellHead@2x.png ├── chatUI │ ├── Contents.json │ ├── VoiceSearchFeedback001.imageset │ │ ├── Contents.json │ │ └── VoiceSearchFeedback001@2x.png │ ├── VoiceSearchFeedback002.imageset │ │ ├── Contents.json │ │ └── VoiceSearchFeedback002@2x.png │ ├── VoiceSearchFeedback003.imageset │ │ ├── Contents.json │ │ └── VoiceSearchFeedback003@2x.png │ ├── VoiceSearchFeedback004.imageset │ │ ├── Contents.json │ │ └── VoiceSearchFeedback004@2x.png │ ├── VoiceSearchFeedback005.imageset │ │ ├── Contents.json │ │ └── VoiceSearchFeedback005@2x.png │ ├── VoiceSearchFeedback006.imageset │ │ ├── Contents.json │ │ └── VoiceSearchFeedback006@2x.png │ ├── VoiceSearchFeedback007.imageset │ │ ├── Contents.json │ │ └── VoiceSearchFeedback007@2x.png │ ├── VoiceSearchFeedback008.imageset │ │ ├── Contents.json │ │ └── VoiceSearchFeedback008@2x.png │ ├── VoiceSearchFeedback009.imageset │ │ ├── Contents.json │ │ └── VoiceSearchFeedback009@2x.png │ ├── VoiceSearchFeedback010.imageset │ │ ├── Contents.json │ │ └── VoiceSearchFeedback010@2x.png │ ├── VoiceSearchFeedback011.imageset │ │ ├── Contents.json │ │ └── VoiceSearchFeedback011@2x.png │ ├── VoiceSearchFeedback012.imageset │ │ ├── Contents.json │ │ └── VoiceSearchFeedback012@2x.png │ ├── VoiceSearchFeedback013.imageset │ │ ├── Contents.json │ │ └── VoiceSearchFeedback013@2x.png │ ├── VoiceSearchFeedback014.imageset │ │ ├── Contents.json │ │ └── VoiceSearchFeedback014@2x.png │ ├── VoiceSearchFeedback015.imageset │ │ ├── Contents.json │ │ └── VoiceSearchFeedback015@2x.png │ ├── VoiceSearchFeedback016.imageset │ │ ├── Contents.json │ │ └── VoiceSearchFeedback016@2x.png │ ├── VoiceSearchFeedback017.imageset │ │ ├── Contents.json │ │ └── VoiceSearchFeedback017@2x.png │ ├── VoiceSearchFeedback018.imageset │ │ ├── Contents.json │ │ └── VoiceSearchFeedback018@2x.png │ ├── VoiceSearchFeedback019.imageset │ │ ├── Contents.json │ │ └── VoiceSearchFeedback019@2x.png │ ├── VoiceSearchFeedback020.imageset │ │ ├── Contents.json │ │ └── VoiceSearchFeedback020@2x.png │ ├── arrow.imageset │ │ ├── Contents.json │ │ └── arrow@2x.png │ ├── back.imageset │ │ ├── Contents.json │ │ └── back@2x.png │ ├── chatBar_colorMore_audioCall.imageset │ │ ├── Contents.json │ │ └── chatBar_colorMore_audioCall@2x.png │ ├── chatBar_colorMore_audioCallSelected.imageset │ │ ├── Contents.json │ │ └── chatBar_colorMore_audioCallSelected@2x.png │ ├── chatBar_colorMore_camera.imageset │ │ ├── Contents.json │ │ └── chatBar_colorMore_camera@2x.png │ ├── chatBar_colorMore_cameraSelected.imageset │ │ ├── Contents.json │ │ └── chatBar_colorMore_cameraSelected@2x.png │ ├── chatBar_colorMore_location.imageset │ │ ├── Contents.json │ │ └── chatBar_colorMore_location@2x.png │ ├── chatBar_colorMore_locationSelected.imageset │ │ ├── Contents.json │ │ └── chatBar_colorMore_locationSelected@2x.png │ ├── chatBar_colorMore_photo.imageset │ │ ├── Contents.json │ │ └── chatBar_colorMore_photo@2x.png │ ├── chatBar_colorMore_photoSelected.imageset │ │ ├── Contents.json │ │ └── chatBar_colorMore_photoSelected@2x.png │ ├── chatBar_colorMore_video.imageset │ │ ├── Contents.json │ │ └── chatBar_colorMore_video@2x.png │ ├── chatBar_colorMore_videoCall.imageset │ │ ├── Contents.json │ │ └── chatBar_colorMore_videoCall@2x.png │ ├── chatBar_colorMore_videoCallSelected.imageset │ │ ├── Contents.json │ │ └── chatBar_colorMore_videoCallSelected@2x.png │ ├── chatBar_colorMore_videoSelected.imageset │ │ ├── Contents.json │ │ └── chatBar_colorMore_videoSelected@2x.png │ ├── chatBar_face.imageset │ │ ├── Contents.json │ │ └── chatBar_face@2x.png │ ├── chatBar_faceSelected.imageset │ │ ├── Contents.json │ │ └── chatBar_faceSelected@2x.png │ ├── chatBar_keyboard.imageset │ │ ├── Contents.json │ │ └── chatBar_keyboard@2x.png │ ├── chatBar_keyboardSelected.imageset │ │ ├── Contents.json │ │ └── chatBar_keyboardSelected@2x.png │ ├── chatBar_more.imageset │ │ ├── Contents.json │ │ └── chatBar_more@2x.png │ ├── chatBar_moreSelected.imageset │ │ ├── Contents.json │ │ └── chatBar_moreSelected@2x.png │ ├── chatBar_record.imageset │ │ ├── Contents.json │ │ └── chatBar_record@2x.png │ ├── chatBar_recordBg.imageset │ │ ├── Contents.json │ │ └── chatBar_recordBg@2x.png │ ├── chatBar_recordSelected.imageset │ │ ├── Contents.json │ │ └── chatBar_recordSelected@2x.png │ ├── chatBar_recordSelectedBg.imageset │ │ ├── Contents.json │ │ └── chatBar_recordSelectedBg@2x.png │ ├── chat_item_file.imageset │ │ ├── Contents.json │ │ └── chat_item_file@2x.png │ ├── chat_location_preview.imageset │ │ ├── Contents.json │ │ └── chat_location_preview@2x.png │ ├── chat_receiver_audio_playing000.imageset │ │ ├── Contents.json │ │ └── chat_receiver_audio_playing000@2x.png │ ├── chat_receiver_audio_playing001.imageset │ │ ├── Contents.json │ │ └── chat_receiver_audio_playing001@2x.png │ ├── chat_receiver_audio_playing002.imageset │ │ ├── Contents.json │ │ └── chat_receiver_audio_playing002@2x.png │ ├── chat_receiver_audio_playing003.imageset │ │ ├── Contents.json │ │ └── chat_receiver_audio_playing003@2x.png │ ├── chat_receiver_audio_playing_full.imageset │ │ ├── Contents.json │ │ └── chat_receiver_audio_playing_full@2x.png │ ├── chat_receiver_bg.imageset │ │ ├── Contents.json │ │ └── chat_receiver_bg@2x.png │ ├── chat_sender_audio_playing_000.imageset │ │ ├── Contents.json │ │ └── chat_sender_audio_playing_000@2x.png │ ├── chat_sender_audio_playing_001.imageset │ │ ├── Contents.json │ │ └── chat_sender_audio_playing_001@2x.png │ ├── chat_sender_audio_playing_002.imageset │ │ ├── Contents.json │ │ └── chat_sender_audio_playing_002@2x.png │ ├── chat_sender_audio_playing_003.imageset │ │ ├── Contents.json │ │ └── chat_sender_audio_playing_003@2x.png │ ├── chat_sender_audio_playing_full.imageset │ │ ├── Contents.json │ │ └── chat_sender_audio_playing_full@2x.png │ ├── chat_sender_bg.imageset │ │ ├── Contents.json │ │ └── chat_sender_bg@2x.png │ ├── faceDelete.imageset │ │ ├── Contents.json │ │ └── faceDelete@2x.png │ ├── group.imageset │ │ ├── Contents.json │ │ └── group@2x.png │ ├── imageDownloadFail.imageset │ │ ├── Contents.json │ │ └── imageDownloadFail@2x.png │ ├── messageSendFail.imageset │ │ ├── Contents.json │ │ └── messageSendFail@2x.png │ ├── messageToolbarBg.imageset │ │ ├── Contents.json │ │ └── messageToolbarBg@2x.png │ ├── messageVideo.imageset │ │ ├── Contents.json │ │ └── messageVideo@2x.png │ └── user.imageset │ │ ├── Contents.json │ │ └── user@2x.png └── tabbar │ ├── Contents.json │ ├── tabbar_chats.imageset │ ├── Contents.json │ ├── tabbar_chats.png │ └── tabbar_chats@2x.png │ ├── tabbar_chatsHL.imageset │ ├── Contents.json │ ├── tabbar_chatsHL.png │ └── tabbar_chatsHL@2x.png │ ├── tabbar_contacts.imageset │ ├── Contents.json │ ├── tabbar_contacts.png │ └── tabbar_contacts@2x.png │ ├── tabbar_contactsHL.imageset │ ├── Contents.json │ ├── tabbar_contactsHL.png │ └── tabbar_contactsHL@2x.png │ ├── tabbar_setting.imageset │ ├── Contents.json │ ├── tabbar_setting.png │ └── tabbar_setting@2x.png │ └── tabbar_settingHL.imageset │ ├── Contents.json │ ├── tabbar_settingHL.png │ └── tabbar_settingHL@2x.png ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── Classes ├── AddressBook │ └── ViewController │ │ ├── BRAddFriendViewController.h │ │ ├── BRAddFriendViewController.m │ │ ├── BRAddressBookController.h │ │ └── BRAddressBookController.m ├── Chat │ ├── Model │ │ ├── BRAttributedText.h │ │ └── BRAttributedText.m │ ├── View │ │ ├── BRChatCell.h │ │ ├── BRChatCell.m │ │ ├── BRTimeCell.h │ │ └── BRTimeCell.m │ └── ViewController │ │ ├── BRChatViewController.h │ │ └── BRChatViewController.m ├── Conversation │ └── ViewController │ │ ├── BRConversationController.h │ │ └── BRConversationController.m ├── Login │ └── ViewController │ │ ├── BRLoginViewController.h │ │ └── BRLoginViewController.m ├── Resource │ ├── default.png │ ├── en.lproj │ │ └── Localizable.strings │ ├── in.caf │ └── zh-Hans.lproj │ │ └── Localizable.strings ├── Setting │ └── ViewController │ │ ├── BRSettingController.h │ │ └── BRSettingController.m ├── Utils │ ├── Category │ │ ├── CALayer+LayerColor.h │ │ ├── CALayer+LayerColor.m │ │ ├── NSDate+BRAdd.h │ │ ├── NSDate+BRAdd.m │ │ ├── UIViewController+Alert.h │ │ └── UIViewController+Alert.m │ └── Tools │ │ ├── BRVoicePlayTool.h │ │ └── BRVoicePlayTool.m ├── Vendor │ └── DeviceHelper │ │ ├── SDWebImage │ │ ├── MKAnnotationView+WebCache.h │ │ ├── MKAnnotationView+WebCache.m │ │ ├── NSData+ImageContentType.h │ │ ├── NSData+ImageContentType.m │ │ ├── NSImage+WebCache.h │ │ ├── NSImage+WebCache.m │ │ ├── SDImageCache.h │ │ ├── SDImageCache.m │ │ ├── SDImageCacheConfig.h │ │ ├── SDImageCacheConfig.m │ │ ├── SDWebImageCompat.h │ │ ├── SDWebImageCompat.m │ │ ├── SDWebImageDecoder.h │ │ ├── SDWebImageDecoder.m │ │ ├── SDWebImageDownloader.h │ │ ├── SDWebImageDownloader.m │ │ ├── SDWebImageDownloaderOperation.h │ │ ├── SDWebImageDownloaderOperation.m │ │ ├── SDWebImageManager.h │ │ ├── SDWebImageManager.m │ │ ├── SDWebImageOperation.h │ │ ├── SDWebImagePrefetcher.h │ │ ├── SDWebImagePrefetcher.m │ │ ├── UIButton+WebCache.h │ │ ├── UIButton+WebCache.m │ │ ├── UIImage+GIF.h │ │ ├── UIImage+GIF.m │ │ ├── UIImage+MultiFormat.h │ │ ├── UIImage+MultiFormat.m │ │ ├── UIImage+WebP.h │ │ ├── UIImage+WebP.m │ │ ├── UIImageView+HighlightedWebCache.h │ │ ├── UIImageView+HighlightedWebCache.m │ │ ├── UIImageView+WebCache.h │ │ ├── UIImageView+WebCache.m │ │ ├── UIView+WebCache.h │ │ ├── UIView+WebCache.m │ │ ├── UIView+WebCacheOperation.h │ │ └── UIView+WebCacheOperation.m │ │ ├── VoiceConvert │ │ ├── EMVoiceConverter.h │ │ ├── EMVoiceConverter.mm │ │ ├── amrwapper │ │ │ ├── amrFileCodec.h │ │ │ ├── amrFileCodec.mm │ │ │ ├── wav.h │ │ │ └── wav.mm │ │ ├── opencore-amrnb │ │ │ ├── interf_dec.h │ │ │ ├── interf_enc.h │ │ │ └── libopencore-amrnb.a │ │ └── opencore-amrwb │ │ │ ├── dec_if.h │ │ │ ├── if_rom.h │ │ │ └── libopencore-amrwb.a │ │ ├── delegates │ │ ├── EMCDDeviceManagerDelegate.h │ │ └── EMCDDeviceManagerProximitySensorDelegate.h │ │ └── internal │ │ ├── DemoErrorCode.h │ │ ├── EMAudioPlayerUtil.h │ │ ├── EMAudioPlayerUtil.m │ │ ├── EMAudioRecorderUtil.h │ │ ├── EMAudioRecorderUtil.m │ │ ├── EMCDDeviceManager+Media.h │ │ ├── EMCDDeviceManager+Media.m │ │ ├── EMCDDeviceManager+Microphone.h │ │ ├── EMCDDeviceManager+Microphone.m │ │ ├── EMCDDeviceManager+ProximitySensor.h │ │ ├── EMCDDeviceManager+ProximitySensor.m │ │ ├── EMCDDeviceManager+Remind.h │ │ ├── EMCDDeviceManager+Remind.m │ │ ├── EMCDDeviceManager.h │ │ ├── EMCDDeviceManagerBase.h │ │ └── EMCDDeviceManagerBase.m └── Xib │ └── Login.storyboard ├── Info.plist ├── PrefixHeader.pch └── main.m /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | # CocoaPods 32 | # 33 | # We recommend against adding the Pods directory to your .gitignore. However 34 | # you should judge for yourself, the pros and cons are mentioned at: 35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 36 | # 37 | # Pods/ 38 | 39 | # Carthage 40 | # 41 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 42 | # Carthage/Checkouts 43 | 44 | Carthage/Build 45 | 46 | # fastlane 47 | # 48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 49 | # screenshots whenever they are needed. 50 | # For more information about the recommended setup visit: 51 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 52 | 53 | fastlane/report.xml 54 | fastlane/Preview.html 55 | fastlane/screenshots 56 | fastlane/test_output 57 | 58 | # Code Injection 59 | # 60 | # After new code Injection tools there's a generated folder /iOSInjectionProject 61 | # https://github.com/johnno1962/injectionforxcode 62 | 63 | iOSInjectionProject/ 64 | # 忽略环信SDK,内存较大,需另行下载 65 | TestIMDemo/Classes/Vendor/HyphenateFullSDK 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TestIMDemo 2 | 测试环信SDK的集成 3 | -------------------------------------------------------------------------------- /TestIMDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TestIMDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/20. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /TestIMDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/20. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // 1.初始化环信SDK 20 | EMOptions *options = [EMOptions optionsWithAppkey:EaseMobAppKey]; // 注册的AppKey 21 | //options.apnsCertName = @"push_cer"; // 推送证书名(不需要加后缀) 22 | [[EMClient sharedClient] initializeSDKWithOptions:options]; 23 | 24 | // 2.设置自动登录 25 | // 设置EMClient的代理(添加回调监听代理),监听自动登录的状态 26 | [[EMClient sharedClient] addDelegate:self delegateQueue:nil]; 27 | // 如果登录过,直接进入主界面 28 | if ([EMClient sharedClient].isAutoLogin) { 29 | // 进入主界面(拿到 Main.storyboard 箭头所指的控制器,即 tabBarController) 30 | self.window.rootViewController = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateInitialViewController]; 31 | } 32 | 33 | return YES; 34 | } 35 | 36 | #pragma mark - EMClientDelegate 自动登录的回调(这个代理方法可以知道自动登录是否成功!) 37 | - (void)autoLoginDidCompleteWithError:(EMError *)aError { 38 | if (!aError) { 39 | NSLog(@"自动登录成功!"); 40 | } else { 41 | NSLog(@"自动登录失败:%@", aError); 42 | } 43 | } 44 | 45 | 46 | - (void)applicationWillResignActive:(UIApplication *)application { 47 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 48 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 49 | } 50 | 51 | // APP进入后台 52 | - (void)applicationDidEnterBackground:(UIApplication *)application { 53 | [[EMClient sharedClient] applicationDidEnterBackground:application]; 54 | } 55 | 56 | // APP将要从后台返回 57 | - (void)applicationWillEnterForeground:(UIApplication *)application { 58 | [[EMClient sharedClient] applicationWillEnterForeground:application]; 59 | } 60 | 61 | 62 | - (void)applicationDidBecomeActive:(UIApplication *)application { 63 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 64 | } 65 | 66 | 67 | - (void)applicationWillTerminate:(UIApplication *)application { 68 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 69 | } 70 | 71 | - (void)dealloc { 72 | // 移除代理(有添加就有移除) 73 | [[EMClient sharedClient] removeDelegate:self]; 74 | } 75 | 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatListCell/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatListCell/chatListCellHead.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "chatListCellHead.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "chatListCellHead@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatListCell/chatListCellHead.imageset/chatListCellHead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatListCell/chatListCellHead.imageset/chatListCellHead.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatListCell/chatListCellHead.imageset/chatListCellHead@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatListCell/chatListCellHead.imageset/chatListCellHead@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback001.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "VoiceSearchFeedback001@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback001.imageset/VoiceSearchFeedback001@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback001.imageset/VoiceSearchFeedback001@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback002.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "VoiceSearchFeedback002@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback002.imageset/VoiceSearchFeedback002@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback002.imageset/VoiceSearchFeedback002@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback003.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "VoiceSearchFeedback003@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback003.imageset/VoiceSearchFeedback003@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback003.imageset/VoiceSearchFeedback003@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback004.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "VoiceSearchFeedback004@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback004.imageset/VoiceSearchFeedback004@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback004.imageset/VoiceSearchFeedback004@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback005.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "VoiceSearchFeedback005@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback005.imageset/VoiceSearchFeedback005@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback005.imageset/VoiceSearchFeedback005@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback006.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "VoiceSearchFeedback006@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback006.imageset/VoiceSearchFeedback006@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback006.imageset/VoiceSearchFeedback006@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback007.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "VoiceSearchFeedback007@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback007.imageset/VoiceSearchFeedback007@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback007.imageset/VoiceSearchFeedback007@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback008.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "VoiceSearchFeedback008@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback008.imageset/VoiceSearchFeedback008@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback008.imageset/VoiceSearchFeedback008@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback009.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "VoiceSearchFeedback009@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback009.imageset/VoiceSearchFeedback009@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback009.imageset/VoiceSearchFeedback009@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback010.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "VoiceSearchFeedback010@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback010.imageset/VoiceSearchFeedback010@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback010.imageset/VoiceSearchFeedback010@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback011.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "VoiceSearchFeedback011@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback011.imageset/VoiceSearchFeedback011@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback011.imageset/VoiceSearchFeedback011@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback012.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "VoiceSearchFeedback012@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback012.imageset/VoiceSearchFeedback012@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback012.imageset/VoiceSearchFeedback012@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback013.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "VoiceSearchFeedback013@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback013.imageset/VoiceSearchFeedback013@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback013.imageset/VoiceSearchFeedback013@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback014.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "VoiceSearchFeedback014@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback014.imageset/VoiceSearchFeedback014@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback014.imageset/VoiceSearchFeedback014@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback015.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "VoiceSearchFeedback015@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback015.imageset/VoiceSearchFeedback015@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback015.imageset/VoiceSearchFeedback015@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback016.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "VoiceSearchFeedback016@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback016.imageset/VoiceSearchFeedback016@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback016.imageset/VoiceSearchFeedback016@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback017.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "VoiceSearchFeedback017@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback017.imageset/VoiceSearchFeedback017@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback017.imageset/VoiceSearchFeedback017@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback018.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "VoiceSearchFeedback018@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback018.imageset/VoiceSearchFeedback018@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback018.imageset/VoiceSearchFeedback018@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback019.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "VoiceSearchFeedback019@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback019.imageset/VoiceSearchFeedback019@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback019.imageset/VoiceSearchFeedback019@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback020.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "VoiceSearchFeedback020@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback020.imageset/VoiceSearchFeedback020@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/VoiceSearchFeedback020.imageset/VoiceSearchFeedback020@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/arrow.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "arrow@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/arrow.imageset/arrow@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/arrow.imageset/arrow@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/back.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "back@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/back.imageset/back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/back.imageset/back@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_audioCall.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chatBar_colorMore_audioCall@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_audioCall.imageset/chatBar_colorMore_audioCall@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_audioCall.imageset/chatBar_colorMore_audioCall@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_audioCallSelected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chatBar_colorMore_audioCallSelected@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_audioCallSelected.imageset/chatBar_colorMore_audioCallSelected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_audioCallSelected.imageset/chatBar_colorMore_audioCallSelected@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_camera.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chatBar_colorMore_camera@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_camera.imageset/chatBar_colorMore_camera@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_camera.imageset/chatBar_colorMore_camera@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_cameraSelected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chatBar_colorMore_cameraSelected@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_cameraSelected.imageset/chatBar_colorMore_cameraSelected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_cameraSelected.imageset/chatBar_colorMore_cameraSelected@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_location.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chatBar_colorMore_location@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_location.imageset/chatBar_colorMore_location@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_location.imageset/chatBar_colorMore_location@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_locationSelected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chatBar_colorMore_locationSelected@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_locationSelected.imageset/chatBar_colorMore_locationSelected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_locationSelected.imageset/chatBar_colorMore_locationSelected@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_photo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chatBar_colorMore_photo@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_photo.imageset/chatBar_colorMore_photo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_photo.imageset/chatBar_colorMore_photo@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_photoSelected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chatBar_colorMore_photoSelected@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_photoSelected.imageset/chatBar_colorMore_photoSelected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_photoSelected.imageset/chatBar_colorMore_photoSelected@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_video.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chatBar_colorMore_video@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_video.imageset/chatBar_colorMore_video@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_video.imageset/chatBar_colorMore_video@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_videoCall.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chatBar_colorMore_videoCall@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_videoCall.imageset/chatBar_colorMore_videoCall@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_videoCall.imageset/chatBar_colorMore_videoCall@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_videoCallSelected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chatBar_colorMore_videoCallSelected@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_videoCallSelected.imageset/chatBar_colorMore_videoCallSelected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_videoCallSelected.imageset/chatBar_colorMore_videoCallSelected@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_videoSelected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chatBar_colorMore_videoSelected@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_videoSelected.imageset/chatBar_colorMore_videoSelected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chatBar_colorMore_videoSelected.imageset/chatBar_colorMore_videoSelected@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_face.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chatBar_face@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_face.imageset/chatBar_face@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chatBar_face.imageset/chatBar_face@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_faceSelected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chatBar_faceSelected@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_faceSelected.imageset/chatBar_faceSelected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chatBar_faceSelected.imageset/chatBar_faceSelected@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_keyboard.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chatBar_keyboard@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_keyboard.imageset/chatBar_keyboard@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chatBar_keyboard.imageset/chatBar_keyboard@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_keyboardSelected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chatBar_keyboardSelected@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_keyboardSelected.imageset/chatBar_keyboardSelected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chatBar_keyboardSelected.imageset/chatBar_keyboardSelected@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_more.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chatBar_more@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_more.imageset/chatBar_more@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chatBar_more.imageset/chatBar_more@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_moreSelected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chatBar_moreSelected@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_moreSelected.imageset/chatBar_moreSelected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chatBar_moreSelected.imageset/chatBar_moreSelected@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_record.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chatBar_record@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_record.imageset/chatBar_record@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chatBar_record.imageset/chatBar_record@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_recordBg.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chatBar_recordBg@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_recordBg.imageset/chatBar_recordBg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chatBar_recordBg.imageset/chatBar_recordBg@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_recordSelected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chatBar_recordSelected@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_recordSelected.imageset/chatBar_recordSelected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chatBar_recordSelected.imageset/chatBar_recordSelected@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_recordSelectedBg.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chatBar_recordSelectedBg@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chatBar_recordSelectedBg.imageset/chatBar_recordSelectedBg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chatBar_recordSelectedBg.imageset/chatBar_recordSelectedBg@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_item_file.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chat_item_file@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_item_file.imageset/chat_item_file@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chat_item_file.imageset/chat_item_file@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_location_preview.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chat_location_preview@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_location_preview.imageset/chat_location_preview@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chat_location_preview.imageset/chat_location_preview@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_receiver_audio_playing000.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chat_receiver_audio_playing000@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_receiver_audio_playing000.imageset/chat_receiver_audio_playing000@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chat_receiver_audio_playing000.imageset/chat_receiver_audio_playing000@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_receiver_audio_playing001.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chat_receiver_audio_playing001@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_receiver_audio_playing001.imageset/chat_receiver_audio_playing001@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chat_receiver_audio_playing001.imageset/chat_receiver_audio_playing001@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_receiver_audio_playing002.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chat_receiver_audio_playing002@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_receiver_audio_playing002.imageset/chat_receiver_audio_playing002@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chat_receiver_audio_playing002.imageset/chat_receiver_audio_playing002@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_receiver_audio_playing003.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chat_receiver_audio_playing003@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_receiver_audio_playing003.imageset/chat_receiver_audio_playing003@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chat_receiver_audio_playing003.imageset/chat_receiver_audio_playing003@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_receiver_audio_playing_full.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chat_receiver_audio_playing_full@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_receiver_audio_playing_full.imageset/chat_receiver_audio_playing_full@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chat_receiver_audio_playing_full.imageset/chat_receiver_audio_playing_full@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_receiver_bg.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chat_receiver_bg@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_receiver_bg.imageset/chat_receiver_bg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chat_receiver_bg.imageset/chat_receiver_bg@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_sender_audio_playing_000.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chat_sender_audio_playing_000@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_sender_audio_playing_000.imageset/chat_sender_audio_playing_000@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chat_sender_audio_playing_000.imageset/chat_sender_audio_playing_000@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_sender_audio_playing_001.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chat_sender_audio_playing_001@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_sender_audio_playing_001.imageset/chat_sender_audio_playing_001@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chat_sender_audio_playing_001.imageset/chat_sender_audio_playing_001@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_sender_audio_playing_002.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chat_sender_audio_playing_002@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_sender_audio_playing_002.imageset/chat_sender_audio_playing_002@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chat_sender_audio_playing_002.imageset/chat_sender_audio_playing_002@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_sender_audio_playing_003.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chat_sender_audio_playing_003@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_sender_audio_playing_003.imageset/chat_sender_audio_playing_003@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chat_sender_audio_playing_003.imageset/chat_sender_audio_playing_003@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_sender_audio_playing_full.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chat_sender_audio_playing_full@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_sender_audio_playing_full.imageset/chat_sender_audio_playing_full@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chat_sender_audio_playing_full.imageset/chat_sender_audio_playing_full@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_sender_bg.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chat_sender_bg@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/chat_sender_bg.imageset/chat_sender_bg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/chat_sender_bg.imageset/chat_sender_bg@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/faceDelete.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "faceDelete@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/faceDelete.imageset/faceDelete@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/faceDelete.imageset/faceDelete@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/group.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "group@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/group.imageset/group@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/group.imageset/group@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/imageDownloadFail.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "imageDownloadFail@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/imageDownloadFail.imageset/imageDownloadFail@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/imageDownloadFail.imageset/imageDownloadFail@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/messageSendFail.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "messageSendFail@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/messageSendFail.imageset/messageSendFail@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/messageSendFail.imageset/messageSendFail@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/messageToolbarBg.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "messageToolbarBg@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/messageToolbarBg.imageset/messageToolbarBg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/messageToolbarBg.imageset/messageToolbarBg@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/messageVideo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "messageVideo@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/messageVideo.imageset/messageVideo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/messageVideo.imageset/messageVideo@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/user.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "user@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/chatUI/user.imageset/user@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/chatUI/user.imageset/user@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/tabbar/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/tabbar/tabbar_chats.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "tabbar_chats.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "tabbar_chats@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/tabbar/tabbar_chats.imageset/tabbar_chats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/tabbar/tabbar_chats.imageset/tabbar_chats.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/tabbar/tabbar_chats.imageset/tabbar_chats@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/tabbar/tabbar_chats.imageset/tabbar_chats@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/tabbar/tabbar_chatsHL.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "tabbar_chatsHL.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "tabbar_chatsHL@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/tabbar/tabbar_chatsHL.imageset/tabbar_chatsHL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/tabbar/tabbar_chatsHL.imageset/tabbar_chatsHL.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/tabbar/tabbar_chatsHL.imageset/tabbar_chatsHL@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/tabbar/tabbar_chatsHL.imageset/tabbar_chatsHL@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/tabbar/tabbar_contacts.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "tabbar_contacts.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "tabbar_contacts@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/tabbar/tabbar_contacts.imageset/tabbar_contacts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/tabbar/tabbar_contacts.imageset/tabbar_contacts.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/tabbar/tabbar_contacts.imageset/tabbar_contacts@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/tabbar/tabbar_contacts.imageset/tabbar_contacts@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/tabbar/tabbar_contactsHL.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "tabbar_contactsHL.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "tabbar_contactsHL@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/tabbar/tabbar_contactsHL.imageset/tabbar_contactsHL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/tabbar/tabbar_contactsHL.imageset/tabbar_contactsHL.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/tabbar/tabbar_contactsHL.imageset/tabbar_contactsHL@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/tabbar/tabbar_contactsHL.imageset/tabbar_contactsHL@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/tabbar/tabbar_setting.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "tabbar_setting.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "tabbar_setting@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/tabbar/tabbar_setting.imageset/tabbar_setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/tabbar/tabbar_setting.imageset/tabbar_setting.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/tabbar/tabbar_setting.imageset/tabbar_setting@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/tabbar/tabbar_setting.imageset/tabbar_setting@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/tabbar/tabbar_settingHL.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "tabbar_settingHL.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "tabbar_settingHL@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/tabbar/tabbar_settingHL.imageset/tabbar_settingHL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/tabbar/tabbar_settingHL.imageset/tabbar_settingHL.png -------------------------------------------------------------------------------- /TestIMDemo/Assets.xcassets/tabbar/tabbar_settingHL.imageset/tabbar_settingHL@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Assets.xcassets/tabbar/tabbar_settingHL.imageset/tabbar_settingHL@2x.png -------------------------------------------------------------------------------- /TestIMDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/AddressBook/ViewController/BRAddFriendViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // BRAddFriendViewController.h 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/24. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BRAddFriendViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/AddressBook/ViewController/BRAddFriendViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // BRAddFriendViewController.m 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/24. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import "BRAddFriendViewController.h" 10 | 11 | @interface BRAddFriendViewController () 12 | @property (weak, nonatomic) IBOutlet UITextField *textField; 13 | 14 | @end 15 | 16 | @implementation BRAddFriendViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | 21 | } 22 | 23 | // 搜索按钮,添加朋友 24 | - (IBAction)clickAddFriendBtn:(id)sender { 25 | NSString *friendName = self.textField.text; 26 | NSString *message = [NSString stringWithFormat:@"我是%@", [EMClient sharedClient].currentUsername]; 27 | // 添加好友请求 28 | [[EMClient sharedClient].contactManager addContact:friendName message:message completion:^(NSString *aUsername, EMError *aError) { 29 | if (!aError) { 30 | NSLog(@"邀请发送成功!"); 31 | } else { 32 | NSLog(@"邀请发送失败:%@", aError); 33 | } 34 | }]; 35 | } 36 | 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/AddressBook/ViewController/BRAddressBookController.h: -------------------------------------------------------------------------------- 1 | // 2 | // BRAddressBookController.h 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/24. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BRAddressBookController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/AddressBook/ViewController/BRAddressBookController.m: -------------------------------------------------------------------------------- 1 | // 2 | // BRAddressBookController.m 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/24. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import "BRAddressBookController.h" 10 | #import "BRChatViewController.h" 11 | 12 | @interface BRAddressBookController () 13 | @property (nonatomic, strong) NSArray *contactArr; 14 | 15 | @end 16 | 17 | @implementation BRAddressBookController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | 22 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveNotification:) name:@"updateContactList" object:nil]; 23 | 24 | [self loadData]; 25 | } 26 | 27 | // 收到 updateContactList 通知之后执行的事件 28 | - (void)receiveNotification:(NSNotification *)sender { 29 | // 更新好友列表 30 | [self loadData]; 31 | } 32 | 33 | 34 | // 获取好友列表的数据 35 | - (void)loadData { 36 | // // 方法1:从本地数据库获取所有的好友(通常是没有网络的情况下用这个) 37 | // self.contactArr = [[EMClient sharedClient].contactManager getContacts]; 38 | // [self.tableView reloadData]; 39 | 40 | // 方法2:从服务器获取所有的好友 <异步方法> 41 | [[EMClient sharedClient].contactManager getContactsFromServerWithCompletion:^(NSArray *aList, EMError *aError) { 42 | if (!aError) { 43 | NSLog(@"获取好友列表成功:%@", aList); 44 | self.contactArr = aList; 45 | [self.tableView reloadData]; 46 | } else { 47 | NSLog(@"获取好友失败:%@", aError); 48 | } 49 | }]; 50 | } 51 | 52 | #pragma mark - Table view data source 53 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 54 | return 1; 55 | } 56 | 57 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 58 | return self.contactArr.count; 59 | } 60 | 61 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 62 | static NSString *cellID = @"AddressBookCell"; 63 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID]; 64 | cell.imageView.image = [UIImage imageNamed:@"chatListCellHead"]; 65 | cell.textLabel.text = self.contactArr[indexPath.row]; 66 | 67 | return cell; 68 | } 69 | 70 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 71 | 72 | } 73 | 74 | - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { 75 | if (editingStyle == UITableViewCellEditingStyleDelete) { 76 | NSLog(@"删除好友"); 77 | // 删除好友请求 78 | [[EMClient sharedClient].contactManager deleteContact:self.contactArr[indexPath.row] isDeleteConversation:YES completion:^(NSString *aUsername, EMError *aError) { 79 | if (!aError) { 80 | NSLog(@"删除好友操作成功!"); 81 | // 重新从服务器获取好友列表数据 82 | [self loadData]; 83 | } 84 | }]; 85 | } 86 | } 87 | 88 | - (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath { 89 | return @"删除"; 90 | } 91 | 92 | #pragma mark - 连线跳转传值(目的:将好友的用户名传到聊天界面) 93 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 94 | // 获取目标控制器 95 | id nextVC = segue.destinationViewController; 96 | if ([nextVC isKindOfClass:[BRChatViewController class]]) { 97 | // 获取当前点击(选中)的cell 98 | NSInteger selectedRow = [self.tableView indexPathForSelectedRow].row; 99 | BRChatViewController *chatVC = nextVC; 100 | // 给下一个页面传值 101 | chatVC.contactUsername = self.contactArr[selectedRow]; 102 | } 103 | } 104 | 105 | - (NSArray *)contactArr { 106 | if (!_contactArr) { 107 | _contactArr = [NSArray array]; 108 | } 109 | return _contactArr; 110 | } 111 | 112 | - (void)dealloc { 113 | // 移除当前对象监听的事件 114 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 115 | } 116 | 117 | @end 118 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Chat/Model/BRAttributedText.h: -------------------------------------------------------------------------------- 1 | // 2 | // BRAttributedText.h 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/31. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BRAttributedText : NSObject 12 | /** 返回语音的富文本 */ 13 | + (NSAttributedString *)voiceAttrText:(EMMessage *)message isPlaying:(BOOL)isPlaying; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Chat/Model/BRAttributedText.m: -------------------------------------------------------------------------------- 1 | // 2 | // BRAttributedText.m 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/31. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import "BRAttributedText.h" 10 | 11 | @implementation BRAttributedText 12 | #pragma mark - 返回语音的富文本 13 | + (NSAttributedString *)voiceAttrText:(EMMessage *)message isPlaying:(BOOL)isPlaying { 14 | EMVoiceMessageBody *voiceMsgBody = (EMVoiceMessageBody *)message.body; 15 | int duration = voiceMsgBody.duration; 16 | // 空格个数,控制语音消息的显示长度 17 | NSString *spacingText = @""; 18 | NSInteger length = MIN(duration, 50) / 2; 19 | for (NSInteger i = 0; i < length; i++) { 20 | spacingText = [spacingText stringByAppendingString:@" "]; 21 | } 22 | 23 | // 1.创建一个可变的富文本 24 | NSMutableAttributedString *voiceAttr = [[NSMutableAttributedString alloc]init]; 25 | if (message.direction == EMMessageDirectionReceive) { 26 | // 接收方(左边):富文本 = 图片 + 时间 27 | // 创建图片附件 28 | NSTextAttachment *imageAttachment = [[NSTextAttachment alloc]init]; 29 | if (!isPlaying) { 30 | imageAttachment.image = [UIImage imageNamed:@"chat_receiver_audio_playing_full"]; 31 | } 32 | // y 为负数,把文字往上调 33 | imageAttachment.bounds = CGRectMake(0, -6, 20, 20); 34 | // 1.1 创建图片富文本 35 | NSAttributedString *imageAttr = [NSAttributedString attributedStringWithAttachment:imageAttachment]; 36 | [voiceAttr appendAttributedString:imageAttr]; 37 | 38 | // 1.2 创建时间富文本 39 | NSString *timeStr = [NSString stringWithFormat:@"%@ %d'", spacingText, duration]; 40 | NSMutableAttributedString *timeAttr = [[NSMutableAttributedString alloc]initWithString:timeStr]; 41 | //富文本样式 42 | [timeAttr addAttribute:NSForegroundColorAttributeName value:[UIColor darkGrayColor] range:NSMakeRange(0, timeStr.length)]; 43 | [timeAttr addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:12] range:NSMakeRange(0, timeStr.length)]; 44 | 45 | [voiceAttr appendAttributedString:timeAttr]; 46 | 47 | } else { 48 | // 发送方(右边):富文本 = 时间 + 图片 49 | // 2.1 创建时间富文本 50 | NSString *timeStr = [NSString stringWithFormat:@"%d' %@", duration, spacingText]; 51 | NSMutableAttributedString *timeAttr = [[NSMutableAttributedString alloc]initWithString:timeStr]; 52 | [timeAttr addAttribute:NSForegroundColorAttributeName value:[UIColor darkGrayColor] range:NSMakeRange(0, timeStr.length)]; 53 | [timeAttr addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:12] range:NSMakeRange(0, timeStr.length)]; 54 | [voiceAttr appendAttributedString:timeAttr]; 55 | // 创建图片附件 56 | NSTextAttachment *imageAttachment = [[NSTextAttachment alloc]init]; 57 | if (!isPlaying) { 58 | imageAttachment.image = [UIImage imageNamed:@"chat_sender_audio_playing_full"]; 59 | } 60 | imageAttachment.bounds = CGRectMake(0, -6, 20, 20); 61 | // 2.2 创建图片富文本 62 | NSAttributedString *imageAttr = [NSAttributedString attributedStringWithAttachment:imageAttachment]; 63 | [voiceAttr appendAttributedString:imageAttr]; 64 | } 65 | 66 | return [voiceAttr copy]; 67 | } 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Chat/View/BRChatCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // BRChatCell.h 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/25. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BRChatCell : UITableViewCell 12 | @property (weak, nonatomic) IBOutlet UILabel *messageLabel; 13 | /** 消息模型 */ 14 | @property (nonatomic, strong) EMMessage *messageModel; 15 | 16 | /** 计算cell的高度 */ 17 | - (CGFloat)cellHeight; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Chat/View/BRChatCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // BRChatCell.m 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/25. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import "BRChatCell.h" 10 | #import "BRVoicePlayTool.h" 11 | #import "BRAttributedText.h" 12 | #import "UIImageView+WebCache.h" 13 | 14 | @interface BRChatCell () 15 | @property (nonatomic, strong) UIImageView *chatImageView; 16 | 17 | @end 18 | 19 | @implementation BRChatCell 20 | 21 | #pragma mark - 在此方法中做一些初始化操作 22 | - (void)awakeFromNib { 23 | [super awakeFromNib]; 24 | // 给label添加tap手势 25 | UITapGestureRecognizer *myTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(didTapMessageLabel:)]; 26 | [self.messageLabel addGestureRecognizer:myTap]; 27 | } 28 | 29 | #pragma mark - label的点击事件 30 | - (void)didTapMessageLabel:(UITapGestureRecognizer *)sender { 31 | EMMessageBody *voiceBody = self.messageModel.body; 32 | if (voiceBody.type == EMMessageBodyTypeVoice) { 33 | NSLog(@"播放语音"); 34 | [BRVoicePlayTool playWithMessage:self.messageModel msgLabel:self.messageLabel]; 35 | } 36 | } 37 | 38 | - (void)setMessageModel:(EMMessage *)messageModel { 39 | _messageModel = messageModel; 40 | 41 | // 重用时,把聊天图片控件移除 42 | [self.chatImageView removeFromSuperview]; 43 | 44 | // 解析普通消息 45 | EMMessageBody *msgBody = messageModel.body; 46 | switch (msgBody.type) { 47 | case EMMessageBodyTypeText: 48 | { 49 | // 文字消息 50 | EMTextMessageBody *textMsgBody = (EMTextMessageBody *)msgBody; 51 | self.messageLabel.text = textMsgBody.text; 52 | } 53 | break; 54 | case EMMessageBodyTypeImage: 55 | { 56 | // 图片消息 57 | EMImageMessageBody *imageMsgBody = (EMImageMessageBody *)msgBody; 58 | [self showImage:imageMsgBody]; 59 | } 60 | break; 61 | case EMMessageBodyTypeVoice: 62 | { 63 | // 音频消息 64 | EMVoiceMessageBody *voiceMsgBody = (EMVoiceMessageBody *)msgBody; 65 | self.messageLabel.attributedText = [BRAttributedText voiceAttrText:messageModel isPlaying:NO]; 66 | } 67 | break; 68 | case EMMessageBodyTypeVideo: 69 | { 70 | // 视频消息 71 | EMVideoMessageBody *videoMsgBody = (EMVideoMessageBody *)msgBody; 72 | self.messageLabel.text = @"【视频消息】"; 73 | } 74 | break; 75 | case EMMessageBodyTypeLocation: 76 | { 77 | // 位置消息 78 | EMLocationMessageBody *locationMsgBody = (EMLocationMessageBody *)msgBody; 79 | self.messageLabel.text = @"【位置消息】"; 80 | } 81 | break; 82 | case EMMessageBodyTypeFile: 83 | { 84 | // 文件消息 85 | EMFileMessageBody *fileMsgBody = (EMFileMessageBody *)msgBody; 86 | self.messageLabel.text = @"【文件消息】"; 87 | } 88 | break; 89 | 90 | default: 91 | break; 92 | } 93 | } 94 | 95 | #pragma mark - 显示图片 96 | - (void)showImage:(EMImageMessageBody *)imageMsgBody { 97 | CGRect thumbFrame = (CGRect){0, 0, imageMsgBody.thumbnailSize}; 98 | // 设置label的尺寸足够显示UIImageView 99 | // 图片附件 100 | NSTextAttachment *imageAttachment = [[NSTextAttachment alloc]init]; 101 | imageAttachment.bounds = thumbFrame; 102 | NSAttributedString *attributedString = [NSAttributedString attributedStringWithAttachment:imageAttachment]; 103 | self.messageLabel.attributedText = attributedString; 104 | 105 | // 1. 在cell的Label上添加UIImageView 106 | [self.messageLabel addSubview:self.chatImageView]; 107 | self.chatImageView.frame = thumbFrame; 108 | 109 | // 下载图片 110 | NSLog(@"缩略图remote路径:%@", imageMsgBody.thumbnailRemotePath); 111 | NSLog(@"缩略图local路径:%@", imageMsgBody.thumbnailLocalPath); 112 | 113 | NSFileManager *manager = [NSFileManager defaultManager]; 114 | // 如果本地图片存在,直接从本地显示图片 115 | if ([manager fileExistsAtPath:imageMsgBody.thumbnailLocalPath]) { 116 | [self.chatImageView sd_setImageWithURL:[NSURL fileURLWithPath:imageMsgBody.thumbnailLocalPath] placeholderImage:[UIImage imageNamed:@"default.png"]]; 117 | } else { 118 | [self.chatImageView sd_setImageWithURL:[NSURL URLWithString:imageMsgBody.thumbnailRemotePath] placeholderImage:[UIImage imageNamed:@"default.png"]]; 119 | } 120 | } 121 | 122 | - (CGFloat)cellHeight { 123 | // 刷新布局,重新布局子控件 124 | [self layoutIfNeeded]; 125 | return 15 + self.messageLabel.bounds.size.height + 15; 126 | } 127 | 128 | - (UIImageView *)chatImageView { 129 | if (!_chatImageView) { 130 | _chatImageView = [[UIImageView alloc]init]; 131 | _chatImageView.backgroundColor = [UIColor redColor]; 132 | } 133 | return _chatImageView; 134 | } 135 | 136 | @end 137 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Chat/View/BRTimeCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // BRTimeCell.h 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/8/1. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BRTimeCell : UITableViewCell 12 | /** 时间label */ 13 | @property (weak, nonatomic) IBOutlet UILabel *timeLabel; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Chat/View/BRTimeCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // BRTimeCell.m 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/8/1. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import "BRTimeCell.h" 10 | 11 | @implementation BRTimeCell 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Chat/ViewController/BRChatViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // BRChatViewController.h 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/25. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BRChatViewController : UIViewController 12 | /** 好友 用户名 */ 13 | @property (nonatomic, strong) NSString *contactUsername; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Conversation/ViewController/BRConversationController.h: -------------------------------------------------------------------------------- 1 | // 2 | // BRConversationController.h 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/21. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BRConversationController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Login/ViewController/BRLoginViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // BRLoginViewController.h 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/20. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BRLoginViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Login/ViewController/BRLoginViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // BRLoginViewController.m 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/20. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import "BRLoginViewController.h" 10 | 11 | @interface BRLoginViewController () 12 | @property (weak, nonatomic) IBOutlet UITextField *usernameTF; 13 | @property (weak, nonatomic) IBOutlet UITextField *pwdTF; 14 | 15 | @end 16 | 17 | @implementation BRLoginViewController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | // Do any additional setup after loading the view. 22 | } 23 | 24 | #pragma mark - 登录 25 | - (IBAction)clickLoginBtn:(id)sender { 26 | NSString *username = self.usernameTF.text; 27 | NSString *password = self.pwdTF.text; 28 | if (username.length == 0 || password.length == 0) { 29 | NSLog(@"请输入用户名或密码"); 30 | return; 31 | } 32 | 33 | // // 判断是否设置了自动登录 34 | // BOOL isAutoLogin = [EMClient sharedClient].options.isAutoLogin; 35 | // if (!isAutoLogin) { 36 | // 37 | // } 38 | 39 | // 异步登录方法 40 | [[EMClient sharedClient] loginWithUsername:username password:password completion:^(NSString *aUsername, EMError *aError) { 41 | if (!aError) { 42 | NSLog(@"登录成功:%@", aUsername); 43 | NSLog(@"沙盒路径:%@", NSHomeDirectory()); 44 | // 开启自动登录 45 | [[EMClient sharedClient].options setIsAutoLogin:YES]; 46 | // 进入主界面(拿到 Main.storyboard 箭头所指的控制器,即 tabBarController) 47 | self.view.window.rootViewController = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateInitialViewController]; 48 | } else { 49 | NSLog(@"登录失败:%@", aError); 50 | } 51 | }]; 52 | } 53 | 54 | #pragma mark - 注册 55 | - (IBAction)clickRegisterBtn:(id)sender { 56 | NSString *username = self.usernameTF.text; 57 | NSString *password = self.pwdTF.text; 58 | if (username.length == 0 || password.length == 0) { 59 | NSLog(@"请输入用户名或密码"); 60 | return; 61 | } 62 | // 异步注册方法 63 | [[EMClient sharedClient] registerWithUsername:username password:password completion:^(NSString *aUsername, EMError *aError) { 64 | if (!aError) { 65 | NSLog(@"注册成功:%@", aUsername); 66 | } else { 67 | NSLog(@"注册失败:%@", aError); 68 | } 69 | }]; 70 | } 71 | 72 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 73 | [self.view endEditing:YES]; 74 | } 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Resource/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Classes/Resource/default.png -------------------------------------------------------------------------------- /TestIMDemo/Classes/Resource/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Classes/Resource/en.lproj/Localizable.strings -------------------------------------------------------------------------------- /TestIMDemo/Classes/Resource/in.caf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Classes/Resource/in.caf -------------------------------------------------------------------------------- /TestIMDemo/Classes/Resource/zh-Hans.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Classes/Resource/zh-Hans.lproj/Localizable.strings -------------------------------------------------------------------------------- /TestIMDemo/Classes/Setting/ViewController/BRSettingController.h: -------------------------------------------------------------------------------- 1 | // 2 | // BRSettingController.h 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/25. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BRSettingController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Setting/ViewController/BRSettingController.m: -------------------------------------------------------------------------------- 1 | // 2 | // BRSettingController.m 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/25. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import "BRSettingController.h" 10 | 11 | @interface BRSettingController () 12 | @property (weak, nonatomic) IBOutlet UIButton *logoutBtn; 13 | 14 | @end 15 | 16 | @implementation BRSettingController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | NSString *title = [NSString stringWithFormat:@"退出登录(%@)", [EMClient sharedClient].currentUsername]; 21 | [self.logoutBtn setTitle:title forState:UIControlStateNormal]; 22 | } 23 | 24 | #pragma mark - 退出登录 25 | - (IBAction)clickLogoutBtn:(id)sender { 26 | NSLog(@"退出登录"); 27 | [[EMClient sharedClient] logout:YES completion:^(EMError *aError) { 28 | if (!aError) { 29 | NSLog(@"退出登录成功!"); 30 | self.view.window.rootViewController = [[UIStoryboard storyboardWithName:@"Login" bundle:nil] instantiateInitialViewController]; 31 | } else { 32 | NSLog(@"退出登录失败:%@", aError); 33 | } 34 | }]; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Utils/Category/CALayer+LayerColor.h: -------------------------------------------------------------------------------- 1 | // 2 | // CALayer+LayerColor.h 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/26. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CALayer (LayerColor) 12 | 13 | - (void)setBorderColorFromUIColor:(UIColor *)color; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Utils/Category/CALayer+LayerColor.m: -------------------------------------------------------------------------------- 1 | // 2 | // CALayer+LayerColor.m 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/26. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import "CALayer+LayerColor.h" 10 | 11 | @implementation CALayer (LayerColor) 12 | 13 | - (void)setBorderColorFromUIColor:(UIColor *)color { 14 | self.borderColor = color.CGColor; 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Utils/Category/NSDate+BRAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDate+BRAdd.h 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/28. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSDate (BRAdd) 12 | /** 获取系统当前的时间戳,即当前时间距1970的秒数(以毫秒为单位) */ 13 | + (NSString *)currentTimestamp; 14 | 15 | 16 | /** 获取当前的时间 */ 17 | + (NSString *)currentDateString; 18 | 19 | 20 | /** 21 | * 按指定格式获取当前的时间 22 | * 23 | * @param formatterStr 设置格式:yyyy-MM-dd HH:mm:ss 24 | */ 25 | + (NSString *)currentDateStringWithFormat:(NSString *)formatterStr; 26 | 27 | /** 28 | * 获取聊天消息的时间 29 | * 时间格式如下: 30 | * 今天:【HH:mm】 31 | * 昨天:【昨天 HH:mm】 32 | * 昨天之前:【yyyy-MM-dd HH:mm】 33 | */ 34 | + (NSString *)chatTime:(long long)timestamp; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Utils/Category/NSDate+BRAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDate+BRAdd.m 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/28. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import "NSDate+BRAdd.h" 10 | 11 | @implementation NSDate (BRAdd) 12 | #pragma mark - 获取系统当前的时间戳,即当前时间距1970的秒数(以毫秒为单位) 13 | + (NSString *)currentTimestamp { 14 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:0]; 15 | /** 当前时间距1970的秒数。*1000 是精确到毫秒,不乘就是精确到秒 */ 16 | NSTimeInterval interval = [date timeIntervalSince1970] * 1000; 17 | NSString *timeString = [NSString stringWithFormat:@"%0.f", interval]; 18 | 19 | return timeString; 20 | } 21 | 22 | #pragma mark - 获取当前的时间 23 | + (NSString *)currentDateString { 24 | return [self currentDateStringWithFormat:@"yyyy-MM-dd HH:mm:ss"]; 25 | } 26 | 27 | #pragma mark - 按指定格式获取当前的时间 28 | + (NSString *)currentDateStringWithFormat:(NSString *)formatterStr { 29 | // 获取系统当前时间 30 | NSDate *currentDate = [NSDate date]; 31 | // 用于格式化NSDate对象 32 | NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; 33 | // 设置格式:yyyy-MM-dd HH:mm:ss 34 | formatter.dateFormat = formatterStr; 35 | // 将 NSDate 按 formatter格式 转成 NSString 36 | NSString *currentDateStr = [formatter stringFromDate:currentDate]; 37 | // 输出currentDateStr 38 | return currentDateStr; 39 | } 40 | 41 | #pragma mark - 获取聊天消息时间 42 | + (NSString *)chatTime:(long long)timestamp { 43 | NSCalendar *calendar = [NSCalendar currentCalendar]; 44 | // 1.获取当前时间的年、月、日 45 | NSDate *currentData = [NSDate date]; 46 | NSDateComponents *currentComponents = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:currentData]; 47 | NSInteger currentYear = currentComponents.year; 48 | NSInteger currentMonth = currentComponents.month; 49 | NSInteger currentDay = currentComponents.day; 50 | 51 | // 2.获取消息发送时间的年、月、日 52 | NSDate *msgData = [NSDate dateWithTimeIntervalSince1970:timestamp / 1000.0]; 53 | NSDateComponents *msgComponents = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:msgData]; 54 | NSInteger msgYear = msgComponents.year; 55 | NSInteger msgMonth = msgComponents.month; 56 | NSInteger msgDay = msgComponents.day; 57 | // 格式化 58 | NSDateFormatter *formatter = [[NSDateFormatter alloc]init]; 59 | if (currentYear == msgYear && currentMonth == msgMonth && currentDay == msgDay) { //今天 60 | formatter.dateFormat = @" HH:mm "; 61 | } else if (currentYear == msgYear && currentMonth == msgMonth && (currentDay - 1) == msgDay) { //昨天 62 | formatter.dateFormat = @" 昨天 HH:mm "; 63 | } else { //昨天以前 64 | formatter.dateFormat = @" yyyy年M月dd日 HH:mm "; 65 | } 66 | 67 | return [formatter stringFromDate:msgData]; 68 | } 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Utils/Category/UIViewController+Alert.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+Alert.h 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/25. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIViewController (Alert) 12 | /** 提示弹窗 */ 13 | - (void)showAlertWithTitle:(NSString *)title buttonTitle:(NSArray *)titleArr sureHandler:(void (^)())sureHandler cancelHandler:(void (^)())cancelHandler; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Utils/Category/UIViewController+Alert.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+Alert.m 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/25. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import "UIViewController+Alert.h" 10 | 11 | @implementation UIViewController (Alert) 12 | #pragma mark - 提示弹窗 13 | - (void)showAlertWithTitle:(NSString *)title buttonTitle:(NSArray *)titleArr sureHandler:(void (^)())sureHandler cancelHandler:(void (^)())cancelHandler { 14 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleAlert]; 15 | UIAlertAction *sureAction = [UIAlertAction actionWithTitle:titleArr[0] style:UIAlertActionStyleDefault handler:sureHandler]; 16 | UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:titleArr[1] style:UIAlertActionStyleDefault handler:cancelHandler]; 17 | [alertController addAction:sureAction]; 18 | [alertController addAction:cancelAction]; 19 | 20 | [self presentViewController:alertController animated:YES completion:nil]; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Utils/Tools/BRVoicePlayTool.h: -------------------------------------------------------------------------------- 1 | // 2 | // BRVoicePlayTool.h 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/28. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BRVoicePlayTool : NSObject 12 | /** 播放语音 */ 13 | + (void)playWithMessage:(EMMessage *)message msgLabel:(UILabel *)msgLabel; 14 | 15 | /** 停止播放语音 */ 16 | + (void)stopPlaying; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Utils/Tools/BRVoicePlayTool.m: -------------------------------------------------------------------------------- 1 | // 2 | // BRVoicePlayTool.m 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/28. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import "BRVoicePlayTool.h" 10 | #import "EMCDDeviceManager.h" 11 | #import "BRAttributedText.h" 12 | 13 | /** 正在执行动画的ImageView */ 14 | static UIImageView *animatingImageView; 15 | 16 | @implementation BRVoicePlayTool 17 | 18 | #pragma mark - 播放语音 19 | + (void)playWithMessage:(EMMessage *)message msgLabel:(UILabel *)msgLabel { 20 | // 进入这个方法前,先把以前的播放动画移除 21 | // 移除ImageView动画 22 | [animatingImageView stopAnimating]; 23 | [animatingImageView removeFromSuperview]; 24 | 25 | msgLabel.attributedText = [BRAttributedText voiceAttrText:message isPlaying:YES]; 26 | 27 | // 1.播放语音 28 | EMVoiceMessageBody *voiceBody = (EMVoiceMessageBody *)message.body; 29 | // 1.1 本地语音文件路径 30 | NSString *voicePath = voiceBody.localPath; 31 | // 1.2 如果本地语音文件不存在,就使用服务的语音 32 | NSFileManager *manager = [NSFileManager defaultManager]; 33 | if (![manager fileExistsAtPath:voicePath]) { 34 | voicePath = voiceBody.remotePath; 35 | } 36 | [[EMCDDeviceManager sharedInstance] asyncPlayingWithPath:voicePath completion:^(NSError *error) { 37 | if (!error) { 38 | NSLog(@"语音播放完成!"); 39 | // 移除ImageView动画 40 | [animatingImageView stopAnimating]; 41 | [animatingImageView removeFromSuperview]; 42 | 43 | msgLabel.attributedText = [BRAttributedText voiceAttrText:message isPlaying:NO]; 44 | } else { 45 | NSLog(@"语音播放失败:%@", error); 46 | } 47 | }]; 48 | 49 | // 2.添加播放动画 50 | // 2.1 创建一个imageView添加到label上 51 | UIImageView *imageView = [[UIImageView alloc]init]; 52 | [msgLabel addSubview:imageView]; 53 | // 2.2 给imageView添加动画图片 54 | if (message.direction == EMMessageDirectionReceive) { // 接收方(对方、左边的消息) 55 | imageView.frame = CGRectMake(0, 0, 20, 20); 56 | imageView.animationImages = @[[UIImage imageNamed:@"chat_receiver_audio_playing000"], 57 | [UIImage imageNamed:@"chat_receiver_audio_playing001"], 58 | [UIImage imageNamed:@"chat_receiver_audio_playing002"], 59 | [UIImage imageNamed:@"chat_receiver_audio_playing003"]]; 60 | } else { // 发送方(自己、右边的消息) 61 | imageView.frame = CGRectMake(msgLabel.bounds.size.width - 20, 0, 20, 20); 62 | imageView.animationImages = @[[UIImage imageNamed:@"chat_sender_audio_playing_000"], 63 | [UIImage imageNamed:@"chat_sender_audio_playing_001"], 64 | [UIImage imageNamed:@"chat_sender_audio_playing_002"], 65 | [UIImage imageNamed:@"chat_sender_audio_playing_003"]]; 66 | } 67 | // 动画时间 68 | imageView.animationDuration = 1.5; 69 | // 执行动画 70 | [imageView startAnimating]; 71 | animatingImageView = imageView; 72 | 73 | } 74 | 75 | #pragma mark - 停止播放语音 76 | + (void)stopPlaying { 77 | // 停止播放语音 78 | [[EMCDDeviceManager sharedInstance] stopPlaying]; 79 | // 移除ImageView动画 80 | [animatingImageView stopAnimating]; 81 | [animatingImageView removeFromSuperview]; 82 | } 83 | 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/MKAnnotationView+WebCache.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "SDWebImageCompat.h" 10 | 11 | #if SD_UIKIT || SD_MAC 12 | 13 | #import 14 | #import "SDWebImageManager.h" 15 | 16 | /** 17 | * Integrates SDWebImage async downloading and caching of remote images with MKAnnotationView. 18 | */ 19 | @interface MKAnnotationView (WebCache) 20 | 21 | /** 22 | * Set the imageView `image` with an `url`. 23 | * 24 | * The download is asynchronous and cached. 25 | * 26 | * @param url The url for the image. 27 | */ 28 | - (void)sd_setImageWithURL:(nullable NSURL *)url NS_REFINED_FOR_SWIFT; 29 | 30 | /** 31 | * Set the imageView `image` with an `url` and a placeholder. 32 | * 33 | * The download is asynchronous and cached. 34 | * 35 | * @param url The url for the image. 36 | * @param placeholder The image to be set initially, until the image request finishes. 37 | * @see sd_setImageWithURL:placeholderImage:options: 38 | */ 39 | - (void)sd_setImageWithURL:(nullable NSURL *)url 40 | placeholderImage:(nullable UIImage *)placeholder NS_REFINED_FOR_SWIFT; 41 | 42 | /** 43 | * Set the imageView `image` with an `url`, placeholder and custom options. 44 | * 45 | * The download is asynchronous and cached. 46 | * 47 | * @param url The url for the image. 48 | * @param placeholder The image to be set initially, until the image request finishes. 49 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. 50 | */ 51 | 52 | - (void)sd_setImageWithURL:(nullable NSURL *)url 53 | placeholderImage:(nullable UIImage *)placeholder 54 | options:(SDWebImageOptions)options NS_REFINED_FOR_SWIFT; 55 | 56 | /** 57 | * Set the imageView `image` with an `url`. 58 | * 59 | * The download is asynchronous and cached. 60 | * 61 | * @param url The url for the image. 62 | * @param completedBlock A block called when operation has been completed. This block has no return value 63 | * and takes the requested UIImage as first parameter. In case of error the image parameter 64 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 65 | * indicating if the image was retrieved from the local cache or from the network. 66 | * The fourth parameter is the original image url. 67 | */ 68 | - (void)sd_setImageWithURL:(nullable NSURL *)url 69 | completed:(nullable SDExternalCompletionBlock)completedBlock; 70 | 71 | /** 72 | * Set the imageView `image` with an `url`, placeholder. 73 | * 74 | * The download is asynchronous and cached. 75 | * 76 | * @param url The url for the image. 77 | * @param placeholder The image to be set initially, until the image request finishes. 78 | * @param completedBlock A block called when operation has been completed. This block has no return value 79 | * and takes the requested UIImage as first parameter. In case of error the image parameter 80 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 81 | * indicating if the image was retrieved from the local cache or from the network. 82 | * The fourth parameter is the original image url. 83 | */ 84 | - (void)sd_setImageWithURL:(nullable NSURL *)url 85 | placeholderImage:(nullable UIImage *)placeholder 86 | completed:(nullable SDExternalCompletionBlock)completedBlock NS_REFINED_FOR_SWIFT; 87 | 88 | /** 89 | * Set the imageView `image` with an `url`, placeholder and custom options. 90 | * 91 | * The download is asynchronous and cached. 92 | * 93 | * @param url The url for the image. 94 | * @param placeholder The image to be set initially, until the image request finishes. 95 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. 96 | * @param completedBlock A block called when operation has been completed. This block has no return value 97 | * and takes the requested UIImage as first parameter. In case of error the image parameter 98 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 99 | * indicating if the image was retrieved from the local cache or from the network. 100 | * The fourth parameter is the original image url. 101 | */ 102 | - (void)sd_setImageWithURL:(nullable NSURL *)url 103 | placeholderImage:(nullable UIImage *)placeholder 104 | options:(SDWebImageOptions)options 105 | completed:(nullable SDExternalCompletionBlock)completedBlock; 106 | 107 | @end 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/MKAnnotationView+WebCache.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "MKAnnotationView+WebCache.h" 10 | 11 | #if SD_UIKIT || SD_MAC 12 | 13 | #import "objc/runtime.h" 14 | #import "UIView+WebCacheOperation.h" 15 | #import "UIView+WebCache.h" 16 | 17 | @implementation MKAnnotationView (WebCache) 18 | 19 | - (void)sd_setImageWithURL:(nullable NSURL *)url { 20 | [self sd_setImageWithURL:url placeholderImage:nil options:0 completed:nil]; 21 | } 22 | 23 | - (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder { 24 | [self sd_setImageWithURL:url placeholderImage:placeholder options:0 completed:nil]; 25 | } 26 | 27 | - (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options { 28 | [self sd_setImageWithURL:url placeholderImage:placeholder options:options completed:nil]; 29 | } 30 | 31 | - (void)sd_setImageWithURL:(nullable NSURL *)url completed:(nullable SDExternalCompletionBlock)completedBlock { 32 | [self sd_setImageWithURL:url placeholderImage:nil options:0 completed:completedBlock]; 33 | } 34 | 35 | - (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder completed:(nullable SDExternalCompletionBlock)completedBlock { 36 | [self sd_setImageWithURL:url placeholderImage:placeholder options:0 completed:completedBlock]; 37 | } 38 | 39 | - (void)sd_setImageWithURL:(nullable NSURL *)url 40 | placeholderImage:(nullable UIImage *)placeholder 41 | options:(SDWebImageOptions)options 42 | completed:(nullable SDExternalCompletionBlock)completedBlock { 43 | __weak typeof(self)weakSelf = self; 44 | [self sd_internalSetImageWithURL:url 45 | placeholderImage:placeholder 46 | options:options 47 | operationKey:nil 48 | setImageBlock:^(UIImage *image, NSData *imageData) { 49 | weakSelf.image = image; 50 | } 51 | progress:nil 52 | completed:completedBlock]; 53 | } 54 | 55 | @end 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/NSData+ImageContentType.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * (c) Fabrice Aneche 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | #import 11 | #import "SDWebImageCompat.h" 12 | 13 | typedef NS_ENUM(NSInteger, SDImageFormat) { 14 | SDImageFormatUndefined = -1, 15 | SDImageFormatJPEG = 0, 16 | SDImageFormatPNG, 17 | SDImageFormatGIF, 18 | SDImageFormatTIFF, 19 | SDImageFormatWebP 20 | }; 21 | 22 | @interface NSData (ImageContentType) 23 | 24 | /** 25 | * Return image format 26 | * 27 | * @param data the input image data 28 | * 29 | * @return the image format as `SDImageFormat` (enum) 30 | */ 31 | + (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/NSData+ImageContentType.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * (c) Fabrice Aneche 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | #import "NSData+ImageContentType.h" 11 | 12 | 13 | @implementation NSData (ImageContentType) 14 | 15 | + (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data { 16 | if (!data) { 17 | return SDImageFormatUndefined; 18 | } 19 | 20 | uint8_t c; 21 | [data getBytes:&c length:1]; 22 | switch (c) { 23 | case 0xFF: 24 | return SDImageFormatJPEG; 25 | case 0x89: 26 | return SDImageFormatPNG; 27 | case 0x47: 28 | return SDImageFormatGIF; 29 | case 0x49: 30 | case 0x4D: 31 | return SDImageFormatTIFF; 32 | case 0x52: 33 | // R as RIFF for WEBP 34 | if (data.length < 12) { 35 | return SDImageFormatUndefined; 36 | } 37 | 38 | NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding]; 39 | if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) { 40 | return SDImageFormatWebP; 41 | } 42 | } 43 | return SDImageFormatUndefined; 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/NSImage+WebCache.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "SDWebImageCompat.h" 10 | 11 | #if SD_MAC 12 | 13 | #import 14 | 15 | @interface NSImage (WebCache) 16 | 17 | - (CGImageRef)CGImage; 18 | - (NSArray *)images; 19 | - (BOOL)isGIF; 20 | 21 | @end 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/NSImage+WebCache.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "NSImage+WebCache.h" 10 | 11 | #if SD_MAC 12 | 13 | @implementation NSImage (WebCache) 14 | 15 | - (CGImageRef)CGImage { 16 | NSRect imageRect = NSMakeRect(0, 0, self.size.width, self.size.height); 17 | CGImageRef cgImage = [self CGImageForProposedRect:&imageRect context:NULL hints:nil]; 18 | return cgImage; 19 | } 20 | 21 | - (NSArray *)images { 22 | return nil; 23 | } 24 | 25 | - (BOOL)isGIF { 26 | return NO; 27 | } 28 | 29 | @end 30 | 31 | #endif 32 | 33 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/SDImageCacheConfig.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import 10 | #import "SDWebImageCompat.h" 11 | 12 | @interface SDImageCacheConfig : NSObject 13 | 14 | /** 15 | * Decompressing images that are downloaded and cached can improve performance but can consume lot of memory. 16 | * Defaults to YES. Set this to NO if you are experiencing a crash due to excessive memory consumption. 17 | */ 18 | @property (assign, nonatomic) BOOL shouldDecompressImages; 19 | 20 | /** 21 | * disable iCloud backup [defaults to YES] 22 | */ 23 | @property (assign, nonatomic) BOOL shouldDisableiCloud; 24 | 25 | /** 26 | * use memory cache [defaults to YES] 27 | */ 28 | @property (assign, nonatomic) BOOL shouldCacheImagesInMemory; 29 | 30 | /** 31 | * The maximum length of time to keep an image in the cache, in seconds 32 | */ 33 | @property (assign, nonatomic) NSInteger maxCacheAge; 34 | 35 | /** 36 | * The maximum size of the cache, in bytes. 37 | */ 38 | @property (assign, nonatomic) NSUInteger maxCacheSize; 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/SDImageCacheConfig.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "SDImageCacheConfig.h" 10 | 11 | static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week 12 | 13 | @implementation SDImageCacheConfig 14 | 15 | - (instancetype)init { 16 | if (self = [super init]) { 17 | _shouldDecompressImages = YES; 18 | _shouldDisableiCloud = YES; 19 | _shouldCacheImagesInMemory = YES; 20 | _maxCacheAge = kDefaultCacheMaxCacheAge; 21 | _maxCacheSize = 0; 22 | } 23 | return self; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/SDWebImageCompat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * (c) Jamie Pinkham 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | #import 11 | 12 | #ifdef __OBJC_GC__ 13 | #error SDWebImage does not support Objective-C Garbage Collection 14 | #endif 15 | 16 | // Apple's defines from TargetConditionals.h are a bit weird. 17 | // Seems like TARGET_OS_MAC is always defined (on all platforms). 18 | // To determine if we are running on OSX, we can only rely on TARGET_OS_IPHONE=0 and all the other platforms 19 | #if !TARGET_OS_IPHONE && !TARGET_OS_IOS && !TARGET_OS_TV && !TARGET_OS_WATCH 20 | #define SD_MAC 1 21 | #else 22 | #define SD_MAC 0 23 | #endif 24 | 25 | // iOS and tvOS are very similar, UIKit exists on both platforms 26 | // Note: watchOS also has UIKit, but it's very limited 27 | #if TARGET_OS_IOS || TARGET_OS_TV 28 | #define SD_UIKIT 1 29 | #else 30 | #define SD_UIKIT 0 31 | #endif 32 | 33 | #if TARGET_OS_IOS 34 | #define SD_IOS 1 35 | #else 36 | #define SD_IOS 0 37 | #endif 38 | 39 | #if TARGET_OS_TV 40 | #define SD_TV 1 41 | #else 42 | #define SD_TV 0 43 | #endif 44 | 45 | #if TARGET_OS_WATCH 46 | #define SD_WATCH 1 47 | #else 48 | #define SD_WATCH 0 49 | #endif 50 | 51 | 52 | #if SD_MAC 53 | #import 54 | #ifndef UIImage 55 | #define UIImage NSImage 56 | #endif 57 | #ifndef UIImageView 58 | #define UIImageView NSImageView 59 | #endif 60 | #ifndef UIView 61 | #define UIView NSView 62 | #endif 63 | #else 64 | #if __IPHONE_OS_VERSION_MIN_REQUIRED != 20000 && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_5_0 65 | #error SDWebImage doesn't support Deployment Target version < 5.0 66 | #endif 67 | 68 | #if SD_UIKIT 69 | #import 70 | #endif 71 | #if SD_WATCH 72 | #import 73 | #endif 74 | #endif 75 | 76 | #ifndef NS_ENUM 77 | #define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type 78 | #endif 79 | 80 | #ifndef NS_OPTIONS 81 | #define NS_OPTIONS(_type, _name) enum _name : _type _name; enum _name : _type 82 | #endif 83 | 84 | #if OS_OBJECT_USE_OBJC 85 | #undef SDDispatchQueueRelease 86 | #undef SDDispatchQueueSetterSementics 87 | #define SDDispatchQueueRelease(q) 88 | #define SDDispatchQueueSetterSementics strong 89 | #else 90 | #undef SDDispatchQueueRelease 91 | #undef SDDispatchQueueSetterSementics 92 | #define SDDispatchQueueRelease(q) (dispatch_release(q)) 93 | #define SDDispatchQueueSetterSementics assign 94 | #endif 95 | 96 | extern UIImage *SDScaledImageForKey(NSString *key, UIImage *image); 97 | 98 | typedef void(^SDWebImageNoParamsBlock)(); 99 | 100 | extern NSString *const SDWebImageErrorDomain; 101 | 102 | #ifndef dispatch_main_async_safe 103 | #define dispatch_main_async_safe(block)\ 104 | if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(dispatch_get_main_queue())) == 0) {\ 105 | block();\ 106 | } else {\ 107 | dispatch_async(dispatch_get_main_queue(), block);\ 108 | } 109 | #endif 110 | 111 | static int64_t kAsyncTestTimeout = 5; 112 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/SDWebImageCompat.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "SDWebImageCompat.h" 10 | 11 | #import "objc/runtime.h" 12 | 13 | #if !__has_feature(objc_arc) 14 | #error SDWebImage is ARC only. Either turn on ARC for the project or use -fobjc-arc flag 15 | #endif 16 | 17 | inline UIImage *SDScaledImageForKey(NSString * _Nullable key, UIImage * _Nullable image) { 18 | if (!image) { 19 | return nil; 20 | } 21 | 22 | #if SD_MAC 23 | return image; 24 | #elif SD_UIKIT || SD_WATCH 25 | if ((image.images).count > 0) { 26 | NSMutableArray *scaledImages = [NSMutableArray array]; 27 | 28 | for (UIImage *tempImage in image.images) { 29 | [scaledImages addObject:SDScaledImageForKey(key, tempImage)]; 30 | } 31 | 32 | UIImage *animatedImage = [UIImage animatedImageWithImages:scaledImages duration:image.duration]; 33 | #ifdef SD_WEBP 34 | if (animatedImage) { 35 | SEL sd_webpLoopCount = NSSelectorFromString(@"sd_webpLoopCount"); 36 | NSNumber *value = objc_getAssociatedObject(image, sd_webpLoopCount); 37 | NSInteger loopCount = value.integerValue; 38 | if (loopCount) { 39 | objc_setAssociatedObject(animatedImage, sd_webpLoopCount, @(loopCount), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 40 | } 41 | } 42 | #endif 43 | return animatedImage; 44 | } else { 45 | #if SD_WATCH 46 | if ([[WKInterfaceDevice currentDevice] respondsToSelector:@selector(screenScale)]) { 47 | #elif SD_UIKIT 48 | if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) { 49 | #endif 50 | CGFloat scale = 1; 51 | if (key.length >= 8) { 52 | NSRange range = [key rangeOfString:@"@2x."]; 53 | if (range.location != NSNotFound) { 54 | scale = 2.0; 55 | } 56 | 57 | range = [key rangeOfString:@"@3x."]; 58 | if (range.location != NSNotFound) { 59 | scale = 3.0; 60 | } 61 | } 62 | 63 | UIImage *scaledImage = [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation]; 64 | image = scaledImage; 65 | } 66 | return image; 67 | } 68 | #endif 69 | } 70 | 71 | NSString *const SDWebImageErrorDomain = @"SDWebImageErrorDomain"; 72 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/SDWebImageDecoder.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * (c) james 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | #import 11 | #import "SDWebImageCompat.h" 12 | 13 | @interface UIImage (ForceDecode) 14 | 15 | + (nullable UIImage *)decodedImageWithImage:(nullable UIImage *)image; 16 | 17 | + (nullable UIImage *)decodedAndScaledDownImageWithImage:(nullable UIImage *)image; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/SDWebImageDownloaderOperation.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import 10 | #import "SDWebImageDownloader.h" 11 | #import "SDWebImageOperation.h" 12 | 13 | extern NSString * _Nonnull const SDWebImageDownloadStartNotification; 14 | extern NSString * _Nonnull const SDWebImageDownloadReceiveResponseNotification; 15 | extern NSString * _Nonnull const SDWebImageDownloadStopNotification; 16 | extern NSString * _Nonnull const SDWebImageDownloadFinishNotification; 17 | 18 | 19 | 20 | /** 21 | Describes a downloader operation. If one wants to use a custom downloader op, it needs to inherit from `NSOperation` and conform to this protocol 22 | */ 23 | @protocol SDWebImageDownloaderOperationInterface 24 | 25 | - (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request 26 | inSession:(nullable NSURLSession *)session 27 | options:(SDWebImageDownloaderOptions)options; 28 | 29 | - (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 30 | completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock; 31 | 32 | - (BOOL)shouldDecompressImages; 33 | - (void)setShouldDecompressImages:(BOOL)value; 34 | 35 | - (nullable NSURLCredential *)credential; 36 | - (void)setCredential:(nullable NSURLCredential *)value; 37 | 38 | @end 39 | 40 | 41 | @interface SDWebImageDownloaderOperation : NSOperation 42 | 43 | /** 44 | * The request used by the operation's task. 45 | */ 46 | @property (strong, nonatomic, readonly, nullable) NSURLRequest *request; 47 | 48 | /** 49 | * The operation's task 50 | */ 51 | @property (strong, nonatomic, readonly, nullable) NSURLSessionTask *dataTask; 52 | 53 | 54 | @property (assign, nonatomic) BOOL shouldDecompressImages; 55 | 56 | /** 57 | * Was used to determine whether the URL connection should consult the credential storage for authenticating the connection. 58 | * @deprecated Not used for a couple of versions 59 | */ 60 | @property (nonatomic, assign) BOOL shouldUseCredentialStorage __deprecated_msg("Property deprecated. Does nothing. Kept only for backwards compatibility"); 61 | 62 | /** 63 | * The credential used for authentication challenges in `-connection:didReceiveAuthenticationChallenge:`. 64 | * 65 | * This will be overridden by any shared credentials that exist for the username or password of the request URL, if present. 66 | */ 67 | @property (nonatomic, strong, nullable) NSURLCredential *credential; 68 | 69 | /** 70 | * The SDWebImageDownloaderOptions for the receiver. 71 | */ 72 | @property (assign, nonatomic, readonly) SDWebImageDownloaderOptions options; 73 | 74 | /** 75 | * The expected size of data. 76 | */ 77 | @property (assign, nonatomic) NSInteger expectedSize; 78 | 79 | /** 80 | * The response returned by the operation's connection. 81 | */ 82 | @property (strong, nonatomic, nullable) NSURLResponse *response; 83 | 84 | /** 85 | * Initializes a `SDWebImageDownloaderOperation` object 86 | * 87 | * @see SDWebImageDownloaderOperation 88 | * 89 | * @param request the URL request 90 | * @param session the URL session in which this operation will run 91 | * @param options downloader options 92 | * 93 | * @return the initialized instance 94 | */ 95 | - (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request 96 | inSession:(nullable NSURLSession *)session 97 | options:(SDWebImageDownloaderOptions)options NS_DESIGNATED_INITIALIZER; 98 | 99 | /** 100 | * Adds handlers for progress and completion. Returns a tokent that can be passed to -cancel: to cancel this set of 101 | * callbacks. 102 | * 103 | * @param progressBlock the block executed when a new chunk of data arrives. 104 | * @note the progress block is executed on a background queue 105 | * @param completedBlock the block executed when the download is done. 106 | * @note the completed block is executed on the main queue for success. If errors are found, there is a chance the block will be executed on a background queue 107 | * 108 | * @return the token to use to cancel this set of handlers 109 | */ 110 | - (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 111 | completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock; 112 | 113 | /** 114 | * Cancels a set of callbacks. Once all callbacks are canceled, the operation is cancelled. 115 | * 116 | * @param token the token representing a set of callbacks to cancel 117 | * 118 | * @return YES if the operation was stopped because this was the last token to be canceled. NO otherwise. 119 | */ 120 | - (BOOL)cancel:(nullable id)token; 121 | 122 | @end 123 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/SDWebImageOperation.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import 10 | 11 | @protocol SDWebImageOperation 12 | 13 | - (void)cancel; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/SDWebImagePrefetcher.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import 10 | #import "SDWebImageManager.h" 11 | 12 | @class SDWebImagePrefetcher; 13 | 14 | @protocol SDWebImagePrefetcherDelegate 15 | 16 | @optional 17 | 18 | /** 19 | * Called when an image was prefetched. 20 | * 21 | * @param imagePrefetcher The current image prefetcher 22 | * @param imageURL The image url that was prefetched 23 | * @param finishedCount The total number of images that were prefetched (successful or not) 24 | * @param totalCount The total number of images that were to be prefetched 25 | */ 26 | - (void)imagePrefetcher:(nonnull SDWebImagePrefetcher *)imagePrefetcher didPrefetchURL:(nullable NSURL *)imageURL finishedCount:(NSUInteger)finishedCount totalCount:(NSUInteger)totalCount; 27 | 28 | /** 29 | * Called when all images are prefetched. 30 | * @param imagePrefetcher The current image prefetcher 31 | * @param totalCount The total number of images that were prefetched (whether successful or not) 32 | * @param skippedCount The total number of images that were skipped 33 | */ 34 | - (void)imagePrefetcher:(nonnull SDWebImagePrefetcher *)imagePrefetcher didFinishWithTotalCount:(NSUInteger)totalCount skippedCount:(NSUInteger)skippedCount; 35 | 36 | @end 37 | 38 | typedef void(^SDWebImagePrefetcherProgressBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfTotalUrls); 39 | typedef void(^SDWebImagePrefetcherCompletionBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfSkippedUrls); 40 | 41 | /** 42 | * Prefetch some URLs in the cache for future use. Images are downloaded in low priority. 43 | */ 44 | @interface SDWebImagePrefetcher : NSObject 45 | 46 | /** 47 | * The web image manager 48 | */ 49 | @property (strong, nonatomic, readonly, nonnull) SDWebImageManager *manager; 50 | 51 | /** 52 | * Maximum number of URLs to prefetch at the same time. Defaults to 3. 53 | */ 54 | @property (nonatomic, assign) NSUInteger maxConcurrentDownloads; 55 | 56 | /** 57 | * SDWebImageOptions for prefetcher. Defaults to SDWebImageLowPriority. 58 | */ 59 | @property (nonatomic, assign) SDWebImageOptions options; 60 | 61 | /** 62 | * Queue options for Prefetcher. Defaults to Main Queue. 63 | */ 64 | @property (SDDispatchQueueSetterSementics, nonatomic, nonnull) dispatch_queue_t prefetcherQueue; 65 | 66 | @property (weak, nonatomic, nullable) id delegate; 67 | 68 | /** 69 | * Return the global image prefetcher instance. 70 | */ 71 | + (nonnull instancetype)sharedImagePrefetcher; 72 | 73 | /** 74 | * Allows you to instantiate a prefetcher with any arbitrary image manager. 75 | */ 76 | - (nonnull instancetype)initWithImageManager:(nonnull SDWebImageManager *)manager NS_DESIGNATED_INITIALIZER; 77 | 78 | /** 79 | * Assign list of URLs to let SDWebImagePrefetcher to queue the prefetching, 80 | * currently one image is downloaded at a time, 81 | * and skips images for failed downloads and proceed to the next image in the list. 82 | * Any previously-running prefetch operations are canceled. 83 | * 84 | * @param urls list of URLs to prefetch 85 | */ 86 | - (void)prefetchURLs:(nullable NSArray *)urls; 87 | 88 | /** 89 | * Assign list of URLs to let SDWebImagePrefetcher to queue the prefetching, 90 | * currently one image is downloaded at a time, 91 | * and skips images for failed downloads and proceed to the next image in the list. 92 | * Any previously-running prefetch operations are canceled. 93 | * 94 | * @param urls list of URLs to prefetch 95 | * @param progressBlock block to be called when progress updates; 96 | * first parameter is the number of completed (successful or not) requests, 97 | * second parameter is the total number of images originally requested to be prefetched 98 | * @param completionBlock block to be called when prefetching is completed 99 | * first param is the number of completed (successful or not) requests, 100 | * second parameter is the number of skipped requests 101 | */ 102 | - (void)prefetchURLs:(nullable NSArray *)urls 103 | progress:(nullable SDWebImagePrefetcherProgressBlock)progressBlock 104 | completed:(nullable SDWebImagePrefetcherCompletionBlock)completionBlock; 105 | 106 | /** 107 | * Remove and cancel queued list 108 | */ 109 | - (void)cancelPrefetching; 110 | 111 | 112 | @end 113 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/SDWebImagePrefetcher.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "SDWebImagePrefetcher.h" 10 | 11 | @interface SDWebImagePrefetcher () 12 | 13 | @property (strong, nonatomic, nonnull) SDWebImageManager *manager; 14 | @property (strong, nonatomic, nullable) NSArray *prefetchURLs; 15 | @property (assign, nonatomic) NSUInteger requestedCount; 16 | @property (assign, nonatomic) NSUInteger skippedCount; 17 | @property (assign, nonatomic) NSUInteger finishedCount; 18 | @property (assign, nonatomic) NSTimeInterval startedTime; 19 | @property (copy, nonatomic, nullable) SDWebImagePrefetcherCompletionBlock completionBlock; 20 | @property (copy, nonatomic, nullable) SDWebImagePrefetcherProgressBlock progressBlock; 21 | 22 | @end 23 | 24 | @implementation SDWebImagePrefetcher 25 | 26 | + (nonnull instancetype)sharedImagePrefetcher { 27 | static dispatch_once_t once; 28 | static id instance; 29 | dispatch_once(&once, ^{ 30 | instance = [self new]; 31 | }); 32 | return instance; 33 | } 34 | 35 | - (nonnull instancetype)init { 36 | return [self initWithImageManager:[SDWebImageManager new]]; 37 | } 38 | 39 | - (nonnull instancetype)initWithImageManager:(SDWebImageManager *)manager { 40 | if ((self = [super init])) { 41 | _manager = manager; 42 | _options = SDWebImageLowPriority; 43 | _prefetcherQueue = dispatch_get_main_queue(); 44 | self.maxConcurrentDownloads = 3; 45 | } 46 | return self; 47 | } 48 | 49 | - (void)setMaxConcurrentDownloads:(NSUInteger)maxConcurrentDownloads { 50 | self.manager.imageDownloader.maxConcurrentDownloads = maxConcurrentDownloads; 51 | } 52 | 53 | - (NSUInteger)maxConcurrentDownloads { 54 | return self.manager.imageDownloader.maxConcurrentDownloads; 55 | } 56 | 57 | - (void)startPrefetchingAtIndex:(NSUInteger)index { 58 | if (index >= self.prefetchURLs.count) return; 59 | self.requestedCount++; 60 | [self.manager loadImageWithURL:self.prefetchURLs[index] options:self.options progress:nil completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { 61 | if (!finished) return; 62 | self.finishedCount++; 63 | 64 | if (image) { 65 | if (self.progressBlock) { 66 | self.progressBlock(self.finishedCount,(self.prefetchURLs).count); 67 | } 68 | } 69 | else { 70 | if (self.progressBlock) { 71 | self.progressBlock(self.finishedCount,(self.prefetchURLs).count); 72 | } 73 | // Add last failed 74 | self.skippedCount++; 75 | } 76 | if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didPrefetchURL:finishedCount:totalCount:)]) { 77 | [self.delegate imagePrefetcher:self 78 | didPrefetchURL:self.prefetchURLs[index] 79 | finishedCount:self.finishedCount 80 | totalCount:self.prefetchURLs.count 81 | ]; 82 | } 83 | if (self.prefetchURLs.count > self.requestedCount) { 84 | dispatch_async(self.prefetcherQueue, ^{ 85 | [self startPrefetchingAtIndex:self.requestedCount]; 86 | }); 87 | } else if (self.finishedCount == self.requestedCount) { 88 | [self reportStatus]; 89 | if (self.completionBlock) { 90 | self.completionBlock(self.finishedCount, self.skippedCount); 91 | self.completionBlock = nil; 92 | } 93 | self.progressBlock = nil; 94 | } 95 | }]; 96 | } 97 | 98 | - (void)reportStatus { 99 | NSUInteger total = (self.prefetchURLs).count; 100 | if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didFinishWithTotalCount:skippedCount:)]) { 101 | [self.delegate imagePrefetcher:self 102 | didFinishWithTotalCount:(total - self.skippedCount) 103 | skippedCount:self.skippedCount 104 | ]; 105 | } 106 | } 107 | 108 | - (void)prefetchURLs:(nullable NSArray *)urls { 109 | [self prefetchURLs:urls progress:nil completed:nil]; 110 | } 111 | 112 | - (void)prefetchURLs:(nullable NSArray *)urls 113 | progress:(nullable SDWebImagePrefetcherProgressBlock)progressBlock 114 | completed:(nullable SDWebImagePrefetcherCompletionBlock)completionBlock { 115 | [self cancelPrefetching]; // Prevent duplicate prefetch request 116 | self.startedTime = CFAbsoluteTimeGetCurrent(); 117 | self.prefetchURLs = urls; 118 | self.completionBlock = completionBlock; 119 | self.progressBlock = progressBlock; 120 | 121 | if (urls.count == 0) { 122 | if (completionBlock) { 123 | completionBlock(0,0); 124 | } 125 | } else { 126 | // Starts prefetching from the very first image on the list with the max allowed concurrency 127 | NSUInteger listCount = self.prefetchURLs.count; 128 | for (NSUInteger i = 0; i < self.maxConcurrentDownloads && self.requestedCount < listCount; i++) { 129 | [self startPrefetchingAtIndex:i]; 130 | } 131 | } 132 | } 133 | 134 | - (void)cancelPrefetching { 135 | self.prefetchURLs = nil; 136 | self.skippedCount = 0; 137 | self.requestedCount = 0; 138 | self.finishedCount = 0; 139 | [self.manager cancelAll]; 140 | } 141 | 142 | @end 143 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/UIImage+GIF.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * (c) Laurin Brandner 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | #import "SDWebImageCompat.h" 11 | 12 | @interface UIImage (GIF) 13 | 14 | /** 15 | * Compatibility method - creates an animated UIImage from an NSData, it will only contain the 1st frame image 16 | */ 17 | + (UIImage *)sd_animatedGIFWithData:(NSData *)data; 18 | 19 | /** 20 | * Checks if an UIImage instance is a GIF. Will use the `images` array 21 | */ 22 | - (BOOL)isGIF; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/UIImage+GIF.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * (c) Laurin Brandner 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | #import "UIImage+GIF.h" 11 | #import 12 | #import "objc/runtime.h" 13 | #import "NSImage+WebCache.h" 14 | 15 | @implementation UIImage (GIF) 16 | 17 | + (UIImage *)sd_animatedGIFWithData:(NSData *)data { 18 | if (!data) { 19 | return nil; 20 | } 21 | 22 | #if SD_MAC 23 | return [[UIImage alloc] initWithData:data]; 24 | #endif 25 | 26 | CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL); 27 | 28 | size_t count = CGImageSourceGetCount(source); 29 | 30 | UIImage *staticImage; 31 | 32 | if (count <= 1) { 33 | staticImage = [[UIImage alloc] initWithData:data]; 34 | } else { 35 | // we will only retrieve the 1st frame. the full GIF support is available via the FLAnimatedImageView category. 36 | // this here is only code to allow drawing animated images as static ones 37 | #if SD_WATCH 38 | CGFloat scale = 1; 39 | scale = [WKInterfaceDevice currentDevice].screenScale; 40 | #elif SD_UIKIT 41 | CGFloat scale = 1; 42 | scale = [UIScreen mainScreen].scale; 43 | #endif 44 | 45 | CGImageRef CGImage = CGImageSourceCreateImageAtIndex(source, 0, NULL); 46 | #if SD_UIKIT || SD_WATCH 47 | UIImage *frameImage = [UIImage imageWithCGImage:CGImage scale:scale orientation:UIImageOrientationUp]; 48 | staticImage = [UIImage animatedImageWithImages:@[frameImage] duration:0.0f]; 49 | #endif 50 | CGImageRelease(CGImage); 51 | } 52 | 53 | CFRelease(source); 54 | 55 | return staticImage; 56 | } 57 | 58 | - (BOOL)isGIF { 59 | return (self.images != nil); 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/UIImage+MultiFormat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "SDWebImageCompat.h" 10 | #import "NSData+ImageContentType.h" 11 | 12 | @interface UIImage (MultiFormat) 13 | 14 | + (nullable UIImage *)sd_imageWithData:(nullable NSData *)data; 15 | - (nullable NSData *)sd_imageData; 16 | - (nullable NSData *)sd_imageDataAsFormat:(SDImageFormat)imageFormat; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/UIImage+MultiFormat.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "UIImage+MultiFormat.h" 10 | #import "UIImage+GIF.h" 11 | #import "NSData+ImageContentType.h" 12 | #import 13 | 14 | #ifdef SD_WEBP 15 | #import "UIImage+WebP.h" 16 | #endif 17 | 18 | @implementation UIImage (MultiFormat) 19 | 20 | + (nullable UIImage *)sd_imageWithData:(nullable NSData *)data { 21 | if (!data) { 22 | return nil; 23 | } 24 | 25 | UIImage *image; 26 | SDImageFormat imageFormat = [NSData sd_imageFormatForImageData:data]; 27 | if (imageFormat == SDImageFormatGIF) { 28 | image = [UIImage sd_animatedGIFWithData:data]; 29 | } 30 | #ifdef SD_WEBP 31 | else if (imageFormat == SDImageFormatWebP) 32 | { 33 | image = [UIImage sd_imageWithWebPData:data]; 34 | } 35 | #endif 36 | else { 37 | image = [[UIImage alloc] initWithData:data]; 38 | #if SD_UIKIT || SD_WATCH 39 | UIImageOrientation orientation = [self sd_imageOrientationFromImageData:data]; 40 | if (orientation != UIImageOrientationUp) { 41 | image = [UIImage imageWithCGImage:image.CGImage 42 | scale:image.scale 43 | orientation:orientation]; 44 | } 45 | #endif 46 | } 47 | 48 | 49 | return image; 50 | } 51 | 52 | #if SD_UIKIT || SD_WATCH 53 | +(UIImageOrientation)sd_imageOrientationFromImageData:(nonnull NSData *)imageData { 54 | UIImageOrientation result = UIImageOrientationUp; 55 | CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL); 56 | if (imageSource) { 57 | CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL); 58 | if (properties) { 59 | CFTypeRef val; 60 | int exifOrientation; 61 | val = CFDictionaryGetValue(properties, kCGImagePropertyOrientation); 62 | if (val) { 63 | CFNumberGetValue(val, kCFNumberIntType, &exifOrientation); 64 | result = [self sd_exifOrientationToiOSOrientation:exifOrientation]; 65 | } // else - if it's not set it remains at up 66 | CFRelease((CFTypeRef) properties); 67 | } else { 68 | //NSLog(@"NO PROPERTIES, FAIL"); 69 | } 70 | CFRelease(imageSource); 71 | } 72 | return result; 73 | } 74 | 75 | #pragma mark EXIF orientation tag converter 76 | // Convert an EXIF image orientation to an iOS one. 77 | // reference see here: http://sylvana.net/jpegcrop/exif_orientation.html 78 | + (UIImageOrientation) sd_exifOrientationToiOSOrientation:(int)exifOrientation { 79 | UIImageOrientation orientation = UIImageOrientationUp; 80 | switch (exifOrientation) { 81 | case 1: 82 | orientation = UIImageOrientationUp; 83 | break; 84 | 85 | case 3: 86 | orientation = UIImageOrientationDown; 87 | break; 88 | 89 | case 8: 90 | orientation = UIImageOrientationLeft; 91 | break; 92 | 93 | case 6: 94 | orientation = UIImageOrientationRight; 95 | break; 96 | 97 | case 2: 98 | orientation = UIImageOrientationUpMirrored; 99 | break; 100 | 101 | case 4: 102 | orientation = UIImageOrientationDownMirrored; 103 | break; 104 | 105 | case 5: 106 | orientation = UIImageOrientationLeftMirrored; 107 | break; 108 | 109 | case 7: 110 | orientation = UIImageOrientationRightMirrored; 111 | break; 112 | default: 113 | break; 114 | } 115 | return orientation; 116 | } 117 | #endif 118 | 119 | - (nullable NSData *)sd_imageData { 120 | return [self sd_imageDataAsFormat:SDImageFormatUndefined]; 121 | } 122 | 123 | - (nullable NSData *)sd_imageDataAsFormat:(SDImageFormat)imageFormat { 124 | NSData *imageData = nil; 125 | if (self) { 126 | #if SD_UIKIT || SD_WATCH 127 | int alphaInfo = CGImageGetAlphaInfo(self.CGImage); 128 | BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone || 129 | alphaInfo == kCGImageAlphaNoneSkipFirst || 130 | alphaInfo == kCGImageAlphaNoneSkipLast); 131 | 132 | BOOL usePNG = hasAlpha; 133 | 134 | // the imageFormat param has priority here. But if the format is undefined, we relly on the alpha channel 135 | if (imageFormat != SDImageFormatUndefined) { 136 | usePNG = (imageFormat == SDImageFormatPNG); 137 | } 138 | 139 | if (usePNG) { 140 | imageData = UIImagePNGRepresentation(self); 141 | } else { 142 | imageData = UIImageJPEGRepresentation(self, (CGFloat)1.0); 143 | } 144 | #else 145 | NSBitmapImageFileType imageFileType = NSJPEGFileType; 146 | if (imageFormat == SDImageFormatGIF) { 147 | imageFileType = NSGIFFileType; 148 | } else if (imageFormat == SDImageFormatPNG) { 149 | imageFileType = NSPNGFileType; 150 | } 151 | 152 | imageData = [NSBitmapImageRep representationOfImageRepsInArray:self.representations 153 | usingType:imageFileType 154 | properties:@{}]; 155 | #endif 156 | } 157 | return imageData; 158 | } 159 | 160 | 161 | @end 162 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/UIImage+WebP.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #ifdef SD_WEBP 10 | 11 | #import "SDWebImageCompat.h" 12 | 13 | @interface UIImage (WebP) 14 | 15 | /** 16 | * Get the current WebP image loop count, the default value is 0. 17 | * For static WebP image, the value is 0. 18 | * For animated WebP image, 0 means repeat the animation indefinitely. 19 | * Note that because of the limitations of categories this property can get out of sync 20 | * if you create another instance with CGImage or other methods. 21 | * @return WebP image loop count 22 | */ 23 | - (NSInteger)sd_webpLoopCount; 24 | 25 | + (nullable UIImage *)sd_imageWithWebPData:(nullable NSData *)data; 26 | 27 | @end 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/UIImageView+HighlightedWebCache.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "SDWebImageCompat.h" 10 | 11 | #if SD_UIKIT 12 | 13 | #import "SDWebImageManager.h" 14 | 15 | /** 16 | * Integrates SDWebImage async downloading and caching of remote images with UIImageView for highlighted state. 17 | */ 18 | @interface UIImageView (HighlightedWebCache) 19 | 20 | /** 21 | * Set the imageView `highlightedImage` with an `url`. 22 | * 23 | * The download is asynchronous and cached. 24 | * 25 | * @param url The url for the image. 26 | */ 27 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url NS_REFINED_FOR_SWIFT; 28 | 29 | /** 30 | * Set the imageView `highlightedImage` with an `url` and custom options. 31 | * 32 | * The download is asynchronous and cached. 33 | * 34 | * @param url The url for the image. 35 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. 36 | */ 37 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url 38 | options:(SDWebImageOptions)options NS_REFINED_FOR_SWIFT; 39 | 40 | /** 41 | * Set the imageView `highlightedImage` with an `url`. 42 | * 43 | * The download is asynchronous and cached. 44 | * 45 | * @param url The url for the image. 46 | * @param completedBlock A block called when operation has been completed. This block has no return value 47 | * and takes the requested UIImage as first parameter. In case of error the image parameter 48 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 49 | * indicating if the image was retrieved from the local cache or from the network. 50 | * The fourth parameter is the original image url. 51 | */ 52 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url 53 | completed:(nullable SDExternalCompletionBlock)completedBlock NS_REFINED_FOR_SWIFT; 54 | 55 | /** 56 | * Set the imageView `highlightedImage` with an `url` and custom options. 57 | * 58 | * The download is asynchronous and cached. 59 | * 60 | * @param url The url for the image. 61 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. 62 | * @param completedBlock A block called when operation has been completed. This block has no return value 63 | * and takes the requested UIImage as first parameter. In case of error the image parameter 64 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 65 | * indicating if the image was retrieved from the local cache or from the network. 66 | * The fourth parameter is the original image url. 67 | */ 68 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url 69 | options:(SDWebImageOptions)options 70 | completed:(nullable SDExternalCompletionBlock)completedBlock; 71 | 72 | /** 73 | * Set the imageView `highlightedImage` with an `url` and custom options. 74 | * 75 | * The download is asynchronous and cached. 76 | * 77 | * @param url The url for the image. 78 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. 79 | * @param progressBlock A block called while image is downloading 80 | * @note the progress block is executed on a background queue 81 | * @param completedBlock A block called when operation has been completed. This block has no return value 82 | * and takes the requested UIImage as first parameter. In case of error the image parameter 83 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 84 | * indicating if the image was retrieved from the local cache or from the network. 85 | * The fourth parameter is the original image url. 86 | */ 87 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url 88 | options:(SDWebImageOptions)options 89 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 90 | completed:(nullable SDExternalCompletionBlock)completedBlock; 91 | 92 | @end 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/UIImageView+HighlightedWebCache.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "UIImageView+HighlightedWebCache.h" 10 | 11 | #if SD_UIKIT 12 | 13 | #import "UIView+WebCacheOperation.h" 14 | #import "UIView+WebCache.h" 15 | 16 | @implementation UIImageView (HighlightedWebCache) 17 | 18 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url { 19 | [self sd_setHighlightedImageWithURL:url options:0 progress:nil completed:nil]; 20 | } 21 | 22 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url options:(SDWebImageOptions)options { 23 | [self sd_setHighlightedImageWithURL:url options:options progress:nil completed:nil]; 24 | } 25 | 26 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url completed:(nullable SDExternalCompletionBlock)completedBlock { 27 | [self sd_setHighlightedImageWithURL:url options:0 progress:nil completed:completedBlock]; 28 | } 29 | 30 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url options:(SDWebImageOptions)options completed:(nullable SDExternalCompletionBlock)completedBlock { 31 | [self sd_setHighlightedImageWithURL:url options:options progress:nil completed:completedBlock]; 32 | } 33 | 34 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url 35 | options:(SDWebImageOptions)options 36 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 37 | completed:(nullable SDExternalCompletionBlock)completedBlock { 38 | __weak typeof(self)weakSelf = self; 39 | [self sd_internalSetImageWithURL:url 40 | placeholderImage:nil 41 | options:options 42 | operationKey:@"UIImageViewImageOperationHighlighted" 43 | setImageBlock:^(UIImage *image, NSData *imageData) { 44 | weakSelf.highlightedImage = image; 45 | } 46 | progress:progressBlock 47 | completed:completedBlock]; 48 | } 49 | 50 | @end 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/UIView+WebCache.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "SDWebImageCompat.h" 10 | 11 | #if SD_UIKIT || SD_MAC 12 | 13 | #import "SDWebImageManager.h" 14 | 15 | typedef void(^SDSetImageBlock)(UIImage * _Nullable image, NSData * _Nullable imageData); 16 | 17 | @interface UIView (WebCache) 18 | 19 | /** 20 | * Get the current image URL. 21 | * 22 | * Note that because of the limitations of categories this property can get out of sync 23 | * if you use setImage: directly. 24 | */ 25 | - (nullable NSURL *)sd_imageURL; 26 | 27 | /** 28 | * Set the imageView `image` with an `url` and optionally a placeholder image. 29 | * 30 | * The download is asynchronous and cached. 31 | * 32 | * @param url The url for the image. 33 | * @param placeholder The image to be set initially, until the image request finishes. 34 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. 35 | * @param operationKey A string to be used as the operation key. If nil, will use the class name 36 | * @param setImageBlock Block used for custom set image code 37 | * @param progressBlock A block called while image is downloading 38 | * @note the progress block is executed on a background queue 39 | * @param completedBlock A block called when operation has been completed. This block has no return value 40 | * and takes the requested UIImage as first parameter. In case of error the image parameter 41 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 42 | * indicating if the image was retrieved from the local cache or from the network. 43 | * The fourth parameter is the original image url. 44 | */ 45 | - (void)sd_internalSetImageWithURL:(nullable NSURL *)url 46 | placeholderImage:(nullable UIImage *)placeholder 47 | options:(SDWebImageOptions)options 48 | operationKey:(nullable NSString *)operationKey 49 | setImageBlock:(nullable SDSetImageBlock)setImageBlock 50 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 51 | completed:(nullable SDExternalCompletionBlock)completedBlock; 52 | 53 | /** 54 | * Cancel the current download 55 | */ 56 | - (void)sd_cancelCurrentImageLoad; 57 | 58 | #if SD_UIKIT 59 | 60 | #pragma mark - Activity indicator 61 | 62 | /** 63 | * Show activity UIActivityIndicatorView 64 | */ 65 | - (void)sd_setShowActivityIndicatorView:(BOOL)show; 66 | 67 | /** 68 | * set desired UIActivityIndicatorViewStyle 69 | * 70 | * @param style The style of the UIActivityIndicatorView 71 | */ 72 | - (void)sd_setIndicatorStyle:(UIActivityIndicatorViewStyle)style; 73 | 74 | - (BOOL)sd_showActivityIndicatorView; 75 | - (void)sd_addActivityIndicator; 76 | - (void)sd_removeActivityIndicator; 77 | 78 | #endif 79 | 80 | @end 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/UIView+WebCacheOperation.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "SDWebImageCompat.h" 10 | 11 | #if SD_UIKIT || SD_MAC 12 | 13 | #import "SDWebImageManager.h" 14 | 15 | @interface UIView (WebCacheOperation) 16 | 17 | /** 18 | * Set the image load operation (storage in a UIView based dictionary) 19 | * 20 | * @param operation the operation 21 | * @param key key for storing the operation 22 | */ 23 | - (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key; 24 | 25 | /** 26 | * Cancel all operations for the current UIView and key 27 | * 28 | * @param key key for identifying the operations 29 | */ 30 | - (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key; 31 | 32 | /** 33 | * Just remove the operations corresponding to the current UIView and key without cancelling them 34 | * 35 | * @param key key for identifying the operations 36 | */ 37 | - (void)sd_removeImageLoadOperationWithKey:(nullable NSString *)key; 38 | 39 | @end 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/SDWebImage/UIView+WebCacheOperation.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "UIView+WebCacheOperation.h" 10 | 11 | #if SD_UIKIT || SD_MAC 12 | 13 | #import "objc/runtime.h" 14 | 15 | static char loadOperationKey; 16 | 17 | typedef NSMutableDictionary SDOperationsDictionary; 18 | 19 | @implementation UIView (WebCacheOperation) 20 | 21 | - (SDOperationsDictionary *)operationDictionary { 22 | SDOperationsDictionary *operations = objc_getAssociatedObject(self, &loadOperationKey); 23 | if (operations) { 24 | return operations; 25 | } 26 | operations = [NSMutableDictionary dictionary]; 27 | objc_setAssociatedObject(self, &loadOperationKey, operations, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 28 | return operations; 29 | } 30 | 31 | - (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key { 32 | if (key) { 33 | [self sd_cancelImageLoadOperationWithKey:key]; 34 | if (operation) { 35 | SDOperationsDictionary *operationDictionary = [self operationDictionary]; 36 | operationDictionary[key] = operation; 37 | } 38 | } 39 | } 40 | 41 | - (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key { 42 | // Cancel in progress downloader from queue 43 | SDOperationsDictionary *operationDictionary = [self operationDictionary]; 44 | id operations = operationDictionary[key]; 45 | if (operations) { 46 | if ([operations isKindOfClass:[NSArray class]]) { 47 | for (id operation in operations) { 48 | if (operation) { 49 | [operation cancel]; 50 | } 51 | } 52 | } else if ([operations conformsToProtocol:@protocol(SDWebImageOperation)]){ 53 | [(id) operations cancel]; 54 | } 55 | [operationDictionary removeObjectForKey:key]; 56 | } 57 | } 58 | 59 | - (void)sd_removeImageLoadOperationWithKey:(nullable NSString *)key { 60 | if (key) { 61 | SDOperationsDictionary *operationDictionary = [self operationDictionary]; 62 | [operationDictionary removeObjectForKey:key]; 63 | } 64 | } 65 | 66 | @end 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/VoiceConvert/EMVoiceConverter.h: -------------------------------------------------------------------------------- 1 | // 2 | // VoiceConverter.h 3 | // Jeans 4 | // 5 | // Created by Jeans Huang on 12-7-22. 6 | // Copyright (c) 2012 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface EMVoiceConverter : NSObject 12 | 13 | + (int)isMP3File:(NSString *)filePath; 14 | 15 | + (int)isAMRFile:(NSString *)filePath; 16 | 17 | + (int)amrToWav:(NSString*)_amrPath wavSavePath:(NSString*)_savePath; 18 | 19 | + (int)wavToAmr:(NSString*)_wavPath amrSavePath:(NSString*)_savePath; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/VoiceConvert/EMVoiceConverter.mm: -------------------------------------------------------------------------------- 1 | // 2 | // VoiceConverter.m 3 | // Jeans 4 | // 5 | // Created by Jeans Huang on 12-7-22. 6 | // Copyright (c) 2012 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "EMVoiceConverter.h" 10 | #import "wav.h" 11 | #import "interf_dec.h" 12 | #import "dec_if.h" 13 | #import "interf_enc.h" 14 | #import "amrFileCodec.h" 15 | 16 | @implementation EMVoiceConverter 17 | 18 | 19 | + (int)isMP3File:(NSString *)filePath{ 20 | const char *_filePath = [filePath cStringUsingEncoding:NSASCIIStringEncoding]; 21 | return isMP3File(_filePath); 22 | } 23 | 24 | + (int)isAMRFile:(NSString *)filePath{ 25 | const char *_filePath = [filePath cStringUsingEncoding:NSASCIIStringEncoding]; 26 | return isAMRFile(_filePath); 27 | } 28 | 29 | + (int)amrToWav:(NSString*)_amrPath wavSavePath:(NSString*)_savePath{ 30 | 31 | if (EM_DecodeAMRFileToWAVEFile([_amrPath cStringUsingEncoding:NSASCIIStringEncoding], [_savePath cStringUsingEncoding:NSASCIIStringEncoding])) 32 | return 0; // success 33 | 34 | return 1; // failed 35 | } 36 | 37 | + (int)wavToAmr:(NSString*)_wavPath amrSavePath:(NSString*)_savePath{ 38 | 39 | if (EM_EncodeWAVEFileToAMRFile([_wavPath cStringUsingEncoding:NSASCIIStringEncoding], [_savePath cStringUsingEncoding:NSASCIIStringEncoding], 1, 16)) 40 | return 0; // success 41 | 42 | return 1; // failed 43 | } 44 | 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/VoiceConvert/amrwapper/amrFileCodec.h: -------------------------------------------------------------------------------- 1 | // 2 | // amrFileCodec.h 3 | // amrDemoForiOS 4 | // 5 | // Created by Tang Xiaoping on 9/27/11. 6 | // Copyright 2011 test. All rights reserved. 7 | // 8 | #ifndef amrFileCodec_h 9 | #define amrFileCodec_h 10 | #include 11 | #include 12 | #include 13 | #include "interf_dec.h" 14 | #include "interf_enc.h" 15 | 16 | #define AMR_MAGIC_NUMBER "#!AMR\n" 17 | #define MP3_MAGIC_NUMBER "ID3" 18 | 19 | #define PCM_FRAME_SIZE 160 // 8khz 8000*0.02=160 20 | #define MAX_AMR_FRAME_SIZE 32 21 | #define AMR_FRAME_COUNT_PER_SECOND 50 22 | 23 | typedef struct 24 | { 25 | char chChunkID[4]; 26 | int nChunkSize; 27 | }EM_XCHUNKHEADER; 28 | 29 | typedef struct 30 | { 31 | short nFormatTag; 32 | short nChannels; 33 | int nSamplesPerSec; 34 | int nAvgBytesPerSec; 35 | short nBlockAlign; 36 | short nBitsPerSample; 37 | }EM_WAVEFORMAT; 38 | 39 | typedef struct 40 | { 41 | short nFormatTag; 42 | short nChannels; 43 | int nSamplesPerSec; 44 | int nAvgBytesPerSec; 45 | short nBlockAlign; 46 | short nBitsPerSample; 47 | short nExSize; 48 | }EM_WAVEFORMATX; 49 | 50 | typedef struct 51 | { 52 | char chRiffID[4]; 53 | int nRiffSize; 54 | char chRiffFormat[4]; 55 | }EM_RIFFHEADER; 56 | 57 | typedef struct 58 | { 59 | char chFmtID[4]; 60 | int nFmtSize; 61 | EM_WAVEFORMAT wf; 62 | }EM_FMTBLOCK; 63 | 64 | // WAVE audio processing frequency is 8khz 65 | // audio processing unit = 8000*0.02 = 160 (decided by audio processing frequency) 66 | // audio channels 1 : 160 67 | // 2 : 160*2 = 320 68 | // bps decides the size of processing sample 69 | // bps = 8 --> 8 bits 70 | // 16 --> 16 bit 71 | int EM_EncodeWAVEFileToAMRFile(const char* pchWAVEFilename, const char* pchAMRFileName, int nChannels, int nBitsPerSample); 72 | 73 | int EM_DecodeAMRFileToWAVEFile(const char* pchAMRFileName, const char* pchWAVEFilename); 74 | 75 | int isMP3File(const char *filePath); 76 | 77 | int isAMRFile(const char *filePath); 78 | #endif -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/VoiceConvert/amrwapper/wav.h: -------------------------------------------------------------------------------- 1 | /* ------------------------------------------------------------------ 2 | * Copyright (C) 2009 Martin Storsjo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 13 | * express or implied. 14 | * See the License for the specific language governing permissions 15 | * and limitations under the License. 16 | * ------------------------------------------------------------------- 17 | */ 18 | 19 | #ifndef WAV_H 20 | #define WAV_H 21 | 22 | #include 23 | 24 | class WavWriter { 25 | public: 26 | WavWriter(const char *filename, int sampleRate, int bitsPerSample, int channels); 27 | ~WavWriter(); 28 | 29 | void writeData(const unsigned char* data, int length); 30 | 31 | private: 32 | void writeString(const char *str); 33 | void writeInt32(int value); 34 | void writeInt16(int value); 35 | 36 | void writeHeader(int length); 37 | 38 | FILE *wav; 39 | int dataLength; 40 | 41 | int sampleRate; 42 | int bitsPerSample; 43 | int channels; 44 | }; 45 | 46 | #endif 47 | 48 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/VoiceConvert/amrwapper/wav.mm: -------------------------------------------------------------------------------- 1 | /* ------------------------------------------------------------------ 2 | * Copyright (C) 2009 Martin Storsjo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 13 | * express or implied. 14 | * See the License for the specific language governing permissions 15 | * and limitations under the License. 16 | * ------------------------------------------------------------------- 17 | */ 18 | 19 | #import 20 | #include "wav.h" 21 | 22 | void WavWriter::writeString(const char *str) { 23 | fputc(str[0], wav); 24 | fputc(str[1], wav); 25 | fputc(str[2], wav); 26 | fputc(str[3], wav); 27 | } 28 | 29 | void WavWriter::writeInt32(int value) { 30 | fputc((value >> 0) & 0xff, wav); 31 | fputc((value >> 8) & 0xff, wav); 32 | fputc((value >> 16) & 0xff, wav); 33 | fputc((value >> 24) & 0xff, wav); 34 | } 35 | 36 | void WavWriter::writeInt16(int value) { 37 | fputc((value >> 0) & 0xff, wav); 38 | fputc((value >> 8) & 0xff, wav); 39 | } 40 | 41 | void WavWriter::writeHeader(int length) { 42 | writeString("RIFF"); 43 | writeInt32(4 + 8 + 20 + 8 + length); //将16改为20 44 | writeString("WAVE"); 45 | 46 | writeString("fmt "); 47 | writeInt32(20); 48 | 49 | int bytesPerFrame = bitsPerSample/8*channels; 50 | int bytesPerSec = bytesPerFrame*sampleRate; 51 | writeInt16(1); // Format 52 | writeInt16(channels); // Channels 53 | writeInt32(sampleRate); // Samplerate 54 | writeInt32(bytesPerSec); // Bytes per sec 55 | writeInt16(bytesPerFrame); // Bytes per frame 56 | writeInt16(bitsPerSample); // Bits per sample 57 | 58 | writeInt32(0); //这儿需要字节对齐 nExSize 59 | 60 | writeString("data"); 61 | writeInt32(length); 62 | } 63 | 64 | WavWriter::WavWriter(const char *filename, int sampleRate, int bitsPerSample, int channels) 65 | { 66 | 67 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 68 | NSString *documentPath = [paths objectAtIndex:0]; 69 | NSString *docFilePath = [documentPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%s", filename]]; 70 | //NSLog(@"documentPath=%@", documentPath); 71 | 72 | wav = fopen([docFilePath cStringUsingEncoding:NSASCIIStringEncoding], "wb"); 73 | if (wav == NULL) 74 | return; 75 | dataLength = 0; 76 | this->sampleRate = sampleRate; 77 | this->bitsPerSample = bitsPerSample; 78 | this->channels = channels; 79 | 80 | writeHeader(dataLength); 81 | } 82 | 83 | WavWriter::~WavWriter() { 84 | if (wav == NULL) 85 | return; 86 | fseek(wav, 0, SEEK_SET); 87 | writeHeader(dataLength); 88 | fclose(wav); 89 | } 90 | 91 | void WavWriter::writeData(const unsigned char* data, int length) { 92 | if (wav == NULL) 93 | return; 94 | fwrite(data, length, 1, wav); 95 | dataLength += length; 96 | } 97 | 98 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/VoiceConvert/opencore-amrnb/interf_dec.h: -------------------------------------------------------------------------------- 1 | /* ------------------------------------------------------------------ 2 | * Copyright (C) 2009 Martin Storsjo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 13 | * express or implied. 14 | * See the License for the specific language governing permissions 15 | * and limitations under the License. 16 | * ------------------------------------------------------------------- 17 | */ 18 | 19 | #ifndef OPENCORE_AMRNB_INTERF_DEC_H 20 | #define OPENCORE_AMRNB_INTERF_DEC_H 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | void* Decoder_Interface_init(void); 27 | void Decoder_Interface_exit(void* state); 28 | void Decoder_Interface_Decode(void* state, const unsigned char* in, short* out, int bfi); 29 | 30 | #ifdef __cplusplus 31 | } 32 | #endif 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/VoiceConvert/opencore-amrnb/interf_enc.h: -------------------------------------------------------------------------------- 1 | /* ------------------------------------------------------------------ 2 | * Copyright (C) 2009 Martin Storsjo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 13 | * express or implied. 14 | * See the License for the specific language governing permissions 15 | * and limitations under the License. 16 | * ------------------------------------------------------------------- 17 | */ 18 | 19 | #ifndef OPENCORE_AMRNB_INTERF_ENC_H 20 | #define OPENCORE_AMRNB_INTERF_ENC_H 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | #ifndef AMRNB_WRAPPER_INTERNAL 27 | /* Copied from enc/src/gsmamr_enc.h */ 28 | enum Mode { 29 | MR475 = 0,/* 4.75 kbps */ 30 | MR515, /* 5.15 kbps */ 31 | MR59, /* 5.90 kbps */ 32 | MR67, /* 6.70 kbps */ 33 | MR74, /* 7.40 kbps */ 34 | MR795, /* 7.95 kbps */ 35 | MR102, /* 10.2 kbps */ 36 | MR122, /* 12.2 kbps */ 37 | MRDTX, /* DTX */ 38 | N_MODES /* Not Used */ 39 | }; 40 | #endif 41 | 42 | void* Encoder_Interface_init(int dtx); 43 | void Encoder_Interface_exit(void* state); 44 | int Encoder_Interface_Encode(void* state, enum Mode mode, const short* speech, unsigned char* out, int forceSpeech); 45 | 46 | #ifdef __cplusplus 47 | } 48 | #endif 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/VoiceConvert/opencore-amrnb/libopencore-amrnb.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Classes/Vendor/DeviceHelper/VoiceConvert/opencore-amrnb/libopencore-amrnb.a -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/VoiceConvert/opencore-amrwb/dec_if.h: -------------------------------------------------------------------------------- 1 | /* ------------------------------------------------------------------ 2 | * Copyright (C) 2009 Martin Storsjo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 13 | * express or implied. 14 | * See the License for the specific language governing permissions 15 | * and limitations under the License. 16 | * ------------------------------------------------------------------- 17 | */ 18 | 19 | #ifndef OPENCORE_AMRWB_DEC_IF_H 20 | #define OPENCORE_AMRWB_DEC_IF_H 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | #define _good_frame 0 27 | 28 | void* D_IF_init(void); 29 | void D_IF_decode(void* state, const unsigned char* bits, short* synth, int bfi); 30 | void D_IF_exit(void* state); 31 | 32 | #ifdef __cplusplus 33 | } 34 | #endif 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/VoiceConvert/opencore-amrwb/if_rom.h: -------------------------------------------------------------------------------- 1 | /* ------------------------------------------------------------------ 2 | * Copyright (C) 2009 Martin Storsjo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 13 | * express or implied. 14 | * See the License for the specific language governing permissions 15 | * and limitations under the License. 16 | * ------------------------------------------------------------------- 17 | */ 18 | 19 | #ifndef OPENCORE_AMRWB_IF_ROM_H 20 | #define OPENCORE_AMRWB_IF_ROM_H 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | #include 27 | typedef int16_t Word16; 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/VoiceConvert/opencore-amrwb/libopencore-amrwb.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiapp/TestIMDemo/3f49aa5e6f7ba12cc347bdc92fc1ba6762246839/TestIMDemo/Classes/Vendor/DeviceHelper/VoiceConvert/opencore-amrwb/libopencore-amrwb.a -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/delegates/EMCDDeviceManagerDelegate.h: -------------------------------------------------------------------------------- 1 | /************************************************************ 2 | * * Hyphenate CONFIDENTIAL 3 | * __________________ 4 | * Copyright (C) 2016 Hyphenate Inc. All rights reserved. 5 | * 6 | * NOTICE: All information contained herein is, and remains 7 | * the property of Hyphenate Inc. 8 | * Dissemination of this information or reproduction of this material 9 | * is strictly forbidden unless prior written permission is obtained 10 | * from Hyphenate Inc. 11 | */ 12 | 13 | #import 14 | #import "EMCDDeviceManagerProximitySensorDelegate.h" 15 | 16 | @protocol EMCDDeviceManagerDelegate 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/delegates/EMCDDeviceManagerProximitySensorDelegate.h: -------------------------------------------------------------------------------- 1 | /************************************************************ 2 | * * Hyphenate CONFIDENTIAL 3 | * __________________ 4 | * Copyright (C) 2016 Hyphenate Inc. All rights reserved. 5 | * 6 | * NOTICE: All information contained herein is, and remains 7 | * the property of Hyphenate Inc. 8 | * Dissemination of this information or reproduction of this material 9 | * is strictly forbidden unless prior written permission is obtained 10 | * from Hyphenate Inc. 11 | */ 12 | 13 | #import 14 | 15 | @protocol EMCDDeviceManagerProximitySensorDelegate 16 | 17 | /*! 18 | @method 19 | @brief Posted when the state of the proximity sensor changes. 20 | @param isCloseToUser indicates whether the proximity sensor is close to the user (YES) or not (NO). 21 | @discussion 22 | @result 23 | */ 24 | - (void)proximitySensorChanged:(BOOL)isCloseToUser; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/internal/DemoErrorCode.h: -------------------------------------------------------------------------------- 1 | /************************************************************ 2 | * * Hyphenate CONFIDENTIAL 3 | * __________________ 4 | * Copyright (C) 2016 Hyphenate Inc. All rights reserved. 5 | * 6 | * NOTICE: All information contained herein is, and remains 7 | * the property of Hyphenate Inc. 8 | * Dissemination of this information or reproduction of this material 9 | * is strictly forbidden unless prior written permission is obtained 10 | * from Hyphenate Inc. 11 | */ 12 | 13 | #ifndef ChatDemo_UI2_0_DemoErrorCode_h 14 | #define ChatDemo_UI2_0_DemoErrorCode_h 15 | 16 | #define EMErrorAudioRecordDurationTooShort -100 17 | #define EMErrorFileTypeConvertionFailure -101 18 | #define EMErrorAudioRecordStoping -102 19 | #define EMErrorAudioRecordNotStarted -103 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/internal/EMAudioPlayerUtil.h: -------------------------------------------------------------------------------- 1 | /************************************************************ 2 | * * Hyphenate CONFIDENTIAL 3 | * __________________ 4 | * Copyright (C) 2016 Hyphenate Inc. All rights reserved. 5 | * 6 | * NOTICE: All information contained herein is, and remains 7 | * the property of Hyphenate Inc. 8 | * Dissemination of this information or reproduction of this material 9 | * is strictly forbidden unless prior written permission is obtained 10 | * from Hyphenate Inc. 11 | */ 12 | 13 | #import 14 | 15 | @interface EMAudioPlayerUtil : NSObject 16 | 17 | + (BOOL)isPlaying; 18 | 19 | // Get the path of what is currently being played 20 | + (NSString *)playingFilePath; 21 | 22 | // Play the audio(wav)from the path 23 | + (void)asyncPlayingWithPath:(NSString *)aFilePath 24 | completion:(void(^)(NSError *error))completon; 25 | 26 | + (void)stopCurrentPlaying; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/internal/EMAudioPlayerUtil.m: -------------------------------------------------------------------------------- 1 | /************************************************************ 2 | * * Hyphenate CONFIDENTIAL 3 | * __________________ 4 | * Copyright (C) 2016 Hyphenate Inc. All rights reserved. 5 | * 6 | * NOTICE: All information contained herein is, and remains 7 | * the property of Hyphenate Inc. 8 | * Dissemination of this information or reproduction of this material 9 | * is strictly forbidden unless prior written permission is obtained 10 | * from Hyphenate Inc. 11 | */ 12 | 13 | #import "EMAudioPlayerUtil.h" 14 | #import 15 | 16 | static EMAudioPlayerUtil *audioPlayerUtil = nil; 17 | 18 | @interface EMAudioPlayerUtil () { 19 | AVAudioPlayer *_player; 20 | void (^playFinish)(NSError *error); 21 | } 22 | 23 | @end 24 | 25 | @implementation EMAudioPlayerUtil 26 | 27 | #pragma mark - public 28 | + (BOOL)isPlaying{ 29 | return [[EMAudioPlayerUtil sharedInstance] isPlaying]; 30 | } 31 | 32 | + (NSString *)playingFilePath{ 33 | return [[EMAudioPlayerUtil sharedInstance] playingFilePath]; 34 | } 35 | 36 | + (void)asyncPlayingWithPath:(NSString *)aFilePath 37 | completion:(void(^)(NSError *error))completon{ 38 | [[EMAudioPlayerUtil sharedInstance] asyncPlayingWithPath:aFilePath 39 | completion:completon]; 40 | } 41 | 42 | + (void)stopCurrentPlaying{ 43 | [[EMAudioPlayerUtil sharedInstance] stopCurrentPlaying]; 44 | } 45 | 46 | 47 | #pragma mark - private 48 | + (EMAudioPlayerUtil *)sharedInstance{ 49 | static dispatch_once_t onceToken; 50 | dispatch_once(&onceToken, ^{ 51 | audioPlayerUtil = [[self alloc] init]; 52 | }); 53 | 54 | return audioPlayerUtil; 55 | } 56 | 57 | - (BOOL)isPlaying 58 | { 59 | return !!_player; 60 | } 61 | 62 | // Get the path of what is currently being played 63 | - (NSString *)playingFilePath 64 | { 65 | NSString *path = nil; 66 | if (_player && _player.isPlaying) { 67 | path = _player.url.path; 68 | } 69 | 70 | return path; 71 | } 72 | 73 | - (void)asyncPlayingWithPath:(NSString *)aFilePath 74 | completion:(void(^)(NSError *error))completon{ 75 | playFinish = completon; 76 | NSError *error = nil; 77 | NSFileManager *fm = [NSFileManager defaultManager]; 78 | if (![fm fileExistsAtPath:aFilePath]) { 79 | error = [NSError errorWithDomain:@"File path not exist" 80 | code:-1 81 | userInfo:nil]; 82 | if (playFinish) { 83 | playFinish(error); 84 | } 85 | playFinish = nil; 86 | 87 | return; 88 | } 89 | 90 | NSURL *wavUrl = [[NSURL alloc] initFileURLWithPath:aFilePath]; 91 | _player = [[AVAudioPlayer alloc] initWithContentsOfURL:wavUrl error:&error]; 92 | if (error || !_player) { 93 | _player = nil; 94 | error = [NSError errorWithDomain:@"Failed to initialize AVAudioPlayer" 95 | code:-1 96 | userInfo:nil]; 97 | if (playFinish) { 98 | playFinish(error); 99 | } 100 | playFinish = nil; 101 | return; 102 | } 103 | 104 | _player.delegate = self; 105 | [_player prepareToPlay]; 106 | [_player play]; 107 | } 108 | 109 | - (void)stopCurrentPlaying{ 110 | if(_player){ 111 | _player.delegate = nil; 112 | [_player stop]; 113 | _player = nil; 114 | } 115 | if (playFinish) { 116 | playFinish = nil; 117 | } 118 | } 119 | 120 | - (void)dealloc{ 121 | if (_player) { 122 | _player.delegate = nil; 123 | [_player stop]; 124 | _player = nil; 125 | } 126 | playFinish = nil; 127 | } 128 | 129 | #pragma mark - AVAudioPlayerDelegate 130 | - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player 131 | successfully:(BOOL)flag{ 132 | if (playFinish) { 133 | playFinish(nil); 134 | } 135 | if (_player) { 136 | _player.delegate = nil; 137 | _player = nil; 138 | } 139 | playFinish = nil; 140 | } 141 | 142 | - (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player 143 | error:(NSError *)error{ 144 | if (playFinish) { 145 | NSError *error = [NSError errorWithDomain:@"Play failure" 146 | code:-1 147 | userInfo:nil]; 148 | playFinish(error); 149 | } 150 | if (_player) { 151 | _player.delegate = nil; 152 | _player = nil; 153 | } 154 | } 155 | 156 | @end 157 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/internal/EMAudioRecorderUtil.h: -------------------------------------------------------------------------------- 1 | /************************************************************ 2 | * * Hyphenate CONFIDENTIAL 3 | * __________________ 4 | * Copyright (C) 2016 Hyphenate Inc. All rights reserved. 5 | * 6 | * NOTICE: All information contained herein is, and remains 7 | * the property of Hyphenate Inc. 8 | * Dissemination of this information or reproduction of this material 9 | * is strictly forbidden unless prior written permission is obtained 10 | * from Hyphenate Inc. 11 | */ 12 | 13 | #import 14 | #import 15 | @interface EMAudioRecorderUtil : NSObject 16 | 17 | +(BOOL)isRecording; 18 | 19 | // Start recording 20 | + (void)asyncStartRecordingWithPreparePath:(NSString *)aFilePath 21 | completion:(void(^)(NSError *error))completion; 22 | // Stop recording 23 | +(void)asyncStopRecordingWithCompletion:(void(^)(NSString *recordPath))completion; 24 | 25 | // Cancel recording 26 | +(void)cancelCurrentRecording; 27 | 28 | // Current recorder 29 | +(AVAudioRecorder *)recorder; 30 | @end 31 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/internal/EMCDDeviceManager+Media.h: -------------------------------------------------------------------------------- 1 | /************************************************************ 2 | * * Hyphenate CONFIDENTIAL 3 | * __________________ 4 | * Copyright (C) 2016 Hyphenate Inc. All rights reserved. 5 | * 6 | * NOTICE: All information contained herein is, and remains 7 | * the property of Hyphenate Inc. 8 | * Dissemination of this information or reproduction of this material 9 | * is strictly forbidden unless prior written permission is obtained 10 | * from Hyphenate Inc. 11 | */ 12 | 13 | #import "EMCDDeviceManagerBase.h" 14 | 15 | @interface EMCDDeviceManager (Media) 16 | 17 | #pragma mark - AudioPlayer 18 | // Play the audio 19 | - (void)asyncPlayingWithPath:(NSString *)aFilePath 20 | completion:(void(^)(NSError *error))completon; 21 | // Stop playing 22 | - (void)stopPlaying; 23 | 24 | - (void)stopPlayingWithChangeCategory:(BOOL)isChange; 25 | 26 | -(BOOL)isPlaying; 27 | 28 | #pragma mark - AudioRecorder 29 | // Start recording 30 | - (void)asyncStartRecordingWithFileName:(NSString *)fileName 31 | completion:(void(^)(NSError *error))completion; 32 | 33 | // Stop recording 34 | -(void)asyncStopRecordingWithCompletion:(void(^)(NSString *recordPath, 35 | NSInteger aDuration, 36 | NSError *error))completion; 37 | // Cancel recording 38 | -(void)cancelCurrentRecording; 39 | 40 | -(BOOL)isRecording; 41 | 42 | // Get the saved data path 43 | + (NSString*)dataPath; 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/internal/EMCDDeviceManager+Microphone.h: -------------------------------------------------------------------------------- 1 | /************************************************************ 2 | * * Hyphenate CONFIDENTIAL 3 | * __________________ 4 | * Copyright (C) 2016 Hyphenate Inc. All rights reserved. 5 | * 6 | * NOTICE: All information contained herein is, and remains 7 | * the property of Hyphenate Inc. 8 | * Dissemination of this information or reproduction of this material 9 | * is strictly forbidden unless prior written permission is obtained 10 | * from Hyphenate Inc. 11 | */ 12 | 13 | #import "EMCDDeviceManager.h" 14 | 15 | @interface EMCDDeviceManager (Microphone) 16 | 17 | // Check the availability for microphone 18 | - (BOOL)emCheckMicrophoneAvailability; 19 | 20 | // Get the audio volumn (0~1) 21 | - (double)emPeekRecorderVoiceMeter; 22 | @end 23 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/internal/EMCDDeviceManager+Microphone.m: -------------------------------------------------------------------------------- 1 | /************************************************************ 2 | * * Hyphenate CONFIDENTIAL 3 | * __________________ 4 | * Copyright (C) 2016 Hyphenate Inc. All rights reserved. 5 | * 6 | * NOTICE: All information contained herein is, and remains 7 | * the property of Hyphenate Inc. 8 | * Dissemination of this information or reproduction of this material 9 | * is strictly forbidden unless prior written permission is obtained 10 | * from Hyphenate Inc. 11 | */ 12 | 13 | #import "EMCDDeviceManager+Microphone.h" 14 | #import "EMAudioRecorderUtil.h" 15 | 16 | @implementation EMCDDeviceManager (Microphone) 17 | 18 | // Check the availability for microphone 19 | - (BOOL)emCheckMicrophoneAvailability{ 20 | __block BOOL ret = NO; 21 | AVAudioSession *session = [AVAudioSession sharedInstance]; 22 | if ([session respondsToSelector:@selector(requestRecordPermission:)]) { 23 | [session performSelector:@selector(requestRecordPermission:) withObject:^(BOOL granted) { 24 | ret = granted; 25 | }]; 26 | } else { 27 | ret = YES; 28 | } 29 | 30 | return ret; 31 | } 32 | 33 | // Get the audio volumn (0~1) 34 | - (double)emPeekRecorderVoiceMeter{ 35 | double ret = 0.0; 36 | if ([EMAudioRecorderUtil recorder].isRecording) { 37 | [[EMAudioRecorderUtil recorder] updateMeters]; 38 | //Average volumn [recorder averagePowerForChannel:0]; 39 | //Maximum volumn [recorder peakPowerForChannel:0]; 40 | double lowPassResults = pow(10, (0.05 * [[EMAudioRecorderUtil recorder] peakPowerForChannel:0])); 41 | ret = lowPassResults; 42 | } 43 | 44 | return ret; 45 | } 46 | @end 47 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/internal/EMCDDeviceManager+ProximitySensor.h: -------------------------------------------------------------------------------- 1 | /************************************************************ 2 | * * Hyphenate CONFIDENTIAL 3 | * __________________ 4 | * Copyright (C) 2016 Hyphenate Inc. All rights reserved. 5 | * 6 | * NOTICE: All information contained herein is, and remains 7 | * the property of Hyphenate Inc. 8 | * Dissemination of this information or reproduction of this material 9 | * is strictly forbidden unless prior written permission is obtained 10 | * from Hyphenate Inc. 11 | */ 12 | 13 | #import "EMCDDeviceManagerBase.h" 14 | 15 | @interface EMCDDeviceManager (ProximitySensor) 16 | #pragma mark - proximity sensor 17 | @property (nonatomic, readonly) BOOL isSupportProximitySensor; 18 | @property (nonatomic, readonly) BOOL isCloseToUser; 19 | @property (nonatomic, readonly) BOOL isProximitySensorEnabled; 20 | 21 | - (BOOL)enableProximitySensor; 22 | - (BOOL)disableProximitySensor; 23 | - (void)sensorStateChanged:(NSNotification *)notification; 24 | @end 25 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/internal/EMCDDeviceManager+ProximitySensor.m: -------------------------------------------------------------------------------- 1 | /************************************************************ 2 | * * Hyphenate CONFIDENTIAL 3 | * __________________ 4 | * Copyright (C) 2016 Hyphenate Inc. All rights reserved. 5 | * 6 | * NOTICE: All information contained herein is, and remains 7 | * the property of Hyphenate Inc. 8 | * Dissemination of this information or reproduction of this material 9 | * is strictly forbidden unless prior written permission is obtained 10 | * from Hyphenate Inc. 11 | */ 12 | 13 | #import 14 | #import "EMCDDeviceManager+ProximitySensor.h" 15 | 16 | @implementation EMCDDeviceManager (ProximitySensor) 17 | @dynamic isSupportProximitySensor; 18 | @dynamic isCloseToUser; 19 | 20 | 21 | #pragma mark - proximity sensor 22 | - (BOOL)isProximitySensorEnabled { 23 | BOOL ret = NO; 24 | ret = self.isSupportProximitySensor && [UIDevice currentDevice].proximityMonitoringEnabled; 25 | 26 | return ret; 27 | } 28 | 29 | - (BOOL)enableProximitySensor { 30 | BOOL ret = NO; 31 | if (_isSupportProximitySensor) { 32 | [[UIDevice currentDevice] setProximityMonitoringEnabled:YES]; 33 | ret = YES; 34 | } 35 | 36 | return ret; 37 | } 38 | 39 | - (BOOL)disableProximitySensor { 40 | BOOL ret = NO; 41 | if (_isSupportProximitySensor) { 42 | [[UIDevice currentDevice] setProximityMonitoringEnabled:NO]; 43 | _isCloseToUser = NO; 44 | ret = YES; 45 | } 46 | 47 | return ret; 48 | } 49 | 50 | - (void)sensorStateChanged:(NSNotification *)notification { 51 | BOOL ret = NO; 52 | if ([[UIDevice currentDevice] proximityState] == YES) { 53 | ret = YES; 54 | } 55 | _isCloseToUser = ret; 56 | if([self.delegate respondsToSelector:@selector(proximitySensorChanged:)]){ 57 | [self.delegate proximitySensorChanged:_isCloseToUser]; 58 | } 59 | } 60 | 61 | #pragma mark - getter 62 | - (BOOL)isCloseToUser { 63 | return _isCloseToUser; 64 | } 65 | 66 | - (BOOL)isSupportProximitySensor { 67 | return _isSupportProximitySensor; 68 | } 69 | @end 70 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/internal/EMCDDeviceManager+Remind.h: -------------------------------------------------------------------------------- 1 | /************************************************************ 2 | * * Hyphenate CONFIDENTIAL 3 | * __________________ 4 | * Copyright (C) 2016 Hyphenate Inc. All rights reserved. 5 | * 6 | * NOTICE: All information contained herein is, and remains 7 | * the property of Hyphenate Inc. 8 | * Dissemination of this information or reproduction of this material 9 | * is strictly forbidden unless prior written permission is obtained 10 | * from Hyphenate Inc. 11 | */ 12 | 13 | #import "EMCDDeviceManager.h" 14 | #import 15 | @interface EMCDDeviceManager (Remind) 16 | 17 | // The system sound for a new message 18 | - (SystemSoundID)playNewMessageSound; 19 | 20 | - (void)playVibration; 21 | @end 22 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/internal/EMCDDeviceManager+Remind.m: -------------------------------------------------------------------------------- 1 | /************************************************************ 2 | * * Hyphenate CONFIDENTIAL 3 | * __________________ 4 | * Copyright (C) 2016 Hyphenate Inc. All rights reserved. 5 | * 6 | * NOTICE: All information contained herein is, and remains 7 | * the property of Hyphenate Inc. 8 | * Dissemination of this information or reproduction of this material 9 | * is strictly forbidden unless prior written permission is obtained 10 | * from Hyphenate Inc. 11 | */ 12 | 13 | #import "EMCDDeviceManager+Remind.h" 14 | 15 | void EMSystemSoundFinishedPlayingCallback(SystemSoundID sound_id, void* user_data) 16 | { 17 | AudioServicesDisposeSystemSoundID(sound_id); 18 | } 19 | 20 | @implementation EMCDDeviceManager (Remind) 21 | 22 | // The system sound for a new message 23 | - (SystemSoundID)playNewMessageSound 24 | { 25 | // Path for the audio file 26 | NSURL *audioPath = [[NSBundle mainBundle] URLForResource:@"in" withExtension:@"caf"]; 27 | 28 | SystemSoundID soundID; 29 | AudioServicesCreateSystemSoundID((__bridge CFURLRef)(audioPath), &soundID); 30 | // Register the sound completion callback. 31 | AudioServicesAddSystemSoundCompletion(soundID, 32 | NULL, // uses the main run loop 33 | NULL, // uses kCFRunLoopDefaultMode 34 | EMSystemSoundFinishedPlayingCallback, // the name of our custom callback function 35 | NULL // for user data, but we don't need to do that in this case, so we just pass NULL 36 | ); 37 | 38 | AudioServicesPlaySystemSound(soundID); 39 | 40 | return soundID; 41 | } 42 | 43 | - (void)playVibration 44 | { 45 | // Register the sound completion callback. 46 | AudioServicesAddSystemSoundCompletion(kSystemSoundID_Vibrate, 47 | NULL, // uses the main run loop 48 | NULL, // uses kCFRunLoopDefaultMode 49 | EMSystemSoundFinishedPlayingCallback, // the name of our custom callback function 50 | NULL // for user data, but we don't need to do that in this case, so we just pass NULL 51 | ); 52 | 53 | AudioServicesPlaySystemSound(kSystemSoundID_Vibrate); 54 | } 55 | @end 56 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/internal/EMCDDeviceManager.h: -------------------------------------------------------------------------------- 1 | /************************************************************ 2 | * * Hyphenate CONFIDENTIAL 3 | * __________________ 4 | * Copyright (C) 2016 Hyphenate Inc. All rights reserved. 5 | * 6 | * NOTICE: All information contained herein is, and remains 7 | * the property of Hyphenate Inc. 8 | * Dissemination of this information or reproduction of this material 9 | * is strictly forbidden unless prior written permission is obtained 10 | * from Hyphenate Inc. 11 | */ 12 | 13 | #ifndef ChatDemo_UI2_0_EMCDDeviceManager_h 14 | #define ChatDemo_UI2_0_EMCDDeviceManager_h 15 | 16 | #import "EMCDDeviceManager+Media.h" 17 | #import "EMCDDeviceManager+Remind.h" 18 | #import "EMCDDeviceManager+Microphone.h" 19 | #import "EMCDDeviceManager+ProximitySensor.h" 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/internal/EMCDDeviceManagerBase.h: -------------------------------------------------------------------------------- 1 | /************************************************************ 2 | * * Hyphenate CONFIDENTIAL 3 | * __________________ 4 | * Copyright (C) 2016 Hyphenate Inc. All rights reserved. 5 | * 6 | * NOTICE: All information contained herein is, and remains 7 | * the property of Hyphenate Inc. 8 | * Dissemination of this information or reproduction of this material 9 | * is strictly forbidden unless prior written permission is obtained 10 | * from Hyphenate Inc. 11 | */ 12 | 13 | #import 14 | #import "EMCDDeviceManagerDelegate.h" 15 | 16 | @interface EMCDDeviceManager : NSObject{ 17 | // recorder 18 | NSDate *_recorderStartDate; 19 | NSDate *_recorderEndDate; 20 | NSString *_currCategory; 21 | BOOL _currActive; 22 | 23 | // proximitySensor 24 | BOOL _isSupportProximitySensor; 25 | BOOL _isCloseToUser; 26 | } 27 | 28 | @property (nonatomic, assign) id delegate; 29 | 30 | +(EMCDDeviceManager *)sharedInstance; 31 | 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /TestIMDemo/Classes/Vendor/DeviceHelper/internal/EMCDDeviceManagerBase.m: -------------------------------------------------------------------------------- 1 | /************************************************************ 2 | * * Hyphenate CONFIDENTIAL 3 | * __________________ 4 | * Copyright (C) 2016 Hyphenate Inc. All rights reserved. 5 | * 6 | * NOTICE: All information contained herein is, and remains 7 | * the property of Hyphenate Inc. 8 | * Dissemination of this information or reproduction of this material 9 | * is strictly forbidden unless prior written permission is obtained 10 | * from Hyphenate Inc. 11 | */ 12 | 13 | #import 14 | #import "EMCDDeviceManagerBase.h" 15 | #import "EMCDDeviceManager+ProximitySensor.h" 16 | 17 | static EMCDDeviceManager *emCDDeviceManager; 18 | @interface EMCDDeviceManager (){ 19 | 20 | } 21 | 22 | @end 23 | 24 | @implementation EMCDDeviceManager 25 | +(EMCDDeviceManager *)sharedInstance{ 26 | static dispatch_once_t onceToken; 27 | dispatch_once(&onceToken, ^{ 28 | emCDDeviceManager = [[EMCDDeviceManager alloc] init]; 29 | }); 30 | 31 | return emCDDeviceManager; 32 | } 33 | 34 | -(instancetype)init{ 35 | if (self = [super init]) { 36 | [self _setupProximitySensor]; 37 | [self registerNotifications]; 38 | } 39 | return self; 40 | } 41 | 42 | - (void)registerNotifications 43 | { 44 | [self unregisterNotifications]; 45 | if (_isSupportProximitySensor) { 46 | static NSString *notif = @"UIDeviceProximityStateDidChangeNotification"; 47 | [[NSNotificationCenter defaultCenter] addObserver:self 48 | selector:@selector(sensorStateChanged:) 49 | name:notif 50 | object:nil]; 51 | } 52 | } 53 | 54 | - (void)unregisterNotifications { 55 | if (_isSupportProximitySensor) { 56 | static NSString *notif = @"UIDeviceProximityStateDidChangeNotification"; 57 | [[NSNotificationCenter defaultCenter] removeObserver:self 58 | name:notif 59 | object:nil]; 60 | } 61 | } 62 | 63 | - (void)_setupProximitySensor 64 | { 65 | UIDevice *device = [UIDevice currentDevice]; 66 | [device setProximityMonitoringEnabled:YES]; 67 | _isSupportProximitySensor = device.proximityMonitoringEnabled; 68 | if (_isSupportProximitySensor) { 69 | [device setProximityMonitoringEnabled:NO]; 70 | } else { 71 | 72 | } 73 | } 74 | 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /TestIMDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | 测试环信IM 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSAllowsArbitraryLoads 28 | 29 | 30 | UILaunchStoryboardName 31 | LaunchScreen 32 | UIMainStoryboardFile 33 | Login 34 | UIRequiredDeviceCapabilities 35 | 36 | armv7 37 | 38 | UISupportedInterfaceOrientations 39 | 40 | UIInterfaceOrientationPortrait 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | NSPhotoLibraryUsageDescription 45 | 该应用要访问你的相册 46 | 47 | 48 | -------------------------------------------------------------------------------- /TestIMDemo/PrefixHeader.pch: -------------------------------------------------------------------------------- 1 | // 2 | // PrefixHeader.pch 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/20. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #ifndef PrefixHeader_pch 10 | #define PrefixHeader_pch 11 | 12 | #define EaseMobAppKey @"1179170720178073#testimdemo" 13 | 14 | // 导入环信头文件 15 | #import 16 | 17 | #import "UIViewController+Alert.h" 18 | 19 | #endif /* PrefixHeader_pch */ 20 | -------------------------------------------------------------------------------- /TestIMDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // TestIMDemo 4 | // 5 | // Created by 任波 on 2017/7/20. 6 | // Copyright © 2017年 renb. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | --------------------------------------------------------------------------------