diff --git a/.travis.yml b/.travis.yml index baec7eb..c5bb0c0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,15 +5,19 @@ cache: cocoapods env: global: - LANG=en_US.UTF-8 - - WORKSPACE="OTAcceleratorCore.xcworkspace" - - IOS_SCHEME="OTAcceleratorCore" - IOS_SDK=iphonesimulator13.6 matrix: + - DESTINATION="OS=12.4,name=iPhone 6s" SDK="$IOS_SDK" SCHEME="OTAcceleratorCore" RUN_TESTS="YES" BUILD_EXAMPLE="NO" POD_LINT="YES" RUN_UI_TESTS="YES" + - DESTINATION="OS=13.6,name=iPhone 11 Pro" SDK="$IOS_SDK" SCHEME="OTAcceleratorCore" RUN_TESTS="YES" BUILD_EXAMPLE="NO" POD_LINT="YES" RUN_UI_TESTS="YES" + + - DESTINATION="OS=12.4,name=iPhone 6s" SDK="$IOS_SDK" SCHEME="OTAnnotationSample" RUN_TESTS="YES" BUILD_EXAMPLE="NO" POD_LINT="YES" RUN_UI_TESTS="YES" + - DESTINATION="OS=13.6,name=iPhone 11 Pro" SDK="$IOS_SDK" SCHEME="OTAnnotationSample" RUN_TESTS="YES" BUILD_EXAMPLE="NO" POD_LINT="YES" RUN_UI_TESTS="YES" + + - DESTINATION="OS=12.4,name=iPhone 6s" SDK="$IOS_SDK" SCHEME="OTTextChatSample" RUN_TESTS="YES" BUILD_EXAMPLE="NO" POD_LINT="YES" RUN_UI_TESTS="NO" + - DESTINATION="OS=13.6,name=iPhone 11 Pro" SDK="$IOS_SDK" SCHEME="OTTextChatSample" RUN_TESTS="YES" BUILD_EXAMPLE="YES" POD_LINT="YES" RUN_UI_TESTS="NO" - - DESTINATION="OS=12.4,name=iPhone 6s" SDK="$IOS_SDK" SCHEME="$IOS_SCHEME" RUN_TESTS="YES" BUILD_EXAMPLE="NO" POD_LINT="YES" RUN_UI_TESTS="YES" - - DESTINATION="OS=13.6,name=iPhone 11 Pro" SDK="$IOS_SDK" SCHEME="$IOS_SCHEME" RUN_TESTS="YES" BUILD_EXAMPLE="NO" POD_LINT="YES" RUN_UI_TESTS="YES" before_install: - gem install cocoapods --pre - pod update @@ -30,6 +34,7 @@ script: else travis_retry xcodebuild clean build -workspace "$WORKSPACE" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug | xcpretty -c; fi + notifications: slack: rooms: diff --git a/OTAcceleratorCore.podspec b/OTAcceleratorCore.podspec index 0d4bb46..11209bc 100644 --- a/OTAcceleratorCore.podspec +++ b/OTAcceleratorCore.podspec @@ -1,12 +1,11 @@ Pod::Spec.new do |s| s.name = "OTAcceleratorCore" - s.version = "2.0.1" - s.summary = "A painless way to integrate WebRTC audio/video(screen sharing) to any iOS applications via OpenTok." + s.version = "3.0.0" + s.summary = "A painless way to integrate WebRTC audio/video(camera + screen sharing)/text to any iOS applications via OpenTok." - s.description = "The OpenTok Accelerator Core is required whenever you use any of the OpenTok accelerators. The OpenTok Accelerator Core is a common layer that permits all accelerators and samples to share the same OpenTok session. The accelerator packs and sample app access the OpenTok session through the Accelerator Core Pack layer, which allows them to share a single OpenTok session. - -On the Android and iOS mobile platforms, when you try to set a listener (Android) or delegate (iOS), it is not normally possible to set multiple listeners or delegates for the same event. For example, on these mobile platforms you can only set one OpenTok signal listener. The Common Accelerator Session Pack, however, allows you to set up several listeners for the same event." + s.description = "The OpenTok Accelerator Core is a collection of reusable components built on top of the OpenTok iOS SDK using best practices. The core components help you to use OpenTok to add one to one or multiparty audio and video calls to your application with screen sharing. The text chat components help add text messaging to your application, and the annotation components will help with sharing screen annotations. + For examples of the components in action clone the repository and try out the sample apps." s.homepage = "https://github.com/opentok/accelerator-core-ios" s.license = { :type => 'MIT', :file => 'LICENSE' } @@ -15,17 +14,21 @@ On the Android and iOS mobile platforms, when you try to set a listener (Android s.social_media_url = 'https://twitter.com/vonagedev' s.platform = :ios, '11.0' + s.requires_arc = true + s.static_framework = true s.source_files = 'OTAcceleratorCore/**/*.{h,m}' + s.public_header_files = 'OTAcceleratorCore/**/*.{h}' s.resource_bundles = { - 'OTAcceleratorCoreBundle' => ['OTAcceleratorCoreBundle/**/*.xib', 'OTAcceleratorCoreBundle/Assets.xcassets'] + 'OTAcceleratorCoreBundle' => [ + 'OTAcceleratorCoreBundle/**/*.xib', 'OTAcceleratorCoreBundle/Assets.xcassets', + 'OTTextChatSampleBundle/**/*.xib', 'OTTextChatSampleBundle/Assets.xcassets', + 'OTAnnotationSampleBundle/**/*.xib', 'OTAnnotationSampleBundle/Assets.xcassets'] } - - s.static_framework = true - s.public_header_files = 'OTAcceleratorCore/**/*.{h}' - s.dependency 'OTKAnalytics', '= 2.1.2' - s.dependency 'OpenTok', '2.18.0' - s.dependency 'SVProgressHUD', '= 2.2.5' + + s.dependency 'OTKAnalytics', '2.1.2' + s.dependency 'OpenTok', '2.18.1' + s.dependency 'SVProgressHUD', '2.2.5' end diff --git a/OTAcceleratorCore.xcodeproj/project.pbxproj b/OTAcceleratorCore.xcodeproj/project.pbxproj index ec02ed0..0d8eafd 100644 --- a/OTAcceleratorCore.xcodeproj/project.pbxproj +++ b/OTAcceleratorCore.xcodeproj/project.pbxproj @@ -8,7 +8,96 @@ /* Begin PBXBuildFile section */ 120640222525FEBC008E3516 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 120640212525FEBC008E3516 /* WebKit.framework */; }; - 70E24F0467232AEC1D62197A /* libPods-OTAcceleratorCore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F8BF33AD6157E1E1E3747A54 /* libPods-OTAcceleratorCore.a */; }; + 127C5D62255177A7000549D5 /* OTAcceleratorCoreBundle.m in Sources */ = {isa = PBXBuildFile; fileRef = A0565F241DF63F62001F9032 /* OTAcceleratorCoreBundle.m */; }; + 127C5D63255177A8000549D5 /* OTAcceleratorCoreBundle.m in Sources */ = {isa = PBXBuildFile; fileRef = A0565F241DF63F62001F9032 /* OTAcceleratorCoreBundle.m */; }; + 127C5D64255177C7000549D5 /* OTOneToOneCommunicator.m in Sources */ = {isa = PBXBuildFile; fileRef = A0565F2A1DF63F62001F9032 /* OTOneToOneCommunicator.m */; }; + 127C5D65255177DD000549D5 /* OTVideoView.m in Sources */ = {isa = PBXBuildFile; fileRef = A05259321E9435DA000F9E88 /* OTVideoView.m */; }; + 127C5D66255177E6000549D5 /* OTScreenCapture.m in Sources */ = {isa = PBXBuildFile; fileRef = A0AB9F0F1E3AC2D50068E20F /* OTScreenCapture.m */; }; + 127C5D67255177F9000549D5 /* OTAudioVideoControlView.m in Sources */ = {isa = PBXBuildFile; fileRef = A05259381E9435EA000F9E88 /* OTAudioVideoControlView.m */; }; + 127C5D6825517873000549D5 /* OTAcceleratorSession.m in Sources */ = {isa = PBXBuildFile; fileRef = A0565F261DF63F62001F9032 /* OTAcceleratorSession.m */; }; + 127C5D6925517B03000549D5 /* UIView+Helper.m in Sources */ = {isa = PBXBuildFile; fileRef = A05259341E9435DA000F9E88 /* UIView+Helper.m */; }; + 127C5D6A25517EB5000549D5 /* OTAcceleratorSession.m in Sources */ = {isa = PBXBuildFile; fileRef = A0565F261DF63F62001F9032 /* OTAcceleratorSession.m */; }; + 129EDD4F253F2C1400541893 /* CustomTimestampTextChatTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 129EDD39253F2C1200541893 /* CustomTimestampTextChatTableViewCell.m */; }; + 129EDD50253F2C1400541893 /* CustomTimestampTextChatTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 129EDD3A253F2C1200541893 /* CustomTimestampTextChatTableViewCell.xib */; }; + 129EDD51253F2C1400541893 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 129EDD3B253F2C1200541893 /* LaunchScreen.storyboard */; }; + 129EDD52253F2C1400541893 /* CustomReceiveTextChatTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 129EDD3C253F2C1200541893 /* CustomReceiveTextChatTableViewCell.m */; }; + 129EDD53253F2C1400541893 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 129EDD3D253F2C1200541893 /* Main.storyboard */; }; + 129EDD54253F2C1400541893 /* CustomSendTextChatTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 129EDD3F253F2C1200541893 /* CustomSendTextChatTableViewCell.m */; }; + 129EDD55253F2C1400541893 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 129EDD40253F2C1200541893 /* AppDelegate.m */; }; + 129EDD56253F2C1400541893 /* DefaultTextChatTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 129EDD42253F2C1300541893 /* DefaultTextChatTableViewController.m */; }; + 129EDD57253F2C1400541893 /* CustomDateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 129EDD43253F2C1300541893 /* CustomDateFormatter.m */; }; + 129EDD58253F2C1400541893 /* CustomReceiveTextChatTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 129EDD45253F2C1300541893 /* CustomReceiveTextChatTableViewCell.xib */; }; + 129EDD59253F2C1400541893 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 129EDD46253F2C1300541893 /* main.m */; }; + 129EDD5A253F2C1400541893 /* CustomSendTextChatTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 129EDD48253F2C1300541893 /* CustomSendTextChatTableViewCell.xib */; }; + 129EDD5C253F2C1400541893 /* CustomTextChatTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 129EDD4A253F2C1300541893 /* CustomTextChatTableViewController.m */; }; + 129EDD5D253F2C1400541893 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 129EDD4B253F2C1400541893 /* Assets.xcassets */; }; + 12B6B93E253F2ED000293718 /* OTTextChat.m in Sources */ = {isa = PBXBuildFile; fileRef = 129EDD00253F0E0600541893 /* OTTextChat.m */; }; + 12B6B93F253F2ED300293718 /* OTTextMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 129EDCFE253F0E0600541893 /* OTTextMessage.m */; }; + 12B6B940253F2EE100293718 /* OTTextChatViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 129EDD16253F0F3400541893 /* OTTextChatViewController.m */; }; + 12B6B941253F2EF100293718 /* OTTextChatTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 129EDD13253F0F3300541893 /* OTTextChatTableView.m */; }; + 12B6B942253F2F0900293718 /* OTTextChatInputView.m in Sources */ = {isa = PBXBuildFile; fileRef = 129EDD12253F0F3300541893 /* OTTextChatInputView.m */; }; + 12B6B943253F2F0F00293718 /* OTTextChatNavigationBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 129EDD14253F0F3300541893 /* OTTextChatNavigationBar.m */; }; + 12B6B944253F2F1A00293718 /* OTTextChatAcceleratorBundle.m in Sources */ = {isa = PBXBuildFile; fileRef = 129EDD07253F0EEC00541893 /* OTTextChatAcceleratorBundle.m */; }; + 12B6B945253F2F2000293718 /* OTTextChatTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 129EDD0C253F0F1600541893 /* OTTextChatTableViewCell.m */; }; + 12B6B97F25407A5C00293718 /* OTTextChatViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 12B6B97725407A5C00293718 /* OTTextChatViewController.xib */; }; + 12B6B98025407A5C00293718 /* TextChatReceivedShortTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 12B6B97825407A5C00293718 /* TextChatReceivedShortTableViewCell.xib */; }; + 12B6B98125407A5C00293718 /* TextChatComponentDivTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 12B6B97925407A5C00293718 /* TextChatComponentDivTableViewCell.xib */; }; + 12B6B98225407A5C00293718 /* TextChatSentTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 12B6B97A25407A5C00293718 /* TextChatSentTableViewCell.xib */; }; + 12B6B98325407A5C00293718 /* TextChatSentShortTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 12B6B97B25407A5C00293718 /* TextChatSentShortTableViewCell.xib */; }; + 12B6B98425407A5C00293718 /* TextChatReceivedTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 12B6B97C25407A5C00293718 /* TextChatReceivedTableViewCell.xib */; }; + 12B6B98525407A5C00293718 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 12B6B97D25407A5C00293718 /* Assets.xcassets */; }; + 12B6B98825407B6300293718 /* OTTextChatSampleBundle.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 12B6B98725407B6300293718 /* OTTextChatSampleBundle.bundle */; }; + 12B6B99C25407C3A00293718 /* OTTextChatTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6B99725407C3A00293718 /* OTTextChatTests.m */; }; + 12B6B99D25407C3A00293718 /* OTTextChatUserInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6B99825407C3A00293718 /* OTTextChatUserInterface.m */; }; + 12B6B99F25407C3A00293718 /* OTTextMessageTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6B99A25407C3A00293718 /* OTTextMessageTests.m */; }; + 12B6B9A025407C3B00293718 /* OTTextChatAcceleratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6B99B25407C3A00293718 /* OTTextChatAcceleratorTests.m */; }; + 12B6BA202540846600293718 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 12B6BA122540846500293718 /* LaunchScreen.storyboard */; }; + 12B6BA212540846600293718 /* AnnotationOnScreenViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BA132540846500293718 /* AnnotationOnScreenViewController.swift */; }; + 12B6BA222540846600293718 /* mvc.png in Resources */ = {isa = PBXBuildFile; fileRef = 12B6BA142540846500293718 /* mvc.png */; }; + 12B6BA232540846600293718 /* ReceiveAnnotationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BA152540846500293718 /* ReceiveAnnotationViewController.m */; }; + 12B6BA252540846600293718 /* SendAnnotationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BA172540846500293718 /* SendAnnotationViewController.swift */; }; + 12B6BA262540846600293718 /* AnnotationBlankCanvasViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BA192540846500293718 /* AnnotationBlankCanvasViewController.swift */; }; + 12B6BA272540846600293718 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BA1B2540846500293718 /* AppDelegate.m */; }; + 12B6BA282540846600293718 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 12B6BA1C2540846600293718 /* Assets.xcassets */; }; + 12B6BA292540846600293718 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 12B6BA1D2540846600293718 /* Main.storyboard */; }; + 12B6BA2A2540846600293718 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BA1E2540846600293718 /* main.m */; }; + 12B6BA842540927C00293718 /* AnnLoggingWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BA822540927C00293718 /* AnnLoggingWrapper.m */; }; + 12B6BA92254092B500293718 /* OTAnnotationToolbarView+Animation.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BA89254092B500293718 /* OTAnnotationToolbarView+Animation.m */; }; + 12B6BA93254092B500293718 /* JSON.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BA8B254092B500293718 /* JSON.m */; }; + 12B6BA94254092B500293718 /* UIColor+HexString.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BA8E254092B500293718 /* UIColor+HexString.m */; }; + 12B6BA95254092B500293718 /* UIButton+AutoLayoutHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BA8F254092B500293718 /* UIButton+AutoLayoutHelper.m */; }; + 12B6BA96254092B500293718 /* UIViewController+Helper.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BA90254092B500293718 /* UIViewController+Helper.m */; }; + 12B6BA9A254092C600293718 /* OTAnnotationKitBundle.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BA99254092C600293718 /* OTAnnotationKitBundle.m */; }; + 12B6BAA82540930C00293718 /* OTAnnotationTextView+Gesture.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BAA12540930C00293718 /* OTAnnotationTextView+Gesture.m */; }; + 12B6BAA92540930C00293718 /* OTAnnotationTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BAA22540930C00293718 /* OTAnnotationTextView.m */; }; + 12B6BAAA2540930C00293718 /* OTAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BAA42540930C00293718 /* OTAnnotationView.m */; }; + 12B6BAAB2540930C00293718 /* OTAnnotationDataManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BAA62540930C00293718 /* OTAnnotationDataManager.m */; }; + 12B6BAAC2540930C00293718 /* OTAnnotationPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BAA72540930C00293718 /* OTAnnotationPath.m */; }; + 12B6BAB42540933B00293718 /* LHToolbar.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BAAE2540933B00293718 /* LHToolbar.m */; }; + 12B6BAB52540933B00293718 /* LHToolbarContainerViewItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BAB12540933B00293718 /* LHToolbarContainerViewItem.m */; }; + 12B6BAB62540933B00293718 /* LHToolbarContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BAB32540933B00293718 /* LHToolbarContainerView.m */; }; + 12B6BABB2540936D00293718 /* OTAnnotationColorPickerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BABA2540936D00293718 /* OTAnnotationColorPickerView.m */; }; + 12B6BAC12540938600293718 /* OTAnnotationScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BABC2540938500293718 /* OTAnnotationScrollView.m */; }; + 12B6BAC22540938600293718 /* OTFullScreenAnnotationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BABE2540938600293718 /* OTFullScreenAnnotationViewController.m */; }; + 12B6BAC8254093D600293718 /* OTAnnotationScreenCaptureViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BAC5254093D600293718 /* OTAnnotationScreenCaptureViewController.m */; }; + 12B6BAC9254093D600293718 /* OTAnnotationScreenCaptureView.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BAC7254093D600293718 /* OTAnnotationScreenCaptureView.m */; }; + 12B6BACD2540940600293718 /* OTAnnotationEditTextViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BACC2540940600293718 /* OTAnnotationEditTextViewController.m */; }; + 12B6BAD32540943700293718 /* OTAnnotationToolbarView.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BAD12540943700293718 /* OTAnnotationToolbarView.m */; }; + 12B6BAD62540945500293718 /* OTAnnotator.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BAD52540945500293718 /* OTAnnotator.m */; }; + 12B6BAE52540968800293718 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 12B6BAE12540968700293718 /* Assets.xcassets */; }; + 12B6BAE62540968800293718 /* OTAnnotationEditTextViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 12B6BAE22540968700293718 /* OTAnnotationEditTextViewController.xib */; }; + 12B6BAE72540968800293718 /* OTAnnotationScreenCaptureViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 12B6BAE32540968800293718 /* OTAnnotationScreenCaptureViewController.xib */; }; + 12B6BAEA2540972A00293718 /* OTAnnotationSampleBundle.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 12B6BAE92540972900293718 /* OTAnnotationSampleBundle.bundle */; }; + 12B6BB0425409B4500293718 /* OTFullScreenAnnotationViewControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BAF925409B4400293718 /* OTFullScreenAnnotationViewControllerTests.m */; }; + 12B6BB0525409B4500293718 /* OTAnnotationKitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BAFA25409B4400293718 /* OTAnnotationKitTests.m */; }; + 12B6BB0625409B4500293718 /* OTAnnotationColorPickerViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BAFB25409B4400293718 /* OTAnnotationColorPickerViewTests.m */; }; + 12B6BB0725409B4500293718 /* OTAnnotationPathTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BAFC25409B4400293718 /* OTAnnotationPathTests.m */; }; + 12B6BB0825409B4500293718 /* OTAnnotationViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BAFD25409B4400293718 /* OTAnnotationViewTests.m */; }; + 12B6BB0925409B4500293718 /* OTAnnotationDataManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BAFE25409B4400293718 /* OTAnnotationDataManagerTests.m */; }; + 12B6BB0B25409B4500293718 /* OTAnnotationEditTextViewControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BB0025409B4400293718 /* OTAnnotationEditTextViewControllerTests.m */; }; + 12B6BB0C25409B4500293718 /* OTAnnotationScreenCaptureViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BB0125409B4500293718 /* OTAnnotationScreenCaptureViewTests.m */; }; + 12B6BB0D25409B4500293718 /* OTAnnotationTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BB0225409B4500293718 /* OTAnnotationTextView.m */; }; + 12B6BB0E25409B4500293718 /* OTAnnotationScrollViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 12B6BB0325409B4500293718 /* OTAnnotationScrollViewTests.m */; }; 96D1A01A22B85DDF00744ABC /* OTVideoViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A0AB9FF31E3ED2060068E20F /* OTVideoViewTests.m */; }; 96DE392C22B8979400A41369 /* OTVideoView.m in Sources */ = {isa = PBXBuildFile; fileRef = A05259321E9435DA000F9E88 /* OTVideoView.m */; }; A0127A901D34651B00CDA3F5 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A0127A8F1D34651B00CDA3F5 /* main.m */; }; @@ -44,10 +133,23 @@ A0AB9FF21E3ED08B0068E20F /* OTViewHelperTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A0AB9FF11E3ED08B0068E20F /* OTViewHelperTests.m */; }; A0C142BD1E95992B000120E4 /* OTAudioVideoControlViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A0C142BC1E95992B000120E4 /* OTAudioVideoControlViewTests.m */; }; A0FEAC001D73B3BF000BDDA4 /* OTAcceleratorCore.podspec in Resources */ = {isa = PBXBuildFile; fileRef = A0FEABFD1D73B3BF000BDDA4 /* OTAcceleratorCore.podspec */; }; - EE9663C7C3580221DE55E457 /* libPods-OTAcceleratorCoreTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2683413A28D94048B6498E35 /* libPods-OTAcceleratorCoreTests.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 12B6B99225407C1400293718 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A0127A831D34651B00CDA3F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 129EDD1F253F2BBD00541893; + remoteInfo = OTTextChatSample; + }; + 12B6BAF425409B2C00293718 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A0127A831D34651B00CDA3F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 12B6B9F82540843200293718; + remoteInfo = OTAnnotationSample; + }; A05CC9F81E984AD700A1459F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = A0127A831D34651B00CDA3F5 /* Project object */; @@ -79,10 +181,150 @@ /* Begin PBXFileReference section */ 120640212525FEBC008E3516 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; - 13482FF31B2028E05711C5FB /* Pods-OTAcceleratorCore.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OTAcceleratorCore.debug.xcconfig"; path = "Target Support Files/Pods-OTAcceleratorCore/Pods-OTAcceleratorCore.debug.xcconfig"; sourceTree = ""; }; - 2683413A28D94048B6498E35 /* libPods-OTAcceleratorCoreTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-OTAcceleratorCoreTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 2D8F94C631606B5ED2303115 /* Pods-OTAcceleratorCoreTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OTAcceleratorCoreTests.debug.xcconfig"; path = "Target Support Files/Pods-OTAcceleratorCoreTests/Pods-OTAcceleratorCoreTests.debug.xcconfig"; sourceTree = ""; }; - 690AA9969CC84105235B9505 /* Pods-OTAcceleratorCoreTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OTAcceleratorCoreTests.release.xcconfig"; path = "Target Support Files/Pods-OTAcceleratorCoreTests/Pods-OTAcceleratorCoreTests.release.xcconfig"; sourceTree = ""; }; + 129EDCFC253F0E0500541893 /* Constant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Constant.h; sourceTree = ""; }; + 129EDCFD253F0E0600541893 /* OTTextMessage_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTTextMessage_Private.h; sourceTree = ""; }; + 129EDCFE253F0E0600541893 /* OTTextMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTTextMessage.m; sourceTree = ""; }; + 129EDD00253F0E0600541893 /* OTTextChat.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTTextChat.m; sourceTree = ""; }; + 129EDD01253F0E0600541893 /* OTTextChat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTTextChat.h; sourceTree = ""; }; + 129EDD02253F0E0600541893 /* OTTextMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTTextMessage.h; sourceTree = ""; }; + 129EDD07253F0EEC00541893 /* OTTextChatAcceleratorBundle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTTextChatAcceleratorBundle.m; sourceTree = ""; }; + 129EDD08253F0EEC00541893 /* OTTextChatAcceleratorBundle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTTextChatAcceleratorBundle.h; sourceTree = ""; }; + 129EDD0B253F0F1600541893 /* OTTextChatTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTTextChatTableViewCell.h; sourceTree = ""; }; + 129EDD0C253F0F1600541893 /* OTTextChatTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTTextChatTableViewCell.m; sourceTree = ""; }; + 129EDD0F253F0F3300541893 /* OTTextChatNavigationBar_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTTextChatNavigationBar_Private.h; sourceTree = ""; }; + 129EDD10253F0F3300541893 /* OTTextChatNavigationBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTTextChatNavigationBar.h; sourceTree = ""; }; + 129EDD11253F0F3300541893 /* OTTextChatTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTTextChatTableView.h; sourceTree = ""; }; + 129EDD12253F0F3300541893 /* OTTextChatInputView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTTextChatInputView.m; sourceTree = ""; }; + 129EDD13253F0F3300541893 /* OTTextChatTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTTextChatTableView.m; sourceTree = ""; }; + 129EDD14253F0F3300541893 /* OTTextChatNavigationBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTTextChatNavigationBar.m; sourceTree = ""; }; + 129EDD15253F0F3400541893 /* OTTextChatViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTTextChatViewController.h; sourceTree = ""; }; + 129EDD16253F0F3400541893 /* OTTextChatViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTTextChatViewController.m; sourceTree = ""; }; + 129EDD17253F0F3400541893 /* OTTextChatInputView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTTextChatInputView.h; sourceTree = ""; }; + 129EDD20253F2BBD00541893 /* OTTextChatSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OTTextChatSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 129EDD39253F2C1200541893 /* CustomTimestampTextChatTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CustomTimestampTextChatTableViewCell.m; sourceTree = ""; }; + 129EDD3A253F2C1200541893 /* CustomTimestampTextChatTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CustomTimestampTextChatTableViewCell.xib; sourceTree = ""; }; + 129EDD3B253F2C1200541893 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + 129EDD3C253F2C1200541893 /* CustomReceiveTextChatTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CustomReceiveTextChatTableViewCell.m; sourceTree = ""; }; + 129EDD3D253F2C1200541893 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; + 129EDD3E253F2C1200541893 /* CustomTextChatTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomTextChatTableViewController.h; sourceTree = ""; }; + 129EDD3F253F2C1200541893 /* CustomSendTextChatTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CustomSendTextChatTableViewCell.m; sourceTree = ""; }; + 129EDD40253F2C1200541893 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 129EDD41253F2C1300541893 /* DefaultTextChatTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DefaultTextChatTableViewController.h; sourceTree = ""; }; + 129EDD42253F2C1300541893 /* DefaultTextChatTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DefaultTextChatTableViewController.m; sourceTree = ""; }; + 129EDD43253F2C1300541893 /* CustomDateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CustomDateFormatter.m; sourceTree = ""; }; + 129EDD44253F2C1300541893 /* CustomDateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomDateFormatter.h; sourceTree = ""; }; + 129EDD45253F2C1300541893 /* CustomReceiveTextChatTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CustomReceiveTextChatTableViewCell.xib; sourceTree = ""; }; + 129EDD46253F2C1300541893 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 129EDD47253F2C1300541893 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 129EDD48253F2C1300541893 /* CustomSendTextChatTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CustomSendTextChatTableViewCell.xib; sourceTree = ""; }; + 129EDD49253F2C1300541893 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 129EDD4A253F2C1300541893 /* CustomTextChatTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CustomTextChatTableViewController.m; sourceTree = ""; }; + 129EDD4B253F2C1400541893 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 129EDD4C253F2C1400541893 /* CustomSendTextChatTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomSendTextChatTableViewCell.h; sourceTree = ""; }; + 129EDD4D253F2C1400541893 /* CustomReceiveTextChatTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomReceiveTextChatTableViewCell.h; sourceTree = ""; }; + 129EDD4E253F2C1400541893 /* CustomTimestampTextChatTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomTimestampTextChatTableViewCell.h; sourceTree = ""; }; + 129EDD5E253F2E0F00541893 /* libOTAcceleratorCore.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libOTAcceleratorCore.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 12B6B97025407A4800293718 /* OTTextChatSampleBundle.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OTTextChatSampleBundle.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; + 12B6B97625407A5C00293718 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = "../../accelerator-textchat-ios/OTTextChatAcceleratorBundle/Info.plist"; sourceTree = ""; }; + 12B6B97725407A5C00293718 /* OTTextChatViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = OTTextChatViewController.xib; sourceTree = ""; }; + 12B6B97825407A5C00293718 /* TextChatReceivedShortTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TextChatReceivedShortTableViewCell.xib; sourceTree = ""; }; + 12B6B97925407A5C00293718 /* TextChatComponentDivTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TextChatComponentDivTableViewCell.xib; sourceTree = ""; }; + 12B6B97A25407A5C00293718 /* TextChatSentTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TextChatSentTableViewCell.xib; sourceTree = ""; }; + 12B6B97B25407A5C00293718 /* TextChatSentShortTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TextChatSentShortTableViewCell.xib; sourceTree = ""; }; + 12B6B97C25407A5C00293718 /* TextChatReceivedTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TextChatReceivedTableViewCell.xib; sourceTree = ""; }; + 12B6B97D25407A5C00293718 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 12B6B98725407B6300293718 /* OTTextChatSampleBundle.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = OTTextChatSampleBundle.bundle; sourceTree = ""; }; + 12B6B98D25407C1400293718 /* OTTextChatTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OTTextChatTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 12B6B99725407C3A00293718 /* OTTextChatTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTTextChatTests.m; sourceTree = ""; }; + 12B6B99825407C3A00293718 /* OTTextChatUserInterface.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTTextChatUserInterface.m; sourceTree = ""; }; + 12B6B99925407C3A00293718 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 12B6B99A25407C3A00293718 /* OTTextMessageTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTTextMessageTests.m; sourceTree = ""; }; + 12B6B99B25407C3A00293718 /* OTTextChatAcceleratorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTTextChatAcceleratorTests.m; sourceTree = ""; }; + 12B6B9F92540843200293718 /* OTAnnotationSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OTAnnotationSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 12B6BA122540846500293718 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + 12B6BA132540846500293718 /* AnnotationOnScreenViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnnotationOnScreenViewController.swift; sourceTree = ""; }; + 12B6BA142540846500293718 /* mvc.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = mvc.png; sourceTree = ""; }; + 12B6BA152540846500293718 /* ReceiveAnnotationViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReceiveAnnotationViewController.m; sourceTree = ""; }; + 12B6BA162540846500293718 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 12B6BA172540846500293718 /* SendAnnotationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendAnnotationViewController.swift; sourceTree = ""; }; + 12B6BA182540846500293718 /* OTAnnotationSample-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OTAnnotationSample-Bridging-Header.h"; sourceTree = ""; }; + 12B6BA192540846500293718 /* AnnotationBlankCanvasViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnnotationBlankCanvasViewController.swift; sourceTree = ""; }; + 12B6BA1A2540846500293718 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 12B6BA1B2540846500293718 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 12B6BA1C2540846600293718 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 12B6BA1D2540846600293718 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; + 12B6BA1E2540846600293718 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 12B6BA1F2540846600293718 /* ReceiveAnnotationViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReceiveAnnotationViewController.h; sourceTree = ""; }; + 12B6BA822540927C00293718 /* AnnLoggingWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AnnLoggingWrapper.m; sourceTree = ""; }; + 12B6BA832540927C00293718 /* AnnLoggingWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AnnLoggingWrapper.h; sourceTree = ""; }; + 12B6BA88254092B500293718 /* UIButton+AutoLayoutHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIButton+AutoLayoutHelper.h"; sourceTree = ""; }; + 12B6BA89254092B500293718 /* OTAnnotationToolbarView+Animation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OTAnnotationToolbarView+Animation.m"; sourceTree = ""; }; + 12B6BA8A254092B500293718 /* OTAnnotationToolbarView+Animation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OTAnnotationToolbarView+Animation.h"; sourceTree = ""; }; + 12B6BA8B254092B500293718 /* JSON.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSON.m; sourceTree = ""; }; + 12B6BA8C254092B500293718 /* UIColor+HexString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIColor+HexString.h"; sourceTree = ""; }; + 12B6BA8D254092B500293718 /* JSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSON.h; sourceTree = ""; }; + 12B6BA8E254092B500293718 /* UIColor+HexString.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIColor+HexString.m"; sourceTree = ""; }; + 12B6BA8F254092B500293718 /* UIButton+AutoLayoutHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIButton+AutoLayoutHelper.m"; sourceTree = ""; }; + 12B6BA90254092B500293718 /* UIViewController+Helper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+Helper.m"; sourceTree = ""; }; + 12B6BA91254092B500293718 /* UIViewController+Helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+Helper.h"; sourceTree = ""; }; + 12B6BA97254092C600293718 /* Constants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Constants.h; sourceTree = ""; }; + 12B6BA98254092C600293718 /* OTAnnotationKitBundle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTAnnotationKitBundle.h; sourceTree = ""; }; + 12B6BA99254092C600293718 /* OTAnnotationKitBundle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTAnnotationKitBundle.m; sourceTree = ""; }; + 12B6BA9C2540930B00293718 /* OTAnnotationPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTAnnotationPath.h; sourceTree = ""; }; + 12B6BA9D2540930B00293718 /* OTAnnotationTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTAnnotationTextView.h; sourceTree = ""; }; + 12B6BA9E2540930B00293718 /* OTAnnotationDataManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTAnnotationDataManager.h; sourceTree = ""; }; + 12B6BA9F2540930B00293718 /* OTAnnotatable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTAnnotatable.h; sourceTree = ""; }; + 12B6BAA02540930C00293718 /* OTAnnotationTextView+Gesture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OTAnnotationTextView+Gesture.h"; sourceTree = ""; }; + 12B6BAA12540930C00293718 /* OTAnnotationTextView+Gesture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OTAnnotationTextView+Gesture.m"; sourceTree = ""; }; + 12B6BAA22540930C00293718 /* OTAnnotationTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTAnnotationTextView.m; sourceTree = ""; }; + 12B6BAA32540930C00293718 /* OTAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTAnnotationView.h; sourceTree = ""; }; + 12B6BAA42540930C00293718 /* OTAnnotationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTAnnotationView.m; sourceTree = ""; }; + 12B6BAA52540930C00293718 /* OTAnnotationTextView_Gesture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTAnnotationTextView_Gesture.h; sourceTree = ""; }; + 12B6BAA62540930C00293718 /* OTAnnotationDataManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTAnnotationDataManager.m; sourceTree = ""; }; + 12B6BAA72540930C00293718 /* OTAnnotationPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTAnnotationPath.m; sourceTree = ""; }; + 12B6BAAE2540933B00293718 /* LHToolbar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LHToolbar.m; sourceTree = ""; }; + 12B6BAAF2540933B00293718 /* LHToolbarContainerViewItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LHToolbarContainerViewItem.h; sourceTree = ""; }; + 12B6BAB02540933B00293718 /* LHToolbar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LHToolbar.h; sourceTree = ""; }; + 12B6BAB12540933B00293718 /* LHToolbarContainerViewItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LHToolbarContainerViewItem.m; sourceTree = ""; }; + 12B6BAB22540933B00293718 /* LHToolbarContainerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LHToolbarContainerView.h; sourceTree = ""; }; + 12B6BAB32540933B00293718 /* LHToolbarContainerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LHToolbarContainerView.m; sourceTree = ""; }; + 12B6BAB92540936D00293718 /* OTAnnotationColorPickerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTAnnotationColorPickerView.h; sourceTree = ""; }; + 12B6BABA2540936D00293718 /* OTAnnotationColorPickerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTAnnotationColorPickerView.m; sourceTree = ""; }; + 12B6BABC2540938500293718 /* OTAnnotationScrollView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTAnnotationScrollView.m; sourceTree = ""; }; + 12B6BABD2540938500293718 /* OTAnnotationScrollView_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTAnnotationScrollView_Private.h; sourceTree = ""; }; + 12B6BABE2540938600293718 /* OTFullScreenAnnotationViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTFullScreenAnnotationViewController.m; sourceTree = ""; }; + 12B6BABF2540938600293718 /* OTAnnotationScrollView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTAnnotationScrollView.h; sourceTree = ""; }; + 12B6BAC02540938600293718 /* OTFullScreenAnnotationViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTFullScreenAnnotationViewController.h; sourceTree = ""; }; + 12B6BAC4254093D600293718 /* OTAnnotationScreenCaptureView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTAnnotationScreenCaptureView.h; sourceTree = ""; }; + 12B6BAC5254093D600293718 /* OTAnnotationScreenCaptureViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTAnnotationScreenCaptureViewController.m; sourceTree = ""; }; + 12B6BAC6254093D600293718 /* OTAnnotationScreenCaptureViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTAnnotationScreenCaptureViewController.h; sourceTree = ""; }; + 12B6BAC7254093D600293718 /* OTAnnotationScreenCaptureView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTAnnotationScreenCaptureView.m; sourceTree = ""; }; + 12B6BACB2540940500293718 /* OTAnnotationEditTextViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTAnnotationEditTextViewController.h; sourceTree = ""; }; + 12B6BACC2540940600293718 /* OTAnnotationEditTextViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTAnnotationEditTextViewController.m; sourceTree = ""; }; + 12B6BACF2540943600293718 /* OTAnnotationToolbarView_UserInterfaces.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTAnnotationToolbarView_UserInterfaces.h; sourceTree = ""; }; + 12B6BAD02540943700293718 /* OTAnnotationToolbarView_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTAnnotationToolbarView_Private.h; sourceTree = ""; }; + 12B6BAD12540943700293718 /* OTAnnotationToolbarView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTAnnotationToolbarView.m; sourceTree = ""; }; + 12B6BAD22540943700293718 /* OTAnnotationToolbarView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTAnnotationToolbarView.h; sourceTree = ""; }; + 12B6BAD42540945500293718 /* OTAnnotator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTAnnotator.h; sourceTree = ""; }; + 12B6BAD52540945500293718 /* OTAnnotator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTAnnotator.m; sourceTree = ""; }; + 12B6BADB254095A600293718 /* OTAnnotationSampleBundle.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OTAnnotationSampleBundle.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; + 12B6BAE12540968700293718 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 12B6BAE22540968700293718 /* OTAnnotationEditTextViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = OTAnnotationEditTextViewController.xib; sourceTree = ""; }; + 12B6BAE32540968800293718 /* OTAnnotationScreenCaptureViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = OTAnnotationScreenCaptureViewController.xib; sourceTree = ""; }; + 12B6BAE42540968800293718 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 12B6BAE92540972900293718 /* OTAnnotationSampleBundle.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = OTAnnotationSampleBundle.bundle; sourceTree = ""; }; + 12B6BAEF25409B2C00293718 /* OTAnnotationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OTAnnotationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 12B6BAF925409B4400293718 /* OTFullScreenAnnotationViewControllerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTFullScreenAnnotationViewControllerTests.m; sourceTree = ""; }; + 12B6BAFA25409B4400293718 /* OTAnnotationKitTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTAnnotationKitTests.m; sourceTree = ""; }; + 12B6BAFB25409B4400293718 /* OTAnnotationColorPickerViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTAnnotationColorPickerViewTests.m; sourceTree = ""; }; + 12B6BAFC25409B4400293718 /* OTAnnotationPathTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTAnnotationPathTests.m; sourceTree = ""; }; + 12B6BAFD25409B4400293718 /* OTAnnotationViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTAnnotationViewTests.m; sourceTree = ""; }; + 12B6BAFE25409B4400293718 /* OTAnnotationDataManagerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTAnnotationDataManagerTests.m; sourceTree = ""; }; + 12B6BAFF25409B4400293718 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 12B6BB0025409B4400293718 /* OTAnnotationEditTextViewControllerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTAnnotationEditTextViewControllerTests.m; sourceTree = ""; }; + 12B6BB0125409B4500293718 /* OTAnnotationScreenCaptureViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTAnnotationScreenCaptureViewTests.m; sourceTree = ""; }; + 12B6BB0225409B4500293718 /* OTAnnotationTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTAnnotationTextView.m; sourceTree = ""; }; + 12B6BB0325409B4500293718 /* OTAnnotationScrollViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTAnnotationScrollViewTests.m; sourceTree = ""; }; A0127A8B1D34651B00CDA3F5 /* OTAcceleratorCore.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OTAcceleratorCore.app; sourceTree = BUILT_PRODUCTS_DIR; }; A0127A8F1D34651B00CDA3F5 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; A0127A911D34651B00CDA3F5 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; @@ -144,12 +386,52 @@ A0CDA0981DF23F060018A2BA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; A0CDA09E1DF23F060018A2BA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; A0FEABFC1D73B3BF000BDDA4 /* CHANGELOG.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = ""; }; - A0FEABFD1D73B3BF000BDDA4 /* OTAcceleratorCore.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = OTAcceleratorCore.podspec; sourceTree = ""; }; - A80AEBA3854C451B02B1AA34 /* Pods-OTAcceleratorCore.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OTAcceleratorCore.release.xcconfig"; path = "Target Support Files/Pods-OTAcceleratorCore/Pods-OTAcceleratorCore.release.xcconfig"; sourceTree = ""; }; - F8BF33AD6157E1E1E3747A54 /* libPods-OTAcceleratorCore.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-OTAcceleratorCore.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + A0FEABFD1D73B3BF000BDDA4 /* OTAcceleratorCore.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = OTAcceleratorCore.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 129EDD1D253F2BBD00541893 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 12B6B96D25407A4800293718 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 12B6B98A25407C1400293718 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 12B6B9F62540843200293718 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 12B6BAD8254095A600293718 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 12B6BAEC25409B2C00293718 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 300DD6EACD41C2282F550C6F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -162,7 +444,6 @@ buildActionMask = 2147483647; files = ( 120640222525FEBC008E3516 /* WebKit.framework in Frameworks */, - 70E24F0467232AEC1D62197A /* libPods-OTAcceleratorCore.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -177,13 +458,293 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - EE9663C7C3580221DE55E457 /* libPods-OTAcceleratorCoreTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 129EDCFB253F0DF500541893 /* TextChat */ = { + isa = PBXGroup; + children = ( + 129EDCFC253F0E0500541893 /* Constant.h */, + 129EDD01253F0E0600541893 /* OTTextChat.h */, + 129EDD00253F0E0600541893 /* OTTextChat.m */, + 129EDCFD253F0E0600541893 /* OTTextMessage_Private.h */, + 129EDD02253F0E0600541893 /* OTTextMessage.h */, + 129EDCFE253F0E0600541893 /* OTTextMessage.m */, + 129EDD06253F0EDD00541893 /* UI */, + ); + path = TextChat; + sourceTree = ""; + }; + 129EDD06253F0EDD00541893 /* UI */ = { + isa = PBXGroup; + children = ( + 129EDD0E253F0F2000541893 /* OTTextChatView */, + 129EDD0A253F0F0E00541893 /* OTTextChatTableViewCells */, + 129EDD08253F0EEC00541893 /* OTTextChatAcceleratorBundle.h */, + 129EDD07253F0EEC00541893 /* OTTextChatAcceleratorBundle.m */, + ); + path = UI; + sourceTree = ""; + }; + 129EDD0A253F0F0E00541893 /* OTTextChatTableViewCells */ = { + isa = PBXGroup; + children = ( + 129EDD0B253F0F1600541893 /* OTTextChatTableViewCell.h */, + 129EDD0C253F0F1600541893 /* OTTextChatTableViewCell.m */, + ); + path = OTTextChatTableViewCells; + sourceTree = ""; + }; + 129EDD0E253F0F2000541893 /* OTTextChatView */ = { + isa = PBXGroup; + children = ( + 129EDD17253F0F3400541893 /* OTTextChatInputView.h */, + 129EDD12253F0F3300541893 /* OTTextChatInputView.m */, + 129EDD0F253F0F3300541893 /* OTTextChatNavigationBar_Private.h */, + 129EDD10253F0F3300541893 /* OTTextChatNavigationBar.h */, + 129EDD14253F0F3300541893 /* OTTextChatNavigationBar.m */, + 129EDD11253F0F3300541893 /* OTTextChatTableView.h */, + 129EDD13253F0F3300541893 /* OTTextChatTableView.m */, + 129EDD15253F0F3400541893 /* OTTextChatViewController.h */, + 129EDD16253F0F3400541893 /* OTTextChatViewController.m */, + ); + path = OTTextChatView; + sourceTree = ""; + }; + 129EDD21253F2BBD00541893 /* OTTextChatSample */ = { + isa = PBXGroup; + children = ( + 12B6B98725407B6300293718 /* OTTextChatSampleBundle.bundle */, + 129EDD47253F2C1300541893 /* AppDelegate.h */, + 129EDD40253F2C1200541893 /* AppDelegate.m */, + 129EDD4B253F2C1400541893 /* Assets.xcassets */, + 129EDD44253F2C1300541893 /* CustomDateFormatter.h */, + 129EDD43253F2C1300541893 /* CustomDateFormatter.m */, + 129EDD4D253F2C1400541893 /* CustomReceiveTextChatTableViewCell.h */, + 129EDD3C253F2C1200541893 /* CustomReceiveTextChatTableViewCell.m */, + 129EDD45253F2C1300541893 /* CustomReceiveTextChatTableViewCell.xib */, + 129EDD4C253F2C1400541893 /* CustomSendTextChatTableViewCell.h */, + 129EDD3F253F2C1200541893 /* CustomSendTextChatTableViewCell.m */, + 129EDD48253F2C1300541893 /* CustomSendTextChatTableViewCell.xib */, + 129EDD3E253F2C1200541893 /* CustomTextChatTableViewController.h */, + 129EDD4A253F2C1300541893 /* CustomTextChatTableViewController.m */, + 129EDD4E253F2C1400541893 /* CustomTimestampTextChatTableViewCell.h */, + 129EDD39253F2C1200541893 /* CustomTimestampTextChatTableViewCell.m */, + 129EDD3A253F2C1200541893 /* CustomTimestampTextChatTableViewCell.xib */, + 129EDD41253F2C1300541893 /* DefaultTextChatTableViewController.h */, + 129EDD42253F2C1300541893 /* DefaultTextChatTableViewController.m */, + 129EDD49253F2C1300541893 /* Info.plist */, + 129EDD3B253F2C1200541893 /* LaunchScreen.storyboard */, + 129EDD46253F2C1300541893 /* main.m */, + 129EDD3D253F2C1200541893 /* Main.storyboard */, + ); + path = OTTextChatSample; + sourceTree = ""; + }; + 12B6B97125407A4800293718 /* OTTextChatSampleBundle */ = { + isa = PBXGroup; + children = ( + 12B6B97D25407A5C00293718 /* Assets.xcassets */, + 12B6B97625407A5C00293718 /* Info.plist */, + 12B6B97725407A5C00293718 /* OTTextChatViewController.xib */, + 12B6B97925407A5C00293718 /* TextChatComponentDivTableViewCell.xib */, + 12B6B97825407A5C00293718 /* TextChatReceivedShortTableViewCell.xib */, + 12B6B97C25407A5C00293718 /* TextChatReceivedTableViewCell.xib */, + 12B6B97B25407A5C00293718 /* TextChatSentShortTableViewCell.xib */, + 12B6B97A25407A5C00293718 /* TextChatSentTableViewCell.xib */, + ); + path = OTTextChatSampleBundle; + sourceTree = ""; + }; + 12B6B98E25407C1400293718 /* OTTextChatTests */ = { + isa = PBXGroup; + children = ( + 12B6B99925407C3A00293718 /* Info.plist */, + 12B6B99B25407C3A00293718 /* OTTextChatAcceleratorTests.m */, + 12B6B99725407C3A00293718 /* OTTextChatTests.m */, + 12B6B99825407C3A00293718 /* OTTextChatUserInterface.m */, + 12B6B99A25407C3A00293718 /* OTTextMessageTests.m */, + ); + path = OTTextChatTests; + sourceTree = ""; + }; + 12B6B9A1254082A200293718 /* OTAnnotation */ = { + isa = PBXGroup; + children = ( + 12B6BA832540927C00293718 /* AnnLoggingWrapper.h */, + 12B6BA822540927C00293718 /* AnnLoggingWrapper.m */, + 12B6BA87254092A400293718 /* Category */, + 12B6BA97254092C600293718 /* Constants.h */, + 12B6BA98254092C600293718 /* OTAnnotationKitBundle.h */, + 12B6BA99254092C600293718 /* OTAnnotationKitBundle.m */, + 12B6BA9B254092F500293718 /* OTAnnotationNative */, + 12B6BAAD2540932700293718 /* LHToolbar */, + 12B6BAB72540934700293718 /* OTAnnotationUI */, + 12B6BAD42540945500293718 /* OTAnnotator.h */, + 12B6BAD52540945500293718 /* OTAnnotator.m */, + ); + path = OTAnnotation; + sourceTree = ""; + }; + 12B6B9FA2540843300293718 /* OTAnnotationSample */ = { + isa = PBXGroup; + children = ( + 12B6BAE92540972900293718 /* OTAnnotationSampleBundle.bundle */, + 12B6BA192540846500293718 /* AnnotationBlankCanvasViewController.swift */, + 12B6BA132540846500293718 /* AnnotationOnScreenViewController.swift */, + 12B6BA1A2540846500293718 /* AppDelegate.h */, + 12B6BA1B2540846500293718 /* AppDelegate.m */, + 12B6BA1C2540846600293718 /* Assets.xcassets */, + 12B6BA162540846500293718 /* Info.plist */, + 12B6BA122540846500293718 /* LaunchScreen.storyboard */, + 12B6BA1E2540846600293718 /* main.m */, + 12B6BA1D2540846600293718 /* Main.storyboard */, + 12B6BA142540846500293718 /* mvc.png */, + 12B6BA182540846500293718 /* OTAnnotationSample-Bridging-Header.h */, + 12B6BA1F2540846600293718 /* ReceiveAnnotationViewController.h */, + 12B6BA152540846500293718 /* ReceiveAnnotationViewController.m */, + 12B6BA172540846500293718 /* SendAnnotationViewController.swift */, + ); + path = OTAnnotationSample; + sourceTree = ""; + }; + 12B6BA87254092A400293718 /* Category */ = { + isa = PBXGroup; + children = ( + 12B6BA8D254092B500293718 /* JSON.h */, + 12B6BA8B254092B500293718 /* JSON.m */, + 12B6BA8A254092B500293718 /* OTAnnotationToolbarView+Animation.h */, + 12B6BA89254092B500293718 /* OTAnnotationToolbarView+Animation.m */, + 12B6BA88254092B500293718 /* UIButton+AutoLayoutHelper.h */, + 12B6BA8F254092B500293718 /* UIButton+AutoLayoutHelper.m */, + 12B6BA8C254092B500293718 /* UIColor+HexString.h */, + 12B6BA8E254092B500293718 /* UIColor+HexString.m */, + 12B6BA91254092B500293718 /* UIViewController+Helper.h */, + 12B6BA90254092B500293718 /* UIViewController+Helper.m */, + ); + path = Category; + sourceTree = ""; + }; + 12B6BA9B254092F500293718 /* OTAnnotationNative */ = { + isa = PBXGroup; + children = ( + 12B6BA9F2540930B00293718 /* OTAnnotatable.h */, + 12B6BA9E2540930B00293718 /* OTAnnotationDataManager.h */, + 12B6BAA62540930C00293718 /* OTAnnotationDataManager.m */, + 12B6BA9C2540930B00293718 /* OTAnnotationPath.h */, + 12B6BAA72540930C00293718 /* OTAnnotationPath.m */, + 12B6BAA02540930C00293718 /* OTAnnotationTextView+Gesture.h */, + 12B6BAA12540930C00293718 /* OTAnnotationTextView+Gesture.m */, + 12B6BA9D2540930B00293718 /* OTAnnotationTextView.h */, + 12B6BAA22540930C00293718 /* OTAnnotationTextView.m */, + 12B6BAA52540930C00293718 /* OTAnnotationTextView_Gesture.h */, + 12B6BAA32540930C00293718 /* OTAnnotationView.h */, + 12B6BAA42540930C00293718 /* OTAnnotationView.m */, + ); + path = OTAnnotationNative; + sourceTree = ""; + }; + 12B6BAAD2540932700293718 /* LHToolbar */ = { + isa = PBXGroup; + children = ( + 12B6BAB02540933B00293718 /* LHToolbar.h */, + 12B6BAAE2540933B00293718 /* LHToolbar.m */, + 12B6BAB22540933B00293718 /* LHToolbarContainerView.h */, + 12B6BAB32540933B00293718 /* LHToolbarContainerView.m */, + 12B6BAAF2540933B00293718 /* LHToolbarContainerViewItem.h */, + 12B6BAB12540933B00293718 /* LHToolbarContainerViewItem.m */, + ); + path = LHToolbar; + sourceTree = ""; + }; + 12B6BAB72540934700293718 /* OTAnnotationUI */ = { + isa = PBXGroup; + children = ( + 12B6BAB82540935C00293718 /* ColorPicker */, + 12B6BABD2540938500293718 /* OTAnnotationScrollView_Private.h */, + 12B6BABF2540938600293718 /* OTAnnotationScrollView.h */, + 12B6BABC2540938500293718 /* OTAnnotationScrollView.m */, + 12B6BAC02540938600293718 /* OTFullScreenAnnotationViewController.h */, + 12B6BABE2540938600293718 /* OTFullScreenAnnotationViewController.m */, + 12B6BAC3254093A500293718 /* ScreenCapture */, + 12B6BACA254093E200293718 /* Text */, + 12B6BACE2540941F00293718 /* Toolbar */, + ); + path = OTAnnotationUI; + sourceTree = ""; + }; + 12B6BAB82540935C00293718 /* ColorPicker */ = { + isa = PBXGroup; + children = ( + 12B6BAB92540936D00293718 /* OTAnnotationColorPickerView.h */, + 12B6BABA2540936D00293718 /* OTAnnotationColorPickerView.m */, + ); + path = ColorPicker; + sourceTree = ""; + }; + 12B6BAC3254093A500293718 /* ScreenCapture */ = { + isa = PBXGroup; + children = ( + 12B6BAC4254093D600293718 /* OTAnnotationScreenCaptureView.h */, + 12B6BAC7254093D600293718 /* OTAnnotationScreenCaptureView.m */, + 12B6BAC6254093D600293718 /* OTAnnotationScreenCaptureViewController.h */, + 12B6BAC5254093D600293718 /* OTAnnotationScreenCaptureViewController.m */, + ); + path = ScreenCapture; + sourceTree = ""; + }; + 12B6BACA254093E200293718 /* Text */ = { + isa = PBXGroup; + children = ( + 12B6BACB2540940500293718 /* OTAnnotationEditTextViewController.h */, + 12B6BACC2540940600293718 /* OTAnnotationEditTextViewController.m */, + ); + path = Text; + sourceTree = ""; + }; + 12B6BACE2540941F00293718 /* Toolbar */ = { + isa = PBXGroup; + children = ( + 12B6BAD22540943700293718 /* OTAnnotationToolbarView.h */, + 12B6BAD12540943700293718 /* OTAnnotationToolbarView.m */, + 12B6BAD02540943700293718 /* OTAnnotationToolbarView_Private.h */, + 12B6BACF2540943600293718 /* OTAnnotationToolbarView_UserInterfaces.h */, + ); + path = Toolbar; + sourceTree = ""; + }; + 12B6BADC254095A600293718 /* OTAnnotationSampleBundle */ = { + isa = PBXGroup; + children = ( + 12B6BAE12540968700293718 /* Assets.xcassets */, + 12B6BAE42540968800293718 /* Info.plist */, + 12B6BAE22540968700293718 /* OTAnnotationEditTextViewController.xib */, + 12B6BAE32540968800293718 /* OTAnnotationScreenCaptureViewController.xib */, + ); + path = OTAnnotationSampleBundle; + sourceTree = ""; + }; + 12B6BAF025409B2C00293718 /* OTAnnotationTests */ = { + isa = PBXGroup; + children = ( + 12B6BAFF25409B4400293718 /* Info.plist */, + 12B6BAFB25409B4400293718 /* OTAnnotationColorPickerViewTests.m */, + 12B6BAFE25409B4400293718 /* OTAnnotationDataManagerTests.m */, + 12B6BB0025409B4400293718 /* OTAnnotationEditTextViewControllerTests.m */, + 12B6BAFA25409B4400293718 /* OTAnnotationKitTests.m */, + 12B6BAFC25409B4400293718 /* OTAnnotationPathTests.m */, + 12B6BB0125409B4500293718 /* OTAnnotationScreenCaptureViewTests.m */, + 12B6BB0325409B4500293718 /* OTAnnotationScrollViewTests.m */, + 12B6BB0225409B4500293718 /* OTAnnotationTextView.m */, + 12B6BAFD25409B4400293718 /* OTAnnotationViewTests.m */, + 12B6BAF925409B4400293718 /* OTFullScreenAnnotationViewControllerTests.m */, + ); + path = OTAnnotationTests; + sourceTree = ""; + }; A0127A821D34651B00CDA3F5 = { isa = PBXGroup; children = ( @@ -197,6 +758,12 @@ A0CDA0971DF23F060018A2BA /* OTAcceleratorCoreBundle */, A0AB9F1D1E3AD9F20068E20F /* OTAcceleratorCoreTests */, A05CC9F41E984AD700A1459F /* OTAcceleratorCoreUITests */, + 129EDD21253F2BBD00541893 /* OTTextChatSample */, + 12B6B97125407A4800293718 /* OTTextChatSampleBundle */, + 12B6B98E25407C1400293718 /* OTTextChatTests */, + 12B6B9FA2540843300293718 /* OTAnnotationSample */, + 12B6BADC254095A600293718 /* OTAnnotationSampleBundle */, + 12B6BAF025409B2C00293718 /* OTAnnotationTests */, A0127A8C1D34651B00CDA3F5 /* Products */, D3534160ECF98265C12CC505 /* Pods */, D554E5E8AC17BFB89B393667 /* Frameworks */, @@ -210,6 +777,12 @@ A07C06101DEBC2B500BB4FD9 /* OTAcceleratorCoreBundle.bundle */, A0AB9F1C1E3AD9F20068E20F /* OTAcceleratorCoreTests.xctest */, A05CC9F31E984AD700A1459F /* OTAcceleratorCoreUITests.xctest */, + 129EDD20253F2BBD00541893 /* OTTextChatSample.app */, + 12B6B97025407A4800293718 /* OTTextChatSampleBundle.bundle */, + 12B6B98D25407C1400293718 /* OTTextChatTests.xctest */, + 12B6B9F92540843200293718 /* OTAnnotationSample.app */, + 12B6BADB254095A600293718 /* OTAnnotationSampleBundle.bundle */, + 12B6BAEF25409B2C00293718 /* OTAnnotationTests.xctest */, ); name = Products; sourceTree = ""; @@ -267,6 +840,8 @@ A0565F211DF63F62001F9032 /* OTAcceleratorCore */ = { isa = PBXGroup; children = ( + 12B6B9A1254082A200293718 /* OTAnnotation */, + 129EDCFB253F0DF500541893 /* TextChat */, A05259301E9435DA000F9E88 /* UI */, A0565F231DF63F62001F9032 /* OTAcceleratorCoreBundle.h */, A0565F241DF63F62001F9032 /* OTAcceleratorCoreBundle.m */, @@ -321,10 +896,6 @@ D3534160ECF98265C12CC505 /* Pods */ = { isa = PBXGroup; children = ( - 13482FF31B2028E05711C5FB /* Pods-OTAcceleratorCore.debug.xcconfig */, - A80AEBA3854C451B02B1AA34 /* Pods-OTAcceleratorCore.release.xcconfig */, - 2D8F94C631606B5ED2303115 /* Pods-OTAcceleratorCoreTests.debug.xcconfig */, - 690AA9969CC84105235B9505 /* Pods-OTAcceleratorCoreTests.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -332,9 +903,8 @@ D554E5E8AC17BFB89B393667 /* Frameworks */ = { isa = PBXGroup; children = ( + 129EDD5E253F2E0F00541893 /* libOTAcceleratorCore.a */, 120640212525FEBC008E3516 /* WebKit.framework */, - F8BF33AD6157E1E1E3747A54 /* libPods-OTAcceleratorCore.a */, - 2683413A28D94048B6498E35 /* libPods-OTAcceleratorCoreTests.a */, ); name = Frameworks; sourceTree = ""; @@ -342,16 +912,118 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 129EDD1F253F2BBD00541893 /* OTTextChatSample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 129EDD36253F2BBE00541893 /* Build configuration list for PBXNativeTarget "OTTextChatSample" */; + buildPhases = ( + 129EDD1C253F2BBD00541893 /* Sources */, + 129EDD1D253F2BBD00541893 /* Frameworks */, + 129EDD1E253F2BBD00541893 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = OTTextChatSample; + productName = OTTextChat; + productReference = 129EDD20253F2BBD00541893 /* OTTextChatSample.app */; + productType = "com.apple.product-type.application"; + }; + 12B6B96F25407A4800293718 /* OTTextChatSampleBundle */ = { + isa = PBXNativeTarget; + buildConfigurationList = 12B6B97325407A4800293718 /* Build configuration list for PBXNativeTarget "OTTextChatSampleBundle" */; + buildPhases = ( + 12B6B96C25407A4800293718 /* Sources */, + 12B6B96D25407A4800293718 /* Frameworks */, + 12B6B96E25407A4800293718 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = OTTextChatSampleBundle; + productName = OTTextChatSampleBundle; + productReference = 12B6B97025407A4800293718 /* OTTextChatSampleBundle.bundle */; + productType = "com.apple.product-type.bundle"; + }; + 12B6B98C25407C1400293718 /* OTTextChatTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 12B6B99425407C1400293718 /* Build configuration list for PBXNativeTarget "OTTextChatTests" */; + buildPhases = ( + 12B6B98925407C1400293718 /* Sources */, + 12B6B98A25407C1400293718 /* Frameworks */, + 12B6B98B25407C1400293718 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 12B6B99325407C1400293718 /* PBXTargetDependency */, + ); + name = OTTextChatTests; + productName = OTTextChatTests; + productReference = 12B6B98D25407C1400293718 /* OTTextChatTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 12B6B9F82540843200293718 /* OTAnnotationSample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 12B6BA0F2540843400293718 /* Build configuration list for PBXNativeTarget "OTAnnotationSample" */; + buildPhases = ( + 12B6B9F52540843200293718 /* Sources */, + 12B6B9F62540843200293718 /* Frameworks */, + 12B6B9F72540843200293718 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = OTAnnotationSample; + productName = OTAnnotationSample; + productReference = 12B6B9F92540843200293718 /* OTAnnotationSample.app */; + productType = "com.apple.product-type.application"; + }; + 12B6BADA254095A600293718 /* OTAnnotationSampleBundle */ = { + isa = PBXNativeTarget; + buildConfigurationList = 12B6BADE254095A600293718 /* Build configuration list for PBXNativeTarget "OTAnnotationSampleBundle" */; + buildPhases = ( + 12B6BAD7254095A600293718 /* Sources */, + 12B6BAD8254095A600293718 /* Frameworks */, + 12B6BAD9254095A600293718 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = OTAnnotationSampleBundle; + productName = OTAnnotationSampleBundle; + productReference = 12B6BADB254095A600293718 /* OTAnnotationSampleBundle.bundle */; + productType = "com.apple.product-type.bundle"; + }; + 12B6BAEE25409B2C00293718 /* OTAnnotationTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 12B6BAF625409B2C00293718 /* Build configuration list for PBXNativeTarget "OTAnnotationTests" */; + buildPhases = ( + 12B6BAEB25409B2C00293718 /* Sources */, + 12B6BAEC25409B2C00293718 /* Frameworks */, + 12B6BAED25409B2C00293718 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 12B6BAF525409B2C00293718 /* PBXTargetDependency */, + ); + name = OTAnnotationTests; + productName = OTAnnotationTests; + productReference = 12B6BAEF25409B2C00293718 /* OTAnnotationTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; A0127A8A1D34651B00CDA3F5 /* OTAcceleratorCore */ = { isa = PBXNativeTarget; buildConfigurationList = A0127AA21D34651B00CDA3F5 /* Build configuration list for PBXNativeTarget "OTAcceleratorCore" */; buildPhases = ( - 39D52BCEDEF4C8C0CF149518 /* [CP] Check Pods Manifest.lock */, A0127A871D34651B00CDA3F5 /* Sources */, A0127A881D34651B00CDA3F5 /* Frameworks */, A0127A891D34651B00CDA3F5 /* Resources */, A0127AB61D34652500CDA3F5 /* Embed Frameworks */, - DCCD823F408DD0BEC541F904 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -401,7 +1073,6 @@ isa = PBXNativeTarget; buildConfigurationList = A0AB9F251E3AD9F20068E20F /* Build configuration list for PBXNativeTarget "OTAcceleratorCoreTests" */; buildPhases = ( - 1A89D99390D3C0C5EA66B8F1 /* [CP] Check Pods Manifest.lock */, A0AB9F181E3AD9F20068E20F /* Sources */, A0AB9F191E3AD9F20068E20F /* Frameworks */, A0AB9F1A1E3AD9F20068E20F /* Resources */, @@ -425,6 +1096,39 @@ LastUpgradeCheck = 1150; ORGANIZATIONNAME = "Tokbox, Inc."; TargetAttributes = { + 129EDD1F253F2BBD00541893 = { + CreatedOnToolsVersion = 11.5; + DevelopmentTeam = 7F2B5ZSP8Q; + ProvisioningStyle = Automatic; + }; + 12B6B96F25407A4800293718 = { + CreatedOnToolsVersion = 11.5; + DevelopmentTeam = 7F2B5ZSP8Q; + ProvisioningStyle = Automatic; + }; + 12B6B98C25407C1400293718 = { + CreatedOnToolsVersion = 11.5; + DevelopmentTeam = 7F2B5ZSP8Q; + ProvisioningStyle = Automatic; + TestTargetID = 129EDD1F253F2BBD00541893; + }; + 12B6B9F82540843200293718 = { + CreatedOnToolsVersion = 11.5; + DevelopmentTeam = 7F2B5ZSP8Q; + LastSwiftMigration = 1150; + ProvisioningStyle = Automatic; + }; + 12B6BADA254095A600293718 = { + CreatedOnToolsVersion = 11.5; + DevelopmentTeam = 7F2B5ZSP8Q; + ProvisioningStyle = Automatic; + }; + 12B6BAEE25409B2C00293718 = { + CreatedOnToolsVersion = 11.5; + DevelopmentTeam = 7F2B5ZSP8Q; + ProvisioningStyle = Automatic; + TestTargetID = 12B6B9F82540843200293718; + }; A0127A8A1D34651B00CDA3F5 = { CreatedOnToolsVersion = 7.3; }; @@ -460,11 +1164,81 @@ A07C060F1DEBC2B500BB4FD9 /* OTAcceleratorCoreBundle */, A0AB9F1B1E3AD9F20068E20F /* OTAcceleratorCoreTests */, A05CC9F21E984AD700A1459F /* OTAcceleratorCoreUITests */, + 129EDD1F253F2BBD00541893 /* OTTextChatSample */, + 12B6B96F25407A4800293718 /* OTTextChatSampleBundle */, + 12B6B98C25407C1400293718 /* OTTextChatTests */, + 12B6B9F82540843200293718 /* OTAnnotationSample */, + 12B6BADA254095A600293718 /* OTAnnotationSampleBundle */, + 12B6BAEE25409B2C00293718 /* OTAnnotationTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 129EDD1E253F2BBD00541893 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 129EDD5A253F2C1400541893 /* CustomSendTextChatTableViewCell.xib in Resources */, + 129EDD53253F2C1400541893 /* Main.storyboard in Resources */, + 129EDD58253F2C1400541893 /* CustomReceiveTextChatTableViewCell.xib in Resources */, + 129EDD5D253F2C1400541893 /* Assets.xcassets in Resources */, + 129EDD50253F2C1400541893 /* CustomTimestampTextChatTableViewCell.xib in Resources */, + 129EDD51253F2C1400541893 /* LaunchScreen.storyboard in Resources */, + 12B6B98825407B6300293718 /* OTTextChatSampleBundle.bundle in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 12B6B96E25407A4800293718 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 12B6B98125407A5C00293718 /* TextChatComponentDivTableViewCell.xib in Resources */, + 12B6B97F25407A5C00293718 /* OTTextChatViewController.xib in Resources */, + 12B6B98025407A5C00293718 /* TextChatReceivedShortTableViewCell.xib in Resources */, + 12B6B98225407A5C00293718 /* TextChatSentTableViewCell.xib in Resources */, + 12B6B98325407A5C00293718 /* TextChatSentShortTableViewCell.xib in Resources */, + 12B6B98525407A5C00293718 /* Assets.xcassets in Resources */, + 12B6B98425407A5C00293718 /* TextChatReceivedTableViewCell.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 12B6B98B25407C1400293718 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 12B6B9F72540843200293718 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 12B6BA222540846600293718 /* mvc.png in Resources */, + 12B6BAEA2540972A00293718 /* OTAnnotationSampleBundle.bundle in Resources */, + 12B6BA292540846600293718 /* Main.storyboard in Resources */, + 12B6BA202540846600293718 /* LaunchScreen.storyboard in Resources */, + 12B6BA282540846600293718 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 12B6BAD9254095A600293718 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 12B6BAE52540968800293718 /* Assets.xcassets in Resources */, + 12B6BAE62540968800293718 /* OTAnnotationEditTextViewController.xib in Resources */, + 12B6BAE72540968800293718 /* OTAnnotationScreenCaptureViewController.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 12B6BAED25409B2C00293718 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; A0127A891D34651B00CDA3F5 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -505,72 +1279,117 @@ }; /* End PBXResourcesBuildPhase section */ -/* Begin PBXShellScriptBuildPhase section */ - 1A89D99390D3C0C5EA66B8F1 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; +/* Begin PBXSourcesBuildPhase section */ + 129EDD1C253F2BBD00541893 /* Sources */ = { + isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-OTAcceleratorCoreTests-checkManifestLockResult.txt", + 129EDD55253F2C1400541893 /* AppDelegate.m in Sources */, + 129EDD57253F2C1400541893 /* CustomDateFormatter.m in Sources */, + 12B6B93E253F2ED000293718 /* OTTextChat.m in Sources */, + 129EDD4F253F2C1400541893 /* CustomTimestampTextChatTableViewCell.m in Sources */, + 129EDD56253F2C1400541893 /* DefaultTextChatTableViewController.m in Sources */, + 127C5D6A25517EB5000549D5 /* OTAcceleratorSession.m in Sources */, + 129EDD52253F2C1400541893 /* CustomReceiveTextChatTableViewCell.m in Sources */, + 12B6B944253F2F1A00293718 /* OTTextChatAcceleratorBundle.m in Sources */, + 129EDD5C253F2C1400541893 /* CustomTextChatTableViewController.m in Sources */, + 12B6B942253F2F0900293718 /* OTTextChatInputView.m in Sources */, + 12B6B941253F2EF100293718 /* OTTextChatTableView.m in Sources */, + 129EDD59253F2C1400541893 /* main.m in Sources */, + 12B6B940253F2EE100293718 /* OTTextChatViewController.m in Sources */, + 127C5D62255177A7000549D5 /* OTAcceleratorCoreBundle.m in Sources */, + 129EDD54253F2C1400541893 /* CustomSendTextChatTableViewCell.m in Sources */, + 12B6B93F253F2ED300293718 /* OTTextMessage.m in Sources */, + 12B6B943253F2F0F00293718 /* OTTextChatNavigationBar.m in Sources */, + 12B6B945253F2F2000293718 /* OTTextChatTableViewCell.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; }; - 39D52BCEDEF4C8C0CF149518 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; + 12B6B96C25407A4800293718 /* Sources */ = { + isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-OTAcceleratorCore-checkManifestLockResult.txt", + runOnlyForDeploymentPostprocessing = 0; + }; + 12B6B98925407C1400293718 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 12B6B99D25407C3A00293718 /* OTTextChatUserInterface.m in Sources */, + 12B6B99F25407C3A00293718 /* OTTextMessageTests.m in Sources */, + 12B6B99C25407C3A00293718 /* OTTextChatTests.m in Sources */, + 12B6B9A025407C3B00293718 /* OTTextChatAcceleratorTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; }; - DCCD823F408DD0BEC541F904 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; + 12B6B9F52540843200293718 /* Sources */ = { + isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 12B6BABB2540936D00293718 /* OTAnnotationColorPickerView.m in Sources */, + 12B6BAC22540938600293718 /* OTFullScreenAnnotationViewController.m in Sources */, + 127C5D6925517B03000549D5 /* UIView+Helper.m in Sources */, + 12B6BA94254092B500293718 /* UIColor+HexString.m in Sources */, + 12B6BA252540846600293718 /* SendAnnotationViewController.swift in Sources */, + 12B6BA842540927C00293718 /* AnnLoggingWrapper.m in Sources */, + 12B6BA212540846600293718 /* AnnotationOnScreenViewController.swift in Sources */, + 12B6BA262540846600293718 /* AnnotationBlankCanvasViewController.swift in Sources */, + 12B6BAC12540938600293718 /* OTAnnotationScrollView.m in Sources */, + 12B6BAD32540943700293718 /* OTAnnotationToolbarView.m in Sources */, + 127C5D6825517873000549D5 /* OTAcceleratorSession.m in Sources */, + 12B6BAA92540930C00293718 /* OTAnnotationTextView.m in Sources */, + 12B6BAAA2540930C00293718 /* OTAnnotationView.m in Sources */, + 12B6BA232540846600293718 /* ReceiveAnnotationViewController.m in Sources */, + 12B6BA92254092B500293718 /* OTAnnotationToolbarView+Animation.m in Sources */, + 12B6BACD2540940600293718 /* OTAnnotationEditTextViewController.m in Sources */, + 12B6BA2A2540846600293718 /* main.m in Sources */, + 127C5D63255177A8000549D5 /* OTAcceleratorCoreBundle.m in Sources */, + 12B6BA96254092B500293718 /* UIViewController+Helper.m in Sources */, + 12B6BAB52540933B00293718 /* LHToolbarContainerViewItem.m in Sources */, + 12B6BAC8254093D600293718 /* OTAnnotationScreenCaptureViewController.m in Sources */, + 127C5D66255177E6000549D5 /* OTScreenCapture.m in Sources */, + 12B6BAAB2540930C00293718 /* OTAnnotationDataManager.m in Sources */, + 12B6BAB62540933B00293718 /* LHToolbarContainerView.m in Sources */, + 12B6BA93254092B500293718 /* JSON.m in Sources */, + 12B6BA95254092B500293718 /* UIButton+AutoLayoutHelper.m in Sources */, + 12B6BAD62540945500293718 /* OTAnnotator.m in Sources */, + 12B6BAAC2540930C00293718 /* OTAnnotationPath.m in Sources */, + 12B6BAB42540933B00293718 /* LHToolbar.m in Sources */, + 12B6BAA82540930C00293718 /* OTAnnotationTextView+Gesture.m in Sources */, + 12B6BA9A254092C600293718 /* OTAnnotationKitBundle.m in Sources */, + 12B6BA272540846600293718 /* AppDelegate.m in Sources */, + 127C5D65255177DD000549D5 /* OTVideoView.m in Sources */, + 127C5D64255177C7000549D5 /* OTOneToOneCommunicator.m in Sources */, + 127C5D67255177F9000549D5 /* OTAudioVideoControlView.m in Sources */, + 12B6BAC9254093D600293718 /* OTAnnotationScreenCaptureView.m in Sources */, ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-OTAcceleratorCore/Pods-OTAcceleratorCore-resources.sh", - "${PODS_ROOT}/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle", + runOnlyForDeploymentPostprocessing = 0; + }; + 12B6BAD7254095A600293718 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SVProgressHUD.bundle", + runOnlyForDeploymentPostprocessing = 0; + }; + 12B6BAEB25409B2C00293718 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 12B6BB0B25409B4500293718 /* OTAnnotationEditTextViewControllerTests.m in Sources */, + 12B6BB0725409B4500293718 /* OTAnnotationPathTests.m in Sources */, + 12B6BB0925409B4500293718 /* OTAnnotationDataManagerTests.m in Sources */, + 12B6BB0525409B4500293718 /* OTAnnotationKitTests.m in Sources */, + 12B6BB0825409B4500293718 /* OTAnnotationViewTests.m in Sources */, + 12B6BB0D25409B4500293718 /* OTAnnotationTextView.m in Sources */, + 12B6BB0425409B4500293718 /* OTFullScreenAnnotationViewControllerTests.m in Sources */, + 12B6BB0625409B4500293718 /* OTAnnotationColorPickerViewTests.m in Sources */, + 12B6BB0C25409B4500293718 /* OTAnnotationScreenCaptureViewTests.m in Sources */, + 12B6BB0E25409B4500293718 /* OTAnnotationScrollViewTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-OTAcceleratorCore/Pods-OTAcceleratorCore-resources.sh\"\n"; - showEnvVarsInLog = 0; }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ A0127A871D34651B00CDA3F5 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -627,6 +1446,16 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 12B6B99325407C1400293718 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 129EDD1F253F2BBD00541893 /* OTTextChatSample */; + targetProxy = 12B6B99225407C1400293718 /* PBXContainerItemProxy */; + }; + 12B6BAF525409B2C00293718 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 12B6B9F82540843200293718 /* OTAnnotationSample */; + targetProxy = 12B6BAF425409B2C00293718 /* PBXContainerItemProxy */; + }; A05CC9F91E984AD700A1459F /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = A0127A8A1D34651B00CDA3F5 /* OTAcceleratorCore */; @@ -659,6 +1488,302 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 129EDD37253F2BBE00541893 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 7F2B5ZSP8Q; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = OTTextChatSample/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.4; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.vonage.OTTextChat; + PRODUCT_NAME = OTTextChatSample; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 129EDD38253F2BBE00541893 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 7F2B5ZSP8Q; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = OTTextChatSample/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.4; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.vonage.OTTextChat; + PRODUCT_NAME = OTTextChatSample; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 12B6B97425407A4800293718 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 7F2B5ZSP8Q; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = OTTextChatSampleBundle/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.vonage.OTTextChatSampleBundle; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + WRAPPER_EXTENSION = bundle; + }; + name = Debug; + }; + 12B6B97525407A4800293718 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 7F2B5ZSP8Q; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = OTTextChatSampleBundle/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.vonage.OTTextChatSampleBundle; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + WRAPPER_EXTENSION = bundle; + }; + name = Release; + }; + 12B6B99525407C1400293718 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 7F2B5ZSP8Q; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = OTTextChatTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.4; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.vonage.OTTextChatTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/OTTextChatSample.app/OTTextChatSample"; + }; + name = Debug; + }; + 12B6B99625407C1400293718 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 7F2B5ZSP8Q; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = OTTextChatTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.4; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.vonage.OTTextChatTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/OTTextChatSample.app/OTTextChatSample"; + }; + name = Release; + }; + 12B6BA102540843400293718 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 7F2B5ZSP8Q; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = OTAnnotationSample/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.4; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.vonage.OTAnnotationSample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "OTAnnotationSample/OTAnnotationSample-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 12B6BA112540843400293718 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 7F2B5ZSP8Q; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = OTAnnotationSample/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.4; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.vonage.OTAnnotationSample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "OTAnnotationSample/OTAnnotationSample-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 12B6BADF254095A600293718 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 7F2B5ZSP8Q; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = OTAnnotationSampleBundle/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.vonage.OTAnnotationSampleBundle; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + WRAPPER_EXTENSION = bundle; + }; + name = Debug; + }; + 12B6BAE0254095A600293718 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 7F2B5ZSP8Q; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = OTAnnotationSampleBundle/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.vonage.OTAnnotationSampleBundle; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + WRAPPER_EXTENSION = bundle; + }; + name = Release; + }; + 12B6BAF725409B2C00293718 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 7F2B5ZSP8Q; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = OTAnnotationTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.4; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.vonage.OTAnnotationTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/OTAnnotationSample.app/OTAnnotationSample"; + }; + name = Debug; + }; + 12B6BAF825409B2C00293718 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 7F2B5ZSP8Q; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = OTAnnotationTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.4; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.vonage.OTAnnotationTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/OTAnnotationSample.app/OTAnnotationSample"; + }; + name = Release; + }; A0127AA01D34651B00CDA3F5 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -767,7 +1892,6 @@ }; A0127AA31D34651B00CDA3F5 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 13482FF31B2028E05711C5FB /* Pods-OTAcceleratorCore.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CURRENT_PROJECT_VERSION = 1; @@ -783,7 +1907,6 @@ }; A0127AA41D34651B00CDA3F5 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A80AEBA3854C451B02B1AA34 /* Pods-OTAcceleratorCore.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CURRENT_PROJECT_VERSION = 1; @@ -864,7 +1987,6 @@ }; A0AB9F231E3AD9F20068E20F /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 2D8F94C631606B5ED2303115 /* Pods-OTAcceleratorCoreTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; @@ -880,7 +2002,6 @@ }; A0AB9F241E3AD9F20068E20F /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 690AA9969CC84105235B9505 /* Pods-OTAcceleratorCoreTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; @@ -897,6 +2018,60 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 129EDD36253F2BBE00541893 /* Build configuration list for PBXNativeTarget "OTTextChatSample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 129EDD37253F2BBE00541893 /* Debug */, + 129EDD38253F2BBE00541893 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 12B6B97325407A4800293718 /* Build configuration list for PBXNativeTarget "OTTextChatSampleBundle" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 12B6B97425407A4800293718 /* Debug */, + 12B6B97525407A4800293718 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 12B6B99425407C1400293718 /* Build configuration list for PBXNativeTarget "OTTextChatTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 12B6B99525407C1400293718 /* Debug */, + 12B6B99625407C1400293718 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 12B6BA0F2540843400293718 /* Build configuration list for PBXNativeTarget "OTAnnotationSample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 12B6BA102540843400293718 /* Debug */, + 12B6BA112540843400293718 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 12B6BADE254095A600293718 /* Build configuration list for PBXNativeTarget "OTAnnotationSampleBundle" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 12B6BADF254095A600293718 /* Debug */, + 12B6BAE0254095A600293718 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 12B6BAF625409B2C00293718 /* Build configuration list for PBXNativeTarget "OTAnnotationTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 12B6BAF725409B2C00293718 /* Debug */, + 12B6BAF825409B2C00293718 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; A0127A861D34651B00CDA3F5 /* Build configuration list for PBXProject "OTAcceleratorCore" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/OTAcceleratorCore/OTAnnotation/AnnLoggingWrapper.h b/OTAcceleratorCore/OTAnnotation/AnnLoggingWrapper.h new file mode 100644 index 0000000..eeb4dba --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/AnnLoggingWrapper.h @@ -0,0 +1,15 @@ +// +// AnnLoggingWrapper.h +// OTAnnotationAccPackKit +// +// Created by Xi Huang on 8/25/16. +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import +#import + +@interface AnnLoggingWrapper : NSObject +@property (readonly, nonatomic) OTKLogger *logger; ++ (instancetype)sharedInstance; +@end diff --git a/OTAcceleratorCore/OTAnnotation/AnnLoggingWrapper.m b/OTAcceleratorCore/OTAnnotation/AnnLoggingWrapper.m new file mode 100644 index 0000000..6266db2 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/AnnLoggingWrapper.m @@ -0,0 +1,32 @@ +// +// AnnLoggingWrapper.m +// OTAnnotationAccPackKit +// +// Created by Xi Huang on 8/25/16. +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "AnnLoggingWrapper.h" +#import "Constants.h" + +@interface AnnLoggingWrapper() +@property (nonatomic) OTKLogger *logger; +@end + +@implementation AnnLoggingWrapper + ++ (instancetype)sharedInstance { + + static AnnLoggingWrapper *sharedInstance; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [[AnnLoggingWrapper alloc] init]; + sharedInstance.logger = [[OTKLogger alloc] initWithClientVersion:KLogClientVersion + source:[[NSBundle mainBundle] bundleIdentifier] + componentId:kLogComponentIdentifier + guid:[[NSUUID UUID] UUIDString]]; + }); + return sharedInstance; +} + +@end diff --git a/OTAcceleratorCore/OTAnnotation/Category/JSON.h b/OTAcceleratorCore/OTAnnotation/Category/JSON.h new file mode 100644 index 0000000..ae060ce --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/Category/JSON.h @@ -0,0 +1,15 @@ +// +// JSON.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import + +@interface JSON : NSObject + ++ (id)parseJSON:(NSString*)string; + ++ (NSString *)stringify:(id)json; + +@end diff --git a/OTAcceleratorCore/OTAnnotation/Category/JSON.m b/OTAcceleratorCore/OTAnnotation/Category/JSON.m new file mode 100644 index 0000000..f575d95 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/Category/JSON.m @@ -0,0 +1,32 @@ +// +// JSON.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "JSON.h" + +@implementation JSON + ++ (id)parseJSON:(NSString*)string { + + if (!string) return nil; + + NSError *error; + id json = [NSJSONSerialization JSONObjectWithData:[string dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:&error]; + if (error) return nil; + if (![json isKindOfClass:[NSDictionary class]] && ![json isKindOfClass:[NSArray class]]) return nil; + return json; +} + ++ (NSString *)stringify:(id)json { + + if (!json) return nil; + + NSError *error; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:json options:0 error:&error]; + if (error) return nil; + return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; +} + +@end diff --git a/OTAcceleratorCore/OTAnnotation/Category/OTAnnotationToolbarView+Animation.h b/OTAcceleratorCore/OTAnnotation/Category/OTAnnotationToolbarView+Animation.h new file mode 100644 index 0000000..5c5900e --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/Category/OTAnnotationToolbarView+Animation.h @@ -0,0 +1,15 @@ +// +// OTAnnotationToolbarView+Animation.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTAnnotationToolbarView.h" + +@interface OTAnnotationToolbarView (Animation) + +- (void)moveSelectionShadowViewTo:(UIButton *)sender; +- (void)showColorPickerView; +- (void)dismissColorPickerViewWithAniamtion:(BOOL)animated; + +@end diff --git a/OTAcceleratorCore/OTAnnotation/Category/OTAnnotationToolbarView+Animation.m b/OTAcceleratorCore/OTAnnotation/Category/OTAnnotationToolbarView+Animation.m new file mode 100644 index 0000000..6fd7fca --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/Category/OTAnnotationToolbarView+Animation.m @@ -0,0 +1,97 @@ +// +// OTAnnotationToolbarView+Animation.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTAnnotationToolbarView+Animation.h" +#import "OTAnnotationToolbarView_UserInterfaces.h" +#import "Constants.h" + +@implementation OTAnnotationToolbarView (Animation) + +- (void)moveSelectionShadowViewTo:(UIButton *)sender { + + if (![sender isKindOfClass:[UIButton class]]) { + [self.selectionShadowView removeFromSuperview]; + return; + } + + [self setUserInteractionEnabled:NO]; + CGRect holderViewFrame = sender.superview.frame; + CGRect hodlerViewBounds = sender.superview.bounds; + self.selectionShadowView.frame = CGRectMake(holderViewFrame.origin.x, holderViewFrame.origin.y, CGRectGetWidth(hodlerViewBounds), CGRectGetHeight(hodlerViewBounds)); + [self insertSubview:self.selectionShadowView atIndex:0]; + [self setUserInteractionEnabled:YES]; +} + +- (void)showColorPickerView { + + if (!self.colorPickerView.superview) { + CGRect selfFrame = self.frame; + self.colorPickerView.frame = selfFrame; + // We are adding this workaround setting the separator view alpha with 0 to fix an unknown animation issue + self.separatorView.alpha = 0; + [self.superview insertSubview:self.colorPickerView belowSubview:self]; + + [UIView animateWithDuration:1.0 animations:^(){ + + if (self.toolbarViewOrientation == OTAnnotationToolbarViewOrientationPortraitlBottom) { + CGFloat newY = selfFrame.origin.y - HeightOfColorPicker - 1; + self.colorPickerView.frame = CGRectMake(selfFrame.origin.x, newY, CGRectGetWidth(self.bounds), HeightOfColorPicker); + CGFloat separatorViewY = newY + HeightOfColorPicker; + self.separatorView.frame = CGRectMake(selfFrame.origin.x, separatorViewY, CGRectGetWidth(self.bounds), 1); + } + else if (self.toolbarViewOrientation == OTAnnotationToolbarViewOrientationLandscapeLeft) { + CGFloat newX = selfFrame.origin.x + HeightOfColorPicker + 1; + self.colorPickerView.frame = CGRectMake(newX, selfFrame.origin.y, WidthOfColorPicker, CGRectGetHeight(self.bounds)); + CGFloat separatorViewX = newX + WidthOfColorPicker; + self.separatorView.frame = CGRectMake(separatorViewX, selfFrame.origin.y, 1, CGRectGetHeight(self.bounds)); + } + else if (self.toolbarViewOrientation == OTAnnotationToolbarViewOrientationLandscapeRight) { + CGFloat newX = selfFrame.origin.x - HeightOfColorPicker - 1; + self.colorPickerView.frame = CGRectMake(newX, selfFrame.origin.y, WidthOfColorPicker, CGRectGetHeight(self.bounds)); + CGFloat separatorViewX = newX + WidthOfColorPicker; + self.separatorView.frame = CGRectMake(separatorViewX, selfFrame.origin.y, 1, CGRectGetHeight(self.bounds)); + } + } completion:^(BOOL finished) { + [self.superview addSubview:self.separatorView]; + // We are adding this workaround setting the separator view alpha with 1 to fix an unknown animation issue + self.separatorView.alpha = 1; + }]; + } + else { + [self dismissColorPickerViewWithAniamtion:NO]; + } +} + +- (void)dismissColorPickerViewWithAniamtion:(BOOL)animated { + + if (!animated) { + [self.colorPickerView removeFromSuperview]; + [self.separatorView removeFromSuperview]; + return; + } + + CGRect colorPickerViewFrame = self.colorPickerView.frame; + [UIView animateWithDuration:1.0 animations:^(){ + if (self.toolbarViewOrientation == OTAnnotationToolbarViewOrientationPortraitlBottom) { + CGFloat newY = colorPickerViewFrame.origin.y + HeightOfColorPicker + 1; + self.colorPickerView.frame = CGRectMake(0, newY, CGRectGetWidth(colorPickerViewFrame), HeightOfColorPicker); + } + else if (self.toolbarViewOrientation == OTAnnotationToolbarViewOrientationLandscapeLeft) { + CGFloat newX = colorPickerViewFrame.origin.x - WidthOfColorPicker - 1; + self.colorPickerView.frame = CGRectMake(newX, 0, WidthOfColorPicker, CGRectGetHeight(colorPickerViewFrame)); + } + else if (self.toolbarViewOrientation == OTAnnotationToolbarViewOrientationLandscapeRight) { + CGFloat newX = colorPickerViewFrame.origin.x + WidthOfColorPicker + 1; + self.colorPickerView.frame = CGRectMake(newX, 0, WidthOfColorPicker, CGRectGetHeight(colorPickerViewFrame)); + } + } completion:^(BOOL finished){ + + [self.colorPickerView removeFromSuperview]; + [self.separatorView removeFromSuperview]; + }]; +} + +@end diff --git a/OTAcceleratorCore/OTAnnotation/Category/UIButton+AutoLayoutHelper.h b/OTAcceleratorCore/OTAnnotation/Category/UIButton+AutoLayoutHelper.h new file mode 100644 index 0000000..8e57b73 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/Category/UIButton+AutoLayoutHelper.h @@ -0,0 +1,13 @@ +// +// UIButton+AutoLayoutHelper.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import + +@interface UIButton (AutoLayoutHelper) + +- (void)addCenterConstraints; + +@end diff --git a/OTAcceleratorCore/OTAnnotation/Category/UIButton+AutoLayoutHelper.m b/OTAcceleratorCore/OTAnnotation/Category/UIButton+AutoLayoutHelper.m new file mode 100644 index 0000000..784527e --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/Category/UIButton+AutoLayoutHelper.m @@ -0,0 +1,23 @@ +// +// UIButton+AutoLayoutHelper.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "UIButton+AutoLayoutHelper.h" + +@implementation UIButton (AutoLayoutHelper) + +- (void)addCenterConstraints { + + if (!self.superview) { + NSLog(@"Could not addCenterConstraints, superview is nil"); + } + + NSLayoutConstraint *centerX = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.superview attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]; + NSLayoutConstraint *centerY = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.superview attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]; + + [NSLayoutConstraint activateConstraints:@[centerX, centerY]]; +} + +@end diff --git a/OTAcceleratorCore/OTAnnotation/Category/UIColor+HexString.h b/OTAcceleratorCore/OTAnnotation/Category/UIColor+HexString.h new file mode 100644 index 0000000..2f65423 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/Category/UIColor+HexString.h @@ -0,0 +1,15 @@ +// +// UIColor+HexString.h +// +// Copyright © 2015 TokBox, Inc. All rights reserved. +// + +#import + +@interface UIColor(HexString) + ++ (NSString *)hexStringFromColor:(UIColor *)color; ++ (UIColor *)colorFromHexString:(NSString *)hexString; ++ (UIColor *)colorFromHex:(int)hex; + +@end diff --git a/OTAcceleratorCore/OTAnnotation/Category/UIColor+HexString.m b/OTAcceleratorCore/OTAnnotation/Category/UIColor+HexString.m new file mode 100644 index 0000000..4e9cc5a --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/Category/UIColor+HexString.m @@ -0,0 +1,38 @@ +// +// UIColor+HexString.m +// +// Copyright © 2015 TokBox, Inc. All rights reserved. +// + +#import "UIColor+HexString.h" + +#define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0] + +@implementation UIColor(HexString) + ++ (NSString *)hexStringFromColor:(UIColor *)color { + const CGFloat *components = CGColorGetComponents(color.CGColor); + + CGFloat r = components[0]; + CGFloat g = components[1]; + CGFloat b = components[2]; + + return [NSString stringWithFormat:@"#%02lX%02lX%02lX", + lroundf(r * 255), + lroundf(g * 255), + lroundf(b * 255)]; +} + ++ (UIColor *)colorFromHexString:(NSString *)hexString { + unsigned rgbValue = 0; + NSScanner *scanner = [NSScanner scannerWithString:hexString]; + [scanner setScanLocation:1]; // bypass '#' character + [scanner scanHexInt:&rgbValue]; + return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16)/255.0 green:((rgbValue & 0xFF00) >> 8)/255.0 blue:(rgbValue & 0xFF)/255.0 alpha:1.0]; +} + ++ (UIColor *)colorFromHex:(int)hex { + return UIColorFromRGB(hex); +} + +@end diff --git a/OTAcceleratorCore/OTAnnotation/Category/UIViewController+Helper.h b/OTAcceleratorCore/OTAnnotation/Category/UIViewController+Helper.h new file mode 100644 index 0000000..cb46fc3 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/Category/UIViewController+Helper.h @@ -0,0 +1,13 @@ +// +// UIViewController+Helper.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import + +@interface UIViewController (Helper) + ++ (UIViewController*)topViewControllerWithRootViewController; + +@end diff --git a/OTAcceleratorCore/OTAnnotation/Category/UIViewController+Helper.m b/OTAcceleratorCore/OTAnnotation/Category/UIViewController+Helper.m new file mode 100644 index 0000000..10bdfc4 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/Category/UIViewController+Helper.m @@ -0,0 +1,39 @@ +// +// UIViewController+Helper.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "UIViewController+Helper.h" + +@implementation UIViewController (Helper) + ++ (UIViewController*)topViewControllerWithRootViewController { + + UIApplication *app = [UIApplication sharedApplication]; + if (!app) return nil; + UIViewController *rootViewController = app.keyWindow.rootViewController; + if (!rootViewController) return nil; + return [UIViewController topViewControllerWithRootViewControllerHelper:rootViewController]; +} + ++ (UIViewController *)topViewControllerWithRootViewControllerHelper:(UIViewController*)rootViewController { + + if ([rootViewController isMemberOfClass:[UITabBarController class]]) { + UITabBarController* tabBarController = (UITabBarController*)rootViewController; + return [self topViewControllerWithRootViewControllerHelper:tabBarController.selectedViewController]; + } + else if ([rootViewController isMemberOfClass:[UINavigationController class]]) { + UINavigationController* navigationController = (UINavigationController*)rootViewController; + return [self topViewControllerWithRootViewControllerHelper:navigationController.visibleViewController]; + } + else if (rootViewController.presentedViewController) { + UIViewController* presentedViewController = rootViewController.presentedViewController; + return [self topViewControllerWithRootViewControllerHelper:presentedViewController]; + } + else { + return rootViewController; + } +} + +@end diff --git a/OTAcceleratorCore/OTAnnotation/Constants.h b/OTAcceleratorCore/OTAnnotation/Constants.h new file mode 100644 index 0000000..7b79ffb --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/Constants.h @@ -0,0 +1,31 @@ +// +// Constants.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import + +static const CGFloat DefaultToolbarHeight = 50.0f; +static const CGFloat WidthOfColorPicker = 49.0f; +static const CGFloat HeightOfColorPicker = 49.0f; +static const CGFloat LeadingPaddingOfAnnotationTextView = 20.0f; + + +static NSString * const kLogComponentIdentifier = @"annotationsAccPack"; +static NSString * const KLogClientVersion = @"ios-vsol-1.1.0"; +static NSString* const KLogActionInitialize = @"Init"; +static NSString* const KLogActionStartDrawing = @"StartDrawing"; +static NSString* const KLogActionEndDrawing = @"EndDrawing"; +static NSString* const KLogActionFreeHand = @"FreeHand"; +static NSString* const KLogActionPickerColor = @"PickerColor"; +static NSString* const KLogActionText = @"Text"; +static NSString* const KLogActionScreenCapture = @"ScreenCapture"; +static NSString* const KLogActionErase = @"Erase"; +static NSString* const KLogActionEraseAll = @"EraseAll"; +static NSString* const KLogActionUseToolbar = @"UseToolbar"; +static NSString* const KLogActionDone = @"DONE"; + +static NSString* const KLogVariationAttempt = @"Attempt"; +static NSString* const KLogVariationSuccess = @"Success"; +static NSString* const KLogVariationFailure = @"Failure"; diff --git a/OTAcceleratorCore/OTAnnotation/LHToolbar/LHToolbar.h b/OTAcceleratorCore/OTAnnotation/LHToolbar/LHToolbar.h new file mode 100644 index 0000000..2ed63cb --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/LHToolbar/LHToolbar.h @@ -0,0 +1,46 @@ +// +// LHToolbar.h +// Pods +// +// Created by Xi Huang on 5/15/16. +// +// + +#import + +@interface LHToolbar : UIView + +typedef NS_ENUM(NSUInteger, LHToolbarOrientation) { + LHToolbarOrientationHorizontal = 0, + LHToolbarOrientationVertical +}; + +@property (nonatomic) LHToolbarOrientation orientation; + +@property (readonly, nonatomic) NSInteger numberOfItems; + +- (instancetype)initWithNumberOfItems:(NSInteger)numberOfItems; + +- (void)reloadToolbar; + +- (void)reloadToolbarAtIndex:(NSInteger)index; + +- (void)setContentView:(UIView *)contentView + atIndex:(NSInteger)index; + +- (UIView *)contentViewAtIndex:(NSInteger)index; + +- (NSInteger)indexOfContentView:(UIView *)contentView; + +- (BOOL)containedContentView:(UIView *)contentView; + +- (void)addContentView:(UIView *)contentView; + +- (void)insertContentView:(UIView *)contentView + atIndex:(NSInteger)index; + +- (void)removeLastContentView; + +- (void)removeContentViewAtIndex:(NSInteger)index; + +@end diff --git a/OTAcceleratorCore/OTAnnotation/LHToolbar/LHToolbar.m b/OTAcceleratorCore/OTAnnotation/LHToolbar/LHToolbar.m new file mode 100644 index 0000000..2bf9a7f --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/LHToolbar/LHToolbar.m @@ -0,0 +1,132 @@ +// +// LHToolbar.m +// Pods +// +// Created by Xi Huang on 5/15/16. +// +// + +#import "LHToolbar.h" +#import "LHToolbarContainerView.h" + +@interface LHToolbar() +@property (nonatomic) NSInteger numberOfItems; +@property (nonatomic) LHToolbarContainerView *containerView; +@end + +@implementation LHToolbar +@synthesize numberOfItems = _numberOfItems; + +- (NSInteger)numberOfItems { + return _numberOfItems; +} + +- (void)setNumberOfItems:(NSInteger)numberOfItems { + if (numberOfItems <= 0) { + _numberOfItems = 0; + } + _numberOfItems = numberOfItems; + [self reloadToolbar]; +} + +- (instancetype)initWithNumberOfItems:(NSInteger)numberOfItems { + if (numberOfItems < 0) { + return nil; + } + + if (self = [super init]) { + + _numberOfItems = numberOfItems; + [self setupToolbarStyle]; + + _containerView = [[LHToolbarContainerView alloc] init]; + for (NSUInteger i = 0; i < numberOfItems; i++) { + [_containerView.contentViews addObject:((UIView *)[NSNull null])]; + } + _containerView.dataSource = self; + [self addSubview:self.containerView]; + } + return self; +} + +- (void)didMoveToSuperview { + [super didMoveToSuperview]; + [self reloadToolbar]; +} + +- (void)setupToolbarStyle { + self.backgroundColor = [UIColor clearColor]; +} + +- (void)reloadToolbar { + [self.containerView reloadToolbarContainerView]; +} + +- (void)reloadToolbarAtIndex:(NSInteger)index { + [self.containerView reloadToolbarContainerViewAtIndex:index]; +} + +- (void)setContentView:(UIView *)contentView atIndex:(NSInteger)index { + if (!contentView || index < 0 || index >= self.numberOfItems) return; + + [self.containerView.contentViews setObject:contentView atIndexedSubscript:index]; +} + +- (UIView *)contentViewAtIndex:(NSInteger)index { + + if (index < 0 || index >= self.numberOfItems) return nil; + + id contentView = self.containerView.contentViews[index]; + if ([contentView isEqual:[NSNull null]]) { + return nil; + } + return contentView; +} + +- (NSInteger)indexOfContentView:(UIView *)contentView { + if (!contentView || ![self containedContentView:contentView]) return -1; + return [self.containerView.contentViews indexOfObject:contentView]; +} + +- (BOOL)containedContentView:(UIView *)contentView { + if (!contentView || [contentView isEqual:[NSNull null]]) return NO; + return [self.containerView.contentViews containsObject:contentView]; +} + +- (void)addContentView:(UIView *)contentView { + [self insertContentView:contentView atIndex:self.containerView.contentViews.count]; +} + +- (void)insertContentView:(UIView *)contentView + atIndex:(NSInteger)index { + + if (!contentView || index < 0 || self.containerView.contentViews.count < index) return; + + [self.containerView.contentViews insertObject:contentView atIndex:index]; + self.numberOfItems += 1; +} + +- (void)removeLastContentView { + [self removeContentViewAtIndex:self.containerView.contentViews.count - 1]; +} + +- (void)removeContentViewAtIndex:(NSInteger)index { + + if (index < 0 || self.containerView.contentViews.count <= index) return; + + [self.containerView.contentViews removeObjectAtIndex:index]; + self.numberOfItems -= 1; +} + +#pragma mark - LHToolbarContainerViewDataSource +- (NSInteger)numberOfItemInContainerView:(LHToolbarContainerView *)containerView { + if (self.numberOfItems <= 0) return 0; + return self.numberOfItems; +} + +- (LHToolbarOrientation)orientationInContainerView:(LHToolbarContainerView *)containerView { + + return self.orientation; +} + +@end diff --git a/OTAcceleratorCore/OTAnnotation/LHToolbar/LHToolbarContainerView.h b/OTAcceleratorCore/OTAnnotation/LHToolbar/LHToolbarContainerView.h new file mode 100644 index 0000000..e2c5fb4 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/LHToolbar/LHToolbarContainerView.h @@ -0,0 +1,22 @@ +// +// LHToolbarContainerView.h +// Pods +// +// Created by Xi Huang on 5/15/16. +// +// + +#import "LHToolbar.h" + +@class LHToolbarContainerView; +@protocol LHToolbarContainerViewDataSource +- (NSInteger)numberOfItemInContainerView:(LHToolbarContainerView *)containerView; +- (LHToolbarOrientation)orientationInContainerView:(LHToolbarContainerView *)containerView; +@end + +@interface LHToolbarContainerView : UIView +@property (weak, nonatomic) id dataSource; +@property (nonatomic) NSMutableArray *contentViews; +- (void)reloadToolbarContainerView; +- (void)reloadToolbarContainerViewAtIndex:(NSInteger)index; +@end diff --git a/OTAcceleratorCore/OTAnnotation/LHToolbar/LHToolbarContainerView.m b/OTAcceleratorCore/OTAnnotation/LHToolbar/LHToolbarContainerView.m new file mode 100644 index 0000000..4ea2ff3 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/LHToolbar/LHToolbarContainerView.m @@ -0,0 +1,83 @@ +// +// LHToolbarContainerView.m +// Pods +// +// Created by Xi Huang on 5/15/16. +// +// + +#import "UIView+Helper.h" +#import "LHToolbarContainerView.h" +#import "LHToolbarContainerViewItem.h" +#import "LHToolbar.h" + +@implementation LHToolbarContainerView + +- (instancetype)init { + if (self = [super init]) { + self.backgroundColor = [UIColor clearColor]; + self.translatesAutoresizingMaskIntoConstraints = NO; + _contentViews = [[NSMutableArray alloc] init]; + } + return self; +} + +- (void)didMoveToSuperview { + [super didMoveToSuperview]; + + [self addAttachedLayoutConstantsToSuperview]; + [self reloadToolbarContainerView]; +} + +- (void)reloadToolbarContainerViewAtIndex:(NSInteger)index { + if (index < 0 || self.contentViews.count <= index) return; + if (!self.dataSource) return; + + UIView *contentView = self.contentViews[index]; + if (![contentView isEqual:[NSNull null]]) { + UIView *view = self.subviews[index]; + [view.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; + [view addSubview:contentView]; + } +} + +- (void)reloadToolbarContainerView { + if (!self.dataSource) return; + + for (UIView *subview in self.subviews) { + [subview removeFromSuperview]; + } + + NSInteger numberOfContainerView = [self.dataSource numberOfItemInContainerView:self]; + if (!numberOfContainerView) return; + + + NSInteger i = 1; + while (i <= numberOfContainerView) { + + LHToolbarOrientation orientation = [self.dataSource orientationInContainerView:self]; + LHToolbarContainerViewItem *item; + if (orientation == LHToolbarOrientationHorizontal) { + item = [[LHToolbarContainerViewItem alloc] initWithPercentageOfScreenWidth:1.0 / numberOfContainerView]; + } + else { + item = [[LHToolbarContainerViewItem alloc] initWithPercentageOfScreenHeight:1.0 / numberOfContainerView]; + } + [self addSubview:item]; + i++; + } + + if (!self.subviews.count) { + return; + } + + for (NSInteger i = 0; i < self.contentViews.count; i++) { + UIView *contentView = self.contentViews[i]; + if (![contentView isEqual:[NSNull null]]) { + UIView *view = self.subviews[i]; + [view addSubview:contentView]; + } + } +} + +@end diff --git a/OTAcceleratorCore/OTAnnotation/LHToolbar/LHToolbarContainerViewItem.h b/OTAcceleratorCore/OTAnnotation/LHToolbar/LHToolbarContainerViewItem.h new file mode 100644 index 0000000..e8b06e4 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/LHToolbar/LHToolbarContainerViewItem.h @@ -0,0 +1,22 @@ +// +// LHToolbarContainerViewItem.h +// Pods +// +// Created by Xi Huang on 5/15/16. +// +// + +#import + +@interface LHToolbarContainerViewItem : UIView + +@property (readonly, nonatomic) CGFloat percentageOfScreenWidth; + +- (instancetype)initWithPercentageOfScreenWidth:(CGFloat)percentageOfScreenWidth; + + +@property (readonly, nonatomic) CGFloat percentageOfScreenHeight; + +- (instancetype)initWithPercentageOfScreenHeight:(CGFloat)percentageOfScreenHeight; + +@end diff --git a/OTAcceleratorCore/OTAnnotation/LHToolbar/LHToolbarContainerViewItem.m b/OTAcceleratorCore/OTAnnotation/LHToolbar/LHToolbarContainerViewItem.m new file mode 100644 index 0000000..a853a9f --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/LHToolbar/LHToolbarContainerViewItem.m @@ -0,0 +1,234 @@ +// +// LHToolbarContainerViewItem.m +// Pods +// +// Created by Xi Huang on 5/15/16. +// +// + +#import "LHToolbarContainerViewItem.h" + +@interface LHToolbarContainerViewItem() +@property (nonatomic) NSLayoutConstraint *top; +@property (nonatomic) NSLayoutConstraint *bottom; +@property (nonatomic) NSLayoutConstraint *leading; +@property (nonatomic) NSLayoutConstraint *trailing; + +@property (nonatomic) NSLayoutConstraint *widthConstraint; +@property (nonatomic) NSLayoutConstraint *heightConstraint; + +@property (nonatomic) CGFloat percentageOfScreenWidth; +@property (nonatomic) CGFloat percentageOfScreenHeight; +@end + +@implementation LHToolbarContainerViewItem + +- (NSLayoutConstraint *)bottom { + if (!_bottom) { + _bottom = [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:self.superview + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:0.0]; + } + return _bottom; +} + +- (NSLayoutConstraint *)trailing { + if (!_trailing) { + _trailing = [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self.superview + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:0.0]; + } + return _trailing; +} + +- (NSLayoutConstraint *)widthConstraint { + if (!_widthConstraint) { + if (self.percentageOfScreenWidth <= 0) return nil; + _widthConstraint = [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:self.superview + attribute:NSLayoutAttributeWidth + multiplier:self.percentageOfScreenWidth + constant:0.0]; + } + return _widthConstraint; +} + +- (NSLayoutConstraint *)heightConstraint { + + if (!_heightConstraint) { + if (self.percentageOfScreenHeight <= 0) return nil; + _heightConstraint = [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:self.superview + attribute:NSLayoutAttributeHeight + multiplier:self.percentageOfScreenHeight + constant:0.0]; + } + return _heightConstraint; +} + +- (instancetype)initWithPercentageOfScreenWidth:(CGFloat)percentageOfScreenWidth { + if (percentageOfScreenWidth <= 0 || percentageOfScreenWidth > 1.0) return nil; + + if (self = [self initLHToolbarContainerViewItem]) { + _percentageOfScreenWidth = percentageOfScreenWidth; + } + return self; +} + +- (instancetype)initWithPercentageOfScreenHeight:(CGFloat)percentageOfScreenHeight { + if (percentageOfScreenHeight <= 0 || percentageOfScreenHeight > 1.0) return nil; + + if (self = [self initLHToolbarContainerViewItem]) { + _percentageOfScreenHeight = percentageOfScreenHeight; + } + return self; +} + +- (instancetype)initLHToolbarContainerViewItem { + if (self = [super init]) { + self.backgroundColor = [UIColor clearColor]; + self.translatesAutoresizingMaskIntoConstraints = NO; + } + return self; +} + +- (void)didMoveToSuperview { + [super didMoveToSuperview]; + + if (!self.superview) return; + + if (self.percentageOfScreenWidth != 0) { + + if (!self.top) { + _top = [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.superview + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:0.0]; + } + + if (!self.bottom) { + _bottom = [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:self.superview + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:0.0]; + } + + self.top.active = YES; + self.bottom.active = YES; + self.widthConstraint.active = YES; + [self setLeadingPadding:0.0f]; + } + else if (self.percentageOfScreenHeight != 0) { + + + if (!self.leading) { + _leading = [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:self.superview + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:0.0]; + } + + if (!self.trailing) { + _trailing = _trailing = [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self.superview + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:0.0]; + } + + self.leading.active = YES; + self.trailing.active = YES; + self.heightConstraint.active = YES; + [self setTopPadding:0.0f]; + } + else { + // error + } +} + +- (void)setLeadingPadding:(CGFloat)padding { + if (!self.superview) return; + if (padding < 0 || 1 < padding) return; + + if (self.leading.active) { + self.leading.active = NO; + } + self.leading = nil; + + if (self.superview.subviews.count == 1) { + self.leading = [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:self.superview + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:padding]; + } + else { + NSInteger count = self.superview.subviews.count; + self.leading = [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:self.superview.subviews[count - 2] + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:padding]; + } + self.leading.active = YES; +} + +- (void)setTopPadding:(CGFloat)padding { + if (!self.superview) return; + if (padding < 0 || 1 < padding) return; + + if (self.top.active) { + self.top.active = NO; + } + self.top = nil; + + if (self.superview.subviews.count == 1) { + self.top = [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.superview + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:padding]; + } + else { + NSInteger count = self.superview.subviews.count; + self.top = [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.superview.subviews[count - 2] + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:padding]; + } + self.top.active = YES; +} + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationKitBundle.h b/OTAcceleratorCore/OTAnnotation/OTAnnotationKitBundle.h new file mode 100644 index 0000000..836634a --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationKitBundle.h @@ -0,0 +1,16 @@ +// +// OTAnnotationKitBundle.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import + +@interface OTAnnotationAcceleratorBundle : NSObject + +/** + * @return The bundle for assets in OTAnnotationAcceleratorBundle. + */ ++ (NSBundle *)annotationAcceleratorBundle; + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationKitBundle.m b/OTAcceleratorCore/OTAnnotation/OTAnnotationKitBundle.m new file mode 100644 index 0000000..94b58ce --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationKitBundle.m @@ -0,0 +1,36 @@ +// +// OTAnnotationKitBundle.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTAnnotationKitBundle.h" +#import "OTAnnotationEditTextViewController.h" +#import "OTAcceleratorCoreBundle.h" + +@implementation OTAnnotationAcceleratorBundle + ++ (NSBundle *)annotationAcceleratorBundle { + + NSURL *annotationtKitBundleURL = [[NSBundle mainBundle] URLForResource:@"OTAnnotationSampleBundle" withExtension:@"bundle"]; + if (annotationtKitBundleURL){ + NSBundle *annotationBundle = [NSBundle bundleWithURL:annotationtKitBundleURL]; + if (!annotationBundle.isLoaded) { + [annotationBundle load]; + } + return annotationBundle; + } + + annotationtKitBundleURL = [[NSBundle bundleForClass:[OTAnnotationEditTextViewController class]] URLForResource:@"OTAnnotationSampleBundle" withExtension:@"bundle"]; + if (annotationtKitBundleURL) { + NSBundle *annotationBundle = [NSBundle bundleWithURL:annotationtKitBundleURL]; + if (!annotationBundle.isLoaded) { + [annotationBundle load]; + } + return annotationBundle; + } + + return [OTAcceleratorCoreBundle acceleratorCoreBundle];; +} + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotatable.h b/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotatable.h new file mode 100644 index 0000000..d9a12d8 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotatable.h @@ -0,0 +1,17 @@ +// +// Annotatable.h +// +// Copyright © 2016 Tokbox. All rights reserved. +// + +#import + +/** + * The protocol that all drawable tools on OTAnnotationView conform. + */ +@protocol OTAnnotatable + +@optional +- (void)commit; + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationDataManager.h b/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationDataManager.h new file mode 100644 index 0000000..9fac6f0 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationDataManager.h @@ -0,0 +1,82 @@ +// +// AnnotationManager.h +// +// Copyright © 2016 Tokbox. All rights reserved. +// + +#import "OTAnnotationPath.h" +#import "OTAnnotationTextView.h" + +/** + * The annotation data manager retains and maintains all annotatable objects. Internally, it's implemented as a stack for conveniently performing operations like redo and undo. + */ +@interface OTAnnotationDataManager : NSObject + +/** + * The array that retains all annotatable objects. + */ +@property (readonly, nonatomic) NSArray> *annotatable; + +/** + * The object of the peak of the stack. + */ +@property (readonly, nonatomic) id peakOfAnnotatable; + +/** + * The remote object of the peak of the stack. + */ +@property (readonly, nonatomic) id peakOfRemoteAnnotatable; + +/** + * Initialize an annotation data manager. + * + * @return A new annotation data manager. + */ +- (instancetype)init; + +/** + * Add an annotatable object on top of the stack. + * + * @param annotatable The new annotatable object. + */ +- (void)addAnnotatable:(id)annotatable; + +/** + * Remove the top annotatable object. + * + * @return The removed annotatable object. + */ +- (id)pop; + +/** + * Remove the top remote annotatable object. + * + * @return The removed annotatable object. + */ +- (id)popRemote; + +/** + * Remove a specified remote annotatable object. + * + * @param annotatable The annotatable object to be removed. + */ +- (void)remove:(id)annotatable; + +/** + * Check whether the stack contains the given annotatable object. + * + * @return The boolean value to indicate whether the stack contains the given annotatable object. + */ +- (BOOL)containsAnnotatable:(id)annotatable; + +/** + * Clear up all local annotatable objects. + */ +- (void)popAll; + +/** + * Clear up all remote annotatable objects. + */ +- (void)popRemoteAll; + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationDataManager.m b/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationDataManager.m new file mode 100644 index 0000000..80c5ad2 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationDataManager.m @@ -0,0 +1,156 @@ +// +// AnnotationManager.m +// +// Copyright © 2016 Tokbox. All rights reserved. +// + +#import "OTAnnotationDataManager.h" + +@interface OTAnnotationDataManager() +@property (nonatomic) NSMutableArray> *mutableAnnotatable; +@property (nonatomic) id peakOfAnnotatable; +@property (nonatomic) id peakOfRemoteAnnotatable; +@end + +@implementation OTAnnotationDataManager + +- (NSArray> *)annotatable { + return [_mutableAnnotatable copy]; +} + +- (instancetype)init { + if (self = [super init]) { + _mutableAnnotatable = [[NSMutableArray alloc] init]; + } + return self; +} + +- (void)addAnnotatable:(id)annotatable { + if (!annotatable || ![annotatable conformsToProtocol:@protocol(OTAnnotatable)]) return; + [_mutableAnnotatable addObject:annotatable]; + + if ([annotatable isMemberOfClass:[OTAnnotationPath class]] || [annotatable isMemberOfClass:[OTAnnotationTextView class]]) { + _peakOfAnnotatable = annotatable; + } + else if ([annotatable isMemberOfClass:[OTRemoteAnnotationPath class]] || [annotatable isMemberOfClass:[OTRemoteAnnotationTextView class]]) { + _peakOfRemoteAnnotatable = annotatable; + } + [self annotatable]; +} + +- (id)pop { + + if (!_peakOfAnnotatable) return nil; + + // remove annotatable + id popAnnotatable = _peakOfAnnotatable; + [_mutableAnnotatable removeObject:_peakOfAnnotatable]; + + [self resetPeakOfAnnotatable]; + + return popAnnotatable; +} + +- (id)popRemote { + + if (!_peakOfRemoteAnnotatable) return nil; + + // remove remote annotatable + id popAnnotatable = _peakOfRemoteAnnotatable; + [_mutableAnnotatable removeObject:_peakOfRemoteAnnotatable]; + + [self resetPeakOfRemoteAnnotatable]; + + return popAnnotatable; +} + +- (void)remove:(id)annotatable { + [_mutableAnnotatable removeObject:annotatable]; + [self resetPeakOfRemoteAnnotatable]; + [self annotatable]; +} + +- (BOOL)containsAnnotatable:(id)annotatable { + if (!annotatable || ![annotatable conformsToProtocol:@protocol(OTAnnotatable)]) return NO; + return [self.annotatable containsObject:annotatable]; +} + +- (void)resetPeakOfAnnotatable { + // remove to the next peak + NSUInteger index = _mutableAnnotatable.count - 1; + while (index != NSUIntegerMax) { + + id annotatable = [_mutableAnnotatable objectAtIndex:index]; + if ([annotatable isMemberOfClass:[OTAnnotationPath class]] || [annotatable isMemberOfClass:[OTAnnotationTextView class]]) { + _peakOfAnnotatable = annotatable; + break; + } + else { + index -= 1; + } + } + + if (index == NSUIntegerMax) { + _peakOfAnnotatable = nil; + } +} + +- (void)resetPeakOfRemoteAnnotatable { + // remove to the next peak + NSUInteger index = _mutableAnnotatable.count - 1; + while (index != NSUIntegerMax) { + + id annotatable = [_mutableAnnotatable objectAtIndex:index]; + if ([annotatable isMemberOfClass:[OTRemoteAnnotationPath class]] || [annotatable isMemberOfClass:[OTRemoteAnnotationTextView class]]) { + _peakOfRemoteAnnotatable = annotatable; + break; + } + else { + index -= 1; + } + } + + if (index == NSUIntegerMax) { + _peakOfRemoteAnnotatable = nil; + } +} + +- (void)popAll { + + NSMutableArray *objectsToRemove = [[NSMutableArray alloc] init]; + for (id annotatable in _mutableAnnotatable) { + if ([annotatable isMemberOfClass:[OTAnnotationPath class]] || [annotatable isMemberOfClass:[OTAnnotationTextView class]]) { + [objectsToRemove addObject:annotatable]; + + if ([annotatable isMemberOfClass:[OTAnnotationTextView class]]) { + OTAnnotationTextView *textView = (OTAnnotationTextView *)annotatable; + [textView removeFromSuperview]; + } + } + } + [_mutableAnnotatable removeObjectsInArray:objectsToRemove]; + + self.peakOfAnnotatable = nil; + [self annotatable]; +} + +- (void)popRemoteAll { + + NSMutableArray *objectsToRemove = [[NSMutableArray alloc] init]; + for (id annotatable in _mutableAnnotatable) { + if ([annotatable isMemberOfClass:[OTRemoteAnnotationPath class]] || [annotatable isMemberOfClass:[OTRemoteAnnotationTextView class]]) { + [objectsToRemove addObject:annotatable]; + + if ([annotatable isMemberOfClass:[OTRemoteAnnotationTextView class]]) { + OTRemoteAnnotationTextView *textView = (OTRemoteAnnotationTextView *)annotatable; + [textView removeFromSuperview]; + } + } + } + + [_mutableAnnotatable removeObjectsInArray:objectsToRemove]; + self.peakOfRemoteAnnotatable = nil; + [self annotatable]; +} + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationPath.h b/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationPath.h new file mode 100644 index 0000000..f7a5033 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationPath.h @@ -0,0 +1,113 @@ +// +// ScreenSharePath.h +// +// Copyright © 2016 Tokbox. All rights reserved. +// + +#import "OTAnnotatable.h" + +/** + * The class describes an annotatable point in an OTAnnotationView. + */ +@interface OTAnnotationPoint : NSObject + +@property (readonly, nonatomic) CGFloat x; + +@property (readonly, nonatomic) CGFloat y; + +@property (readonly, nonatomic) CGPoint cgPoint; + ++ (instancetype)pointWithX:(CGFloat)x + andY:(CGFloat)y; + +@end + +/** + * The class describes a free-hand path in an OTAnnotationView. + */ +@interface OTAnnotationPath : UIBezierPath + +/** + * The stroke color of the path. + */ +@property (nonatomic) UIColor *strokeColor; + +/** + * The start point of the path. + */ +@property (readonly, nonatomic) CGPoint startPoint; + +/** + * The last point of the path. + */ +@property (readonly, nonatomic) CGPoint endPoint; + +/** + * An array of points, from startPoint to endPoint, describes how the path is constructed. + */ +@property (readonly, nonatomic) NSArray *points; + +/** + * An identifier for grouping a continuous annotation path object. + */ +@property (readonly, nonatomic) NSString *uuid; + +/** + * Initialize a path with given strokeColor. + * + * @param strokeColor The stroke color of the newer path. + * + * @return A new object of OTAnnotationPath. + */ +- (instancetype)initWithStrokeColor:(UIColor *)strokeColor; + +/** + * Give the path a start point. + * + * @param point The start point of the path. + */ +- (void)startAtPoint:(OTAnnotationPoint *)point; + +/** + * Draw a stright line from the last point to the given point. + * + * @param point The destination point of the line segment. + */ +- (void)drawToPoint:(OTAnnotationPoint *)point; + +/** + * Draw a curve line from the last point to the given point. + * + * @param point The destination point of the line segment. + */ + +- (void)drawCurveFrom:(OTAnnotationPoint *)fromPoint to:(OTAnnotationPoint *)toPoint; + +- (void)addPointToCollection:(OTAnnotationPoint *)point; + +/** + * Initialize a path with given existence coordinates and given strokeColor. The actual construction will not happen until you call drawWholePath. + * + * @param points An array of points, from startPoint to endPoint, describes how the path is composed. + * @param strokeColor The stroke color of the path. + * + * @return A new object of OTAnnotationPath. + */ +- (instancetype)initWithPoints:(NSArray *)points + strokeColor:(UIColor *)strokeColor; + +/** + * Construct the path based on the current points array. + */ +- (void)drawWholePath; + +@end + +@interface OTRemoteAnnotationPath : OTAnnotationPath + +@property (readonly, nonatomic) NSString *remoteGUID; + +- (instancetype)initWithStrokeColor:(UIColor *)strokeColor + remoteGUID:(NSString *)remoteGUID; + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationPath.m b/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationPath.m new file mode 100644 index 0000000..326f2fa --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationPath.m @@ -0,0 +1,154 @@ +// +// ScreenSharePath.m +// +// Copyright © 2016 Tokbox. All rights reserved. +// + +#import "OTAnnotationPath.h" + +@interface OTAnnotationPoint() +@property (nonatomic) CGFloat x; +@property (nonatomic) CGFloat y; +@property (nonatomic) CGPoint point; +@end + +@implementation OTAnnotationPoint + ++ (instancetype)pointWithX:(CGFloat)x andY:(CGFloat)y { + OTAnnotationPoint *pt = [[OTAnnotationPoint alloc] init]; + pt.x = x; + pt.y = y; + return pt; +} + +- (CGPoint)cgPoint { + return CGPointMake(_x, _y); +} +@end + +@interface OTAnnotationPath() +@property (nonatomic) CGPoint startPoint; +@property (nonatomic) CGPoint endPoint; +@property (nonatomic) NSMutableArray *mutablePoints; +@end + +@implementation OTAnnotationPath + +- (NSArray *)points { + return [_mutablePoints copy]; +} + +- (instancetype)initWithStrokeColor:(UIColor *)strokeColor { + + if (self = [super init]) { + _mutablePoints = [[NSMutableArray alloc] init]; + _strokeColor = strokeColor; + _uuid = [NSUUID UUID].UUIDString; + self.lineWidth = 2.0f; + } + return self; +} + +- (instancetype)initWithPoints:(NSArray *)points + strokeColor:(UIColor *)strokeColor { + + if (self = [super init]) { + _mutablePoints = [[NSMutableArray alloc] initWithArray:points]; + _strokeColor = strokeColor; + _uuid = [NSUUID UUID].UUIDString; + self.lineWidth = 2.0f; + + OTAnnotationPoint *startPoint = [points firstObject]; + OTAnnotationPoint *endPoint = [points lastObject]; + _startPoint = CGPointMake(startPoint.x, startPoint.y); + _endPoint = CGPointMake(endPoint.x, endPoint.y); + } + return self; +} + +- (void)drawWholePath { + + OTAnnotationPoint *firstPoint = [self.points firstObject]; + + [self moveToPoint:[firstPoint cgPoint]]; + for (NSUInteger i = 1; i < self.points.count - 1; i++) { + OTAnnotationPoint *thisPoint = self.points[i]; + [self addLineToPoint:[thisPoint cgPoint]]; + } + + OTAnnotationPoint *lastPoint = [self.points lastObject]; + [self addLineToPoint:[lastPoint cgPoint]]; +} + +- (void)startAtPoint:(OTAnnotationPoint *)point { + + CGPoint cgPoint = [point cgPoint]; + [self moveToPoint:cgPoint]; + [self addPointToCollection:point]; +} + +- (void)drawToPoint:(OTAnnotationPoint *)point { + + CGPoint cgPoint = [point cgPoint]; + [self addLineToPoint:cgPoint]; + [self addPointToCollection:point]; +} + +- (void)drawToPoint:(OTAnnotationPoint *)fromPoint endPoint:(OTAnnotationPoint *)toPoint{ + + CGPoint endPoint = [toPoint cgPoint]; + CGPoint startPoint = self.points.firstObject.cgPoint; + [self moveToPoint:startPoint]; + [self addLineToPoint:endPoint]; + [self addPointToCollection:toPoint]; +} + +- (void)drawCurveFrom:(OTAnnotationPoint *)fromPoint to:(OTAnnotationPoint *)toPoint { + + if (self.points.count == 0) { + [self addPointToCollection:fromPoint]; + } + + CGPoint secondLastPoint = self.points.count == 1? self.points.firstObject.cgPoint : self.points[self.points.count - 2].cgPoint; + CGPoint lastPoint = self.points.lastObject.cgPoint; + CGPoint middlePoint = CGPointMake((lastPoint.x + secondLastPoint.x) / 2, (lastPoint.y + secondLastPoint.y) / 2); + CGPoint controlPoint = CGPointMake((lastPoint.x + toPoint.x) / 2, (lastPoint.y + toPoint.y) / 2); + + if (self.points.count == 1) { + [self addPointToCollection:toPoint]; + + [self moveToPoint:self.points.firstObject.cgPoint]; + [self addQuadCurveToPoint:controlPoint controlPoint:lastPoint]; + } + + else { + + [self moveToPoint:self.points.count != 1? middlePoint : self.points.firstObject.cgPoint]; + [self addQuadCurveToPoint:controlPoint controlPoint:lastPoint]; + [self addPointToCollection:toPoint]; + } +} + +#pragma mark - private method +- (void)addPointToCollection:(OTAnnotationPoint *)touchPoint { + [_mutablePoints addObject:touchPoint]; +} +@end + +#pragma mark - OTRemoteAnnotationPath +@interface OTRemoteAnnotationPath() +@property (nonatomic) NSString *remoteGUID; +@end + +@implementation OTRemoteAnnotationPath + +- (instancetype)initWithStrokeColor:(UIColor *)strokeColor + remoteGUID:(NSString *)remoteGUID { + + if (self = [super initWithStrokeColor:strokeColor]) { + _remoteGUID = remoteGUID; + } + return self; +} + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationTextView+Gesture.h b/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationTextView+Gesture.h new file mode 100644 index 0000000..d5ec745 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationTextView+Gesture.h @@ -0,0 +1,23 @@ +// +// OTAnnotationTextView+Gesture.h +// OTAnnotationAccPackKit +// +// Created by Xi Huang on 8/1/16. +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTAnnotationTextView.h" + +@interface OTAnnotationTextView (Gesture) + +- (void)handleOnViewDragGesture:(UIGestureRecognizer *)recognizer; + +- (void)handleOnViewRotateGesture:(UIGestureRecognizer *)recognizer; + +- (void)handleOnViewZoomGesture:(UIGestureRecognizer *)recognizer; + +- (void)handleOnButtonZoomGesture:(UIGestureRecognizer *)recognizer; + +- (void)handleOnButtonRotateGesture:(UIGestureRecognizer *)recognizer; + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationTextView+Gesture.m b/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationTextView+Gesture.m new file mode 100644 index 0000000..d195960 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationTextView+Gesture.m @@ -0,0 +1,173 @@ +// +// OTAnnotationTextView+Gesture.m +// OTAnnotationAccPackKit +// +// Created by Xi Huang on 8/1/16. +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTAnnotationTextView+Gesture.h" +#import "OTAnnotationTextView_Gesture.h" + +CGFloat distanceBetweenPoints(CGPoint point1, CGPoint point2) { + CGFloat dx = point1.x - point2.x; + CGFloat dy = point1.y - point2.y; + return sqrt(dx*dx + dy*dy); +} + +CGFloat angleBetweenLinesInDegrees(CGPoint beginLineA, + CGPoint endLineA, + CGPoint beginLineB, + CGPoint endLineB) { + CGFloat a = endLineA.x - beginLineA.x; + CGFloat b = endLineA.y - beginLineA.y; + CGFloat c = endLineB.x - beginLineB.x; + CGFloat d = endLineB.y - beginLineB.y; + + CGFloat atanA = atan2(a, b); + CGFloat atanB = atan2(c, d); + + // convert radiants to degrees +// return (atanA - atanB) * 180 / M_PI; + return atanA - atanB; +} + +@implementation OTAnnotationTextView (Gesture) + +- (void)handleOnViewDragGesture:(UIGestureRecognizer *)recognizer { + + if (![recognizer isKindOfClass:[UIPanGestureRecognizer class]]) return; + + switch (recognizer.state) { + case UIGestureRecognizerStateBegan: { + self.referenceCenter = self.center; + break; + } + + case UIGestureRecognizerStateChanged: { + CGPoint panTranslation = [(UIPanGestureRecognizer *)recognizer translationInView:self.superview]; + self.currentCenter = CGPointMake(self.referenceCenter.x + panTranslation.x, + self.referenceCenter.y + panTranslation.y); + self.center = self.currentCenter; + break; + } + + default: + break; + } +} + +- (void)handleOnViewRotateGesture:(UIGestureRecognizer *)recognizer { + if (![recognizer isKindOfClass:[UIRotationGestureRecognizer class]]) return; + + switch (recognizer.state) { + case UIGestureRecognizerStateBegan: { + self.currentTransform = self.referenceTransform; + break; + } + + case UIGestureRecognizerStateChanged: { + self.currentTransform = CGAffineTransformRotate(self.referenceTransform, self.onViewRotationRecognizer.rotation); + self.transform = self.currentTransform; + break; + } + + case UIGestureRecognizerStateEnded: { + self.referenceTransform = self.currentTransform; + break; + } + + default: + break; + } +} + +- (void)handleOnViewZoomGesture:(UIGestureRecognizer *)recognizer +{ + if (![recognizer isKindOfClass:[UIPinchGestureRecognizer class]]) return; + + switch (recognizer.state) { + case UIGestureRecognizerStateBegan: { + self.currentTransform = self.referenceTransform; + break; + } + + case UIGestureRecognizerStateChanged: { + self.currentTransform = CGAffineTransformScale(self.referenceTransform, self.onViewPinchRecognizer.scale, self.onViewPinchRecognizer.scale); + self.transform = self.currentTransform; + break; + } + + case UIGestureRecognizerStateEnded: { + self.referenceTransform = self.currentTransform; + break; + } + + default: + break; + } +} + +- (void)handleOnButtonZoomGesture:(UIGestureRecognizer *)recognizer { + if (![recognizer isKindOfClass:[UIPanGestureRecognizer class]]) return; + + switch (recognizer.state) { + case UIGestureRecognizerStateBegan: { + self.currentTransform = self.referenceTransform; + CGPoint panTranslation = [(UIPanGestureRecognizer *)recognizer translationInView:self.superview]; + self.referenceDistance = distanceBetweenPoints(self.center, panTranslation); + break; + } + + case UIGestureRecognizerStateChanged: { + + CGPoint panTranslation = [(UIPanGestureRecognizer *)recognizer translationInView:self.superview]; + CGFloat distance = distanceBetweenPoints(self.center, panTranslation); + CGFloat scale = 1.0f; + if (self.referenceDistance != distance) { + scale = self.referenceDistance / distance; + } + self.currentTransform = CGAffineTransformScale(self.referenceTransform, scale, scale); + self.transform = self.currentTransform; + break; + } + + case UIGestureRecognizerStateEnded: { + self.referenceTransform = self.currentTransform; + break; + } + + default: + break; + } +} + +- (void)handleOnButtonRotateGesture:(UIGestureRecognizer *)recognizer { + if (![recognizer isKindOfClass:[UIPanGestureRecognizer class]]) return; + + switch (recognizer.state) { + case UIGestureRecognizerStateBegan: { + self.referenceTransform = self.currentTransform; + self.referencePoint = [(UIPanGestureRecognizer *)recognizer translationInView:self.superview]; + break; + } + + case UIGestureRecognizerStateChanged: { + CGPoint panTranslation = [(UIPanGestureRecognizer *)recognizer translationInView:self.superview]; + CGFloat radiants = -angleBetweenLinesInDegrees(self.currentCenter, panTranslation, self.currentCenter, self.referencePoint); + self.currentTransform = CGAffineTransformRotate(self.referenceTransform, radiants); + self.transform = self.currentTransform; + break; + } + + case UIGestureRecognizerStateEnded: { + self.referenceTransform = self.currentTransform; + break; + } + + default: + break; + } +} + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationTextView.h b/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationTextView.h new file mode 100644 index 0000000..f3d7a1f --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationTextView.h @@ -0,0 +1,103 @@ +// +// ScreenShareTextView.h +// +// Copyright © 2016 Tokbox. All rights reserved. +// + +#import "OTAnnotatable.h" + +@class OTAnnotationTextView; + +@protocol OTAnnotationTextViewDelegate + +@optional +- (void)annotationTextViewDidAddText:(OTAnnotationTextView *)textView; + +- (void)annotationTextViewDidFinishEditing:(OTAnnotationTextView *)textView; + +- (void)annotationTextViewDidCancel:(OTAnnotationTextView *)textView; + +@end + +/** + * The class describes a text annotation in an OTAnnotationView. The text view is draggable, resizable and rotatable. + */ +@interface OTAnnotationTextView: UITextView + +/** + * A boolean value to indicate whether the text view is resizable by using pinch gesture on it. + */ +@property (nonatomic, getter=isResizable) BOOL resizable; + +/** + * A boolean value to indicate whether the text view is draggable by using pan gesture on it. + */ +@property (nonatomic, getter=isDraggable) BOOL draggable; + +/** + * A boolean value to indicate whether the text view is rotatable by using rotate gesture on it. + */ +@property (nonatomic, getter=isRotatable) BOOL rotatable; + +/** + * The object that acts as the delegate of the annotation text view. + * + * The delegate must adopt the OTAnnotationTextViewDelegate protocol. The delegate is not retained. + */ +@property (weak, nonatomic) id annotationTextViewDelegate; + +/** + * Initialize a text annotation with given textColor. + * + * @param textColor The text color. + * + * @return A new object of OTAnnotationTextView. + */ +- (instancetype)initWithTextColor:(UIColor *)textColor; + +/** + * Initialize a text annotation with given content, color and font size. + * + * @param text The content of the text view. + * @param textColor The text color. + * @param fontSize The font size of the content. + * + * @return A new object of OTAnnotationTextView + */ +- (instancetype)initWithText:(NSString *)text + textColor:(UIColor *)textColor + fontSize:(CGFloat)fontSize; + +/** + * Initialize a remote text annotation with given content, color and font size. It's not resizable and rotatable. + * + * @param text The content of the text view. + * @param textColor The text color. + * @param fontSize The font size of the content. + * + * @return A new object of OTAnnotationTextView + */ +- (instancetype)initRemoteWithText:(NSString *)text + textColor:(UIColor *)textColor + fontSize:(CGFloat)fontSize; + +/** + * Commit the change. + */ +- (void)commit; + +@end + +@interface OTRemoteAnnotationTextView : OTAnnotationTextView + +@property (readonly, nonatomic) NSString *remoteGUID; + +- (instancetype)initWithTextColor:(UIColor *)textColor + remoteGUID:(NSString *)remoteGUID; + +- (instancetype)initWithText:(NSString *)text + textColor:(UIColor *)textColor + fontSize:(CGFloat)fontSize + remoteGUID:(NSString *)remoteGUID; + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationTextView.m b/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationTextView.m new file mode 100644 index 0000000..1523331 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationTextView.m @@ -0,0 +1,346 @@ +// +// ScreenShareTextView.m +// +// Copyright © 2016 Tokbox. All rights reserved. +// + +#import "OTAnnotationTextView.h" +#import "AnnLoggingWrapper.h" + +#import "OTAnnotationTextView+Gesture.h" +#import "OTAnnotationTextView_Gesture.h" + +#import "OTAnnotationKitBundle.h" +#import "Constants.h" + +@interface OTAnnotationTextView() { + BOOL startTyping; + BOOL isRemoteSignaling; +} +@property (nonatomic) UIButton *commitButton; +@property (nonatomic) UIButton *cancelButton; +@property (nonatomic) UIButton *rotateButton; +@property (nonatomic) UIButton *pinchButton; +@end + +@implementation OTAnnotationTextView + +- (UIButton *)cancelButton { + if (!_cancelButton) { + _cancelButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 30, 30)]; + [_cancelButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; + [_cancelButton setBackgroundColor:[UIColor clearColor]]; + [_cancelButton setImage:[UIImage imageNamed:@"delete icon" inBundle:[OTAnnotationAcceleratorBundle annotationAcceleratorBundle] compatibleWithTraitCollection:nil] forState:UIControlStateNormal]; + _cancelButton.center = CGPointMake(0, 0); + [_cancelButton addTarget:self action:@selector(cancelButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; + [self addSubview:_cancelButton]; + } + return _cancelButton; +} + +- (UIButton *)commitButton { + if (!_commitButton) { + _commitButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 30, 30)]; + [_commitButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; + [_commitButton setBackgroundColor:[UIColor colorWithRed:118.0/255.0f green:206.0/255.0f blue:31.0/255.0f alpha:1.0]]; + [_commitButton setImage:[UIImage imageNamed:@"checkmark" inBundle:[OTAnnotationAcceleratorBundle annotationAcceleratorBundle] compatibleWithTraitCollection:nil] forState:UIControlStateNormal]; + [_commitButton setImageEdgeInsets:UIEdgeInsetsMake(6, 6, 6, 6)]; + _commitButton.center = CGPointMake(CGRectGetWidth(self.bounds), 0); + _commitButton.layer.cornerRadius = CGRectGetWidth(_commitButton.bounds) / 2; + _commitButton.layer.borderColor = [UIColor whiteColor].CGColor; + _commitButton.layer.borderWidth = 2.0f; + [_commitButton addTarget:self action:@selector(commitButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; + [self addSubview:_commitButton]; + } + return _commitButton; +} + +- (void)setDraggable:(BOOL)draggable { + _draggable = draggable; + if (_draggable) { + _onViewPanRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self + action:@selector(handleOnViewDragGesture:)]; + [self addGestureRecognizer:_onViewPanRecognizer]; + } + else { + [self removeGestureRecognizer:_onViewPanRecognizer]; + _onViewPanRecognizer = nil; + } +} + +- (void)setResizable:(BOOL)resizable { + _resizable = resizable; + if (_resizable) { + _onViewPinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handleOnViewZoomGesture:)]; + [self addGestureRecognizer:_onViewPinchRecognizer]; + + _pinchButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 30, 30)]; + [_pinchButton setBackgroundColor:[UIColor clearColor]]; + [_pinchButton setImage:[UIImage imageNamed:@"resize icon" inBundle:[OTAnnotationAcceleratorBundle annotationAcceleratorBundle] compatibleWithTraitCollection:nil] forState:UIControlStateNormal]; + _pinchButton.center = CGPointMake(CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds)); + _pinchButton.layer.cornerRadius = CGRectGetWidth(_pinchButton.bounds) / 2; + _pinchButton.layer.borderColor = [UIColor whiteColor].CGColor; + _pinchButton.layer.borderWidth = 2.0f; + [self addSubview:_pinchButton]; + + _onButtonZoomRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleOnButtonZoomGesture:)]; + [_pinchButton addGestureRecognizer:_onButtonZoomRecognizer]; + } + else { + + [self removeGestureRecognizer:_onViewPinchRecognizer]; + _onViewPinchRecognizer = nil; + + // workaround: the text view will get cut off if we remove it directly + _pinchButton.hidden = YES; + _pinchButton = nil; + + [self removeGestureRecognizer:_onButtonZoomRecognizer]; + _onButtonZoomRecognizer = nil; + } +} + +- (void)setRotatable:(BOOL)rotatable { + _rotatable = rotatable; + if (_rotatable) { + _onViewRotationRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(handleOnViewRotateGesture:)]; + [self addGestureRecognizer:_onViewRotationRecognizer]; + + _rotateButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 30, 30)]; + [_rotateButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; + [_rotateButton setBackgroundColor:[UIColor clearColor]]; + [_rotateButton setImage:[UIImage imageNamed:@"rotate icon" inBundle:[OTAnnotationAcceleratorBundle annotationAcceleratorBundle] compatibleWithTraitCollection:nil] forState:UIControlStateNormal]; + _rotateButton.center = CGPointMake(0, CGRectGetHeight(self.bounds)); + _rotateButton.layer.cornerRadius = CGRectGetWidth(_rotateButton.bounds) / 2; + _rotateButton.layer.borderColor = [UIColor whiteColor].CGColor; + _rotateButton.layer.borderWidth = 2.0f; + [self addSubview:_rotateButton]; + + _onButtonRotateRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleOnButtonRotateGesture:)]; + [_rotateButton addGestureRecognizer:_onButtonRotateRecognizer]; + } + else { + + [self removeGestureRecognizer:_onViewRotationRecognizer]; + _onViewRotationRecognizer = nil; + + // workaround: the text view will get cut off if we remove it directly + _rotateButton.hidden = YES; + _rotateButton = nil; + + [self removeGestureRecognizer:_onButtonRotateRecognizer]; + _onButtonRotateRecognizer = nil; + } +} + +- (instancetype)init { + return nil; +} + +- (instancetype)initWithTextColor:(UIColor *)textColor { + if (!textColor) return nil; + return [[OTAnnotationTextView alloc] initWithText:nil textColor:textColor fontSize:0.0f]; +} + +- (instancetype)initWithText:(NSString *)text + textColor:(UIColor *)textColor + fontSize:(CGFloat)fontSize { + + if (self = [super init]) { + + CGRect screenBounds = [UIScreen mainScreen].bounds; + self.frame = CGRectMake(LeadingPaddingOfAnnotationTextView, 100, CGRectGetWidth(screenBounds) - LeadingPaddingOfAnnotationTextView * 2, 0); + + // attributes + [self setClipsToBounds:NO]; + [self setBackgroundColor:[UIColor clearColor]]; + [self setDelegate:self]; + [self setTextColor:textColor]; + [self setScrollEnabled:NO]; + [self setShowsVerticalScrollIndicator:NO]; + [self setShowsHorizontalScrollIndicator:NO]; + [self setTextAlignment:NSTextAlignmentCenter]; + [self setTextContainerInset:UIEdgeInsetsMake(0, 0, 0, 0)]; + [self setSelectable:NO]; + [self setEditable:YES]; + [self setUserInteractionEnabled:NO]; + + // text content + if (!text) { + [self setText:@"Type Something"]; + } + else { + [self setText:text]; + } + + if (fontSize == 0.0f) { + [self setFont:[UIFont systemFontOfSize:32.0f]]; + } + else { + [self setFont:[UIFont systemFontOfSize:fontSize]]; + } + + _referenceTransform = CGAffineTransformIdentity; + _currentTransform = CGAffineTransformIdentity; + + _referenceCenter = self.center; + _currentCenter = self.center; + + [self resizeTextView]; + } + + isRemoteSignaling = NO; + return self; +} + +- (instancetype)initRemoteWithText:(NSString *)text + textColor:(UIColor *)textColor + fontSize:(CGFloat)fontSize{ + if (self = [[OTAnnotationTextView alloc] initWithText:text textColor:textColor fontSize:fontSize]) { + isRemoteSignaling = YES; + } + return self; +} + +- (void)setFont:(UIFont *)font { + super.font = font; + [self resizeTextView]; +} + +- (void)setFrame:(CGRect)frame { + // -_- -_- -_- -_- -_- + // workaround: http://stackoverflow.com/questions/16301147/why-uitextview-draws-text-in-bad-frame-after-resizing + // by doing this, the text view won't get cut off after resizing + [super setFrame:CGRectZero]; + [super setFrame:frame]; +} + +- (void)commitToMove { + if (self.annotationTextViewDelegate) { + + if (isRemoteSignaling) { + self.resizable = NO; + self.rotatable = NO; + [self setFont:[UIFont systemFontOfSize:12.0f]]; + [self sizeToFit]; + } + else { + self.resizable = YES; + self.rotatable = YES; + } + self.draggable = YES; + + // set editable and selectable NO so it won't have the pop-up menu + [self setEditable:NO]; + [self setSelectable:NO]; + [self commitButton]; + [self cancelButton]; + + if (self.annotationTextViewDelegate && [self.annotationTextViewDelegate respondsToSelector:@selector(annotationTextViewDidAddText:)]) { + [self.annotationTextViewDelegate annotationTextViewDidAddText:self]; + } + } +} + +- (void)commit { + + self.layer.borderWidth = 0.0f; + self.layer.borderColor = nil; + self.backgroundColor = nil; + + self.resizable = NO; + self.draggable = NO; + self.rotatable = NO; + + // workaround: the text view will get cut off if we remove it directly + self.commitButton.hidden = YES; + self.commitButton = nil; + self.cancelButton.hidden = YES; + self.cancelButton = nil; + + [self setUserInteractionEnabled:NO]; + + if (isRemoteSignaling) { + [self sizeToFit]; + } + + [[AnnLoggingWrapper sharedInstance].logger logEventAction:KLogActionText variation:KLogVariationSuccess completion:nil]; +} + +- (void)resizeTextView { + CGFloat fixedWidth = CGRectGetWidth([UIScreen mainScreen].bounds) - LeadingPaddingOfAnnotationTextView * 2; + CGSize newSize = [self sizeThatFits:CGSizeMake(fixedWidth, MAXFLOAT)]; + CGRect newFrame = self.frame; + newFrame.size = CGSizeMake(fixedWidth, newSize.height); + self.frame = newFrame; +} + +- (void)commitButtonPressed:(UIButton *)sender { + if (self.annotationTextViewDelegate && [self.annotationTextViewDelegate respondsToSelector:@selector(annotationTextViewDidFinishEditing:)]) { + [self.annotationTextViewDelegate annotationTextViewDidFinishEditing:self]; + } +} + +- (void)cancelButtonPressed:(UIButton *)sender { + if (self.annotationTextViewDelegate && [self.annotationTextViewDelegate respondsToSelector:@selector(annotationTextViewDidCancel:)]) { + [self.annotationTextViewDelegate annotationTextViewDidCancel:self]; + } + [self removeFromSuperview]; +} + +- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { + return YES; +} + +#pragma mark - UITextViewDelegate +- (void)textViewDidChange:(UITextView *)textView { + [self resizeTextView]; +} + +- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text { + + // for faking the place holder in UITextView + if (!startTyping) { + startTyping = YES; + self.text = nil; + } + + // handle done button from keyboard + if ([text isEqualToString:@"\n"]) { + + if (!textView.text.length) return NO; + [self commitToMove]; + } + return YES; +} + +@end + +#pragma mark - OTRemoteAnnotationTextView +@interface OTRemoteAnnotationTextView() +@property (nonatomic) NSString *remoteGUID; +@end + +@implementation OTRemoteAnnotationTextView + +- (instancetype)initWithTextColor:(UIColor *)textColor + remoteGUID:(NSString *)remoteGUID { + + if (self = [super initWithTextColor:textColor]) { + _remoteGUID = remoteGUID; + } + return self; +} + +- (instancetype)initWithText:(NSString *)text + textColor:(UIColor *)textColor + fontSize:(CGFloat)fontSize + remoteGUID:(NSString *)remoteGUID{ + + if (self = [super initWithText:text textColor:textColor fontSize:fontSize]) { + _remoteGUID = remoteGUID; + } + return self; +} + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationTextView_Gesture.h b/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationTextView_Gesture.h new file mode 100644 index 0000000..affc270 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationTextView_Gesture.h @@ -0,0 +1,22 @@ +// +// OTAnnotationTextView_Gesture.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +@interface OTAnnotationTextView () + +@property (nonatomic) CGPoint referenceCenter; +@property (nonatomic) CGPoint currentCenter; +@property (nonatomic) CGAffineTransform referenceTransform; +@property (nonatomic) CGAffineTransform currentTransform; +@property (nonatomic) UIPanGestureRecognizer *onViewPanRecognizer; +@property (nonatomic) UIPinchGestureRecognizer *onViewPinchRecognizer; +@property (nonatomic) UIRotationGestureRecognizer *onViewRotationRecognizer; + +// external button +@property (nonatomic) CGPoint referencePoint; +@property (nonatomic) CGFloat referenceDistance; +@property (nonatomic) UIPanGestureRecognizer *onButtonZoomRecognizer; +@property (nonatomic) UIPanGestureRecognizer *onButtonRotateRecognizer; +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationView.h b/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationView.h new file mode 100644 index 0000000..a757d9f --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationView.h @@ -0,0 +1,104 @@ +// +// AnnotationView.h +// +// Copyright © 2016 Tokbox. All rights reserved. +// + +#import "OTAnnotatable.h" +#import "OTAnnotationDataManager.h" +#import "OTAnnotationPath.h" + +@class OTAnnotationView; +@protocol OTAnnotationViewDelegate + +- (void)annotationView:(OTAnnotationView *)annotationView + touchBegan:(UITouch *)touch + withEvent:(UIEvent *)event; + +- (void)annotationView:(OTAnnotationView *)annotationView + touchMoved:(UITouch *)touch + withEvent:(UIEvent *)event; + +- (void)annotationView:(OTAnnotationView *)annotationView + touchEnded:(UITouch *)touch + withEvent:(UIEvent *)event; + +@end + +@interface OTAnnotationView : UIView + +/** + * The associated annotation data manager. + */ +@property (readonly, nonatomic) OTAnnotationDataManager *annotationDataManager; + +@property (weak, nonatomic) id annotationViewDelegate; + +/** + * Initializes an annotataion view with the specified frame rectangle. + * + * @param frame The frame rectangle for the view, measured in points. + * + * @return A new OTAnnotationView object + */ +- (instancetype)initWithFrame:(CGRect)frame; + +/** + * Draw the given annotatable object on the annotation view. + * + * @param annotatable The annotatable object to be drawn. + */ +- (void)addAnnotatable:(id)annotatable; + +/** + * Erase the last annotatable object on the annotation view. + * + * @return The last annotatable object, nil otherwise. + */ +- (id)undoAnnotatable; + +/** + * Erase the last remote annotatable object on the annotation view. + * + * @return The last remote annotatableobject, nil otherwise. + */ +- (id)undoRemoteAnnotatable; + +/** + * Erase a remote annotatable object with guid. + * + * @param The guid associated with the remote annotatable object to be removed. + */ +- (void)removeRemoteAnnotatableWithGUID:(NSString *)guid; + +/** + * Clear all annotatable objects from the annotation view + */ +- (void)removeAllAnnotatables; + +/** + * Clear all remote annotatable objects from the annotation view + */ +- (void)removeAllRemoteAnnotatables; + +/** + * The remote annotatable object that being used, uncommitted. If a new remoteAnnotatable is being set, the last remoteAnnotatable object will be commited. + */ +@property (nonatomic) id remoteAnnotatable; + +/** + * The current annotatable object that being used, uncommitted. If a new currentAnnotatable is being set, the last annotatable object will be commited. + */ +@property (nonatomic) id currentAnnotatable; + +/** + * Commit the current annotatable object + */ +- (void)commitCurrentAnnotatable; + +/** + * Capture the current screen + */ +- (UIImage *)captureScreenWithView:(UIView *)view; + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationView.m b/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationView.m new file mode 100644 index 0000000..2901a1f --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationNative/OTAnnotationView.m @@ -0,0 +1,293 @@ +// +// AnnotationView.m +// +// Copyright © 2016 Tokbox. All rights reserved. +// + +#import "OTAnnotationView.h" + +#import "AnnLoggingWrapper.h" + +#import "OTAnnotator.h" + +#import "Constants.h" + +@interface OTAnnotationView() { + UIColor *previousStrokeColor; // this is for memorizing the previous stroke color +} +@property (nonatomic) OTAnnotationTextView *currentEditingTextView; +@property (nonatomic) OTAnnotationPath *localDrawPath; +@property (nonatomic) OTAnnotationDataManager *annotationDataManager; +@end + +@implementation OTAnnotationView + +- (instancetype)init { + return [[OTAnnotationView alloc] initWithFrame:CGRectZero]; +} + +- (instancetype)initWithFrame:(CGRect)frame { + + if (self = [super initWithFrame:frame]) { + + [[AnnLoggingWrapper sharedInstance].logger logEventAction:KLogActionInitialize variation:KLogVariationSuccess completion:nil]; + + _annotationDataManager = [[OTAnnotationDataManager alloc] init]; + [self setBackgroundColor:[UIColor clearColor]]; + } + return self; +} + +- (void)setRemoteAnnotatable:(id)remoteAnnotatable { + + if ([remoteAnnotatable isKindOfClass:[OTRemoteAnnotationPath class]]) { + _remoteAnnotatable = remoteAnnotatable; + [self addAnnotatable:remoteAnnotatable]; + [[AnnLoggingWrapper sharedInstance].logger logEventAction:KLogActionFreeHand variation:KLogVariationSuccess completion:nil]; + } +} + +- (void)setCurrentAnnotatable:(id)annotatable { + + if ([annotatable isKindOfClass:[OTAnnotationPath class]]) { + _currentAnnotatable = annotatable; + _localDrawPath = (OTAnnotationPath *)annotatable; + [self addAnnotatable:annotatable]; + [[AnnLoggingWrapper sharedInstance].logger logEventAction:KLogActionFreeHand variation:KLogVariationSuccess completion:nil]; + } + else if ([annotatable isKindOfClass:[OTAnnotationTextView class]]) { + _currentAnnotatable = annotatable; + _currentEditingTextView = (OTAnnotationTextView *)annotatable; + [self addAnnotatable:annotatable]; + } + else { + [self commitCurrentAnnotatable]; + } +} + +- (void)addAnnotatable:(id)annotatable { + + if (!annotatable || ![annotatable conformsToProtocol:@protocol(OTAnnotatable)]) { + return; + } + + if ([annotatable isKindOfClass:[OTAnnotationPath class]]) { + OTAnnotationPath *path = (OTAnnotationPath *)annotatable; + if (path.points.count != 0) { + [path drawWholePath]; + } + [self.annotationDataManager addAnnotatable:path]; + [self setNeedsDisplay]; + } + else if ([annotatable isKindOfClass:[OTAnnotationTextView class]]) { + + OTAnnotationTextView *textfield = (OTAnnotationTextView *)annotatable; + [self addSubview:textfield]; + [self.annotationDataManager addAnnotatable:textfield]; + } +} + +- (id)undoAnnotatable { + + id annotatable = [self.annotationDataManager peakOfAnnotatable]; + if ([annotatable isMemberOfClass:[OTAnnotationPath class]]) { + id annotatableToRemove = [self.annotationDataManager pop]; + [self setNeedsDisplay]; + [[AnnLoggingWrapper sharedInstance].logger logEventAction:KLogActionErase variation:KLogVariationSuccess completion:nil]; + return annotatableToRemove; + } + else if ([annotatable isMemberOfClass:[OTAnnotationTextView class]]) { + [self.annotationDataManager pop]; + OTAnnotationTextView *textfield = (OTAnnotationTextView *)annotatable; + [textfield removeFromSuperview]; + [[AnnLoggingWrapper sharedInstance].logger logEventAction:KLogActionErase variation:KLogVariationSuccess completion:nil]; + return textfield; + } + return nil; +} + +- (void)removeRemoteAnnotatableWithGUID:(NSString *)guid { + + __block id annotationToRemove; + + [self.annotationDataManager.annotatable enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id _Nonnull annotatable, NSUInteger idx, BOOL * _Nonnull stop) { + if (guid) { + if ([annotatable isMemberOfClass:[OTRemoteAnnotationPath class]]) { + OTRemoteAnnotationPath *path = (OTRemoteAnnotationPath *)annotatable; + if ([path.remoteGUID isEqualToString:guid]) { + annotationToRemove = path; + *stop = YES; + } + } + } + else { + if ([annotatable isMemberOfClass:[OTRemoteAnnotationTextView class]]) { + OTRemoteAnnotationTextView *textView = (OTRemoteAnnotationTextView *)annotatable; + annotationToRemove = textView; + [textView removeFromSuperview]; + *stop = YES; + } + } + }]; + + [self.annotationDataManager remove:annotationToRemove]; + if (guid) { + [self setNeedsDisplay]; + } + [[AnnLoggingWrapper sharedInstance].logger logEventAction:KLogActionErase variation:KLogVariationSuccess completion:nil]; +} + +- (id)undoRemoteAnnotatable { + + id annotatable = [self.annotationDataManager peakOfRemoteAnnotatable]; + if ([annotatable isMemberOfClass:[OTRemoteAnnotationPath class]]) { + id annotatableToRemove = [self.annotationDataManager popRemote]; + [self setNeedsDisplay]; + [[AnnLoggingWrapper sharedInstance].logger logEventAction:KLogActionErase variation:KLogVariationSuccess completion:nil]; + return annotatableToRemove; + } + else if ([annotatable isMemberOfClass:[OTRemoteAnnotationTextView class]]) { + [self.annotationDataManager popRemote]; + OTAnnotationTextView *textfield = (OTAnnotationTextView *)annotatable; + [textfield removeFromSuperview]; + [[AnnLoggingWrapper sharedInstance].logger logEventAction:KLogActionErase variation:KLogVariationSuccess completion:nil]; + return textfield; + } + return nil; +} + +- (void)removeAllAnnotatables { + + [self.annotationDataManager popAll]; + [self setNeedsDisplay]; + [[AnnLoggingWrapper sharedInstance].logger logEventAction:KLogActionEraseAll variation:KLogVariationSuccess completion:nil]; +} + +- (void)removeAllRemoteAnnotatables { + + [self.annotationDataManager popRemoteAll]; + [self setNeedsDisplay]; + [[AnnLoggingWrapper sharedInstance].logger logEventAction:KLogActionEraseAll variation:KLogVariationSuccess completion:nil]; +} + +- (void)commitCurrentAnnotatable { + + if ([self.currentAnnotatable isKindOfClass:[OTAnnotationPath class]]) { + OTAnnotationPath *path = (OTAnnotationPath *)self.currentAnnotatable; + previousStrokeColor = path.strokeColor; + } + else if ([self.currentAnnotatable isKindOfClass:[OTAnnotationTextView class]]) { + OTAnnotationTextView *textView = (OTAnnotationTextView *)self.currentAnnotatable; + previousStrokeColor = textView.textColor; + } + + if ([self.currentAnnotatable respondsToSelector:@selector(commit)]) { + [self.currentAnnotatable commit]; + } + _currentAnnotatable = nil; + _localDrawPath = nil; + _currentEditingTextView = nil; +} + +- (UIImage *)captureFullScreen { + [[AnnLoggingWrapper sharedInstance].logger logEventAction:KLogActionScreenCapture variation:KLogVariationSuccess completion:nil]; + CGRect screenRect = [[UIScreen mainScreen] bounds]; + UIGraphicsBeginImageContext(screenRect.size); + CGContextRef ctx = UIGraphicsGetCurrentContext(); + CGContextFillRect(ctx, screenRect); + [self.window.layer renderInContext:ctx]; + UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return newImage; +} + +- (UIImage *)captureScreenWithView:(UIView *)view { + + [[AnnLoggingWrapper sharedInstance].logger logEventAction:KLogActionScreenCapture variation:KLogVariationSuccess completion:nil]; + if (view == [UIApplication sharedApplication].keyWindow.rootViewController.view) { + return [self captureFullScreen]; + } + else { + UIGraphicsBeginImageContext(view.frame.size); + [view.layer renderInContext:UIGraphicsGetCurrentContext()]; + UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return outputImage; + } +} + +- (void)drawRect:(CGRect)rect { + [self.annotationDataManager.annotatable enumerateObjectsUsingBlock:^(id annotatable, NSUInteger idx, BOOL *stop) { + + if ([annotatable isKindOfClass:[OTAnnotationPath class]]) { + OTAnnotationPath *path = (OTAnnotationPath *)annotatable; + [path.strokeColor setStroke]; + [path stroke]; + } + }]; +} + +#pragma mark - UIResponder +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + + if (_currentEditingTextView) return; + if (!_currentAnnotatable) return; + + if (!_localDrawPath || _localDrawPath.points.count != 0) { + + if (_localDrawPath.strokeColor) { + self.currentAnnotatable = [[OTAnnotationPath alloc] initWithStrokeColor:_localDrawPath.strokeColor]; + } + else if (previousStrokeColor) { + self.currentAnnotatable = [[OTAnnotationPath alloc] initWithStrokeColor:previousStrokeColor]; + } + } + UITouch *touch = [touches anyObject]; + + if (self.annotationViewDelegate) { + [self.annotationViewDelegate annotationView:self touchBegan:touch withEvent:event]; + } + + CGPoint touchPoint = [touch locationInView:touch.view]; + OTAnnotationPoint *annotationPoint = [OTAnnotationPoint pointWithX:touchPoint.x andY:touchPoint.y]; + [_localDrawPath startAtPoint:annotationPoint]; + [[AnnLoggingWrapper sharedInstance].logger logEventAction:KLogActionStartDrawing variation:KLogVariationSuccess completion:nil]; +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { + + if (_localDrawPath) { + UITouch *touch = [touches anyObject]; + + if (self.annotationViewDelegate) { + [self.annotationViewDelegate annotationView:self touchMoved:touch withEvent:event]; + } + + CGPoint touchPoint = [touch locationInView:touch.view]; + CGPoint previousPoint = [touch previousLocationInView:touch.view]; + OTAnnotationPoint *prevPoint = [OTAnnotationPoint pointWithX:previousPoint.x andY:previousPoint.y]; + OTAnnotationPoint *annotationPoint = [OTAnnotationPoint pointWithX:touchPoint.x andY:touchPoint.y]; + [_localDrawPath drawCurveFrom:prevPoint to:annotationPoint]; + [self setNeedsDisplay]; + } +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + + if (_localDrawPath) { + UITouch *touch = [touches anyObject]; + + if (self.annotationViewDelegate) { + [self.annotationViewDelegate annotationView:self touchEnded:touch withEvent:event]; + } + + CGPoint touchPoint = [touch locationInView:touch.view]; + OTAnnotationPoint *annotationPoint = [OTAnnotationPoint pointWithX:touchPoint.x andY:touchPoint.y]; + [_localDrawPath drawToPoint:annotationPoint]; + [self setNeedsDisplay]; + [[AnnLoggingWrapper sharedInstance].logger logEventAction:KLogActionEndDrawing variation:KLogVariationSuccess completion:nil]; + } +} + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/ColorPicker/OTAnnotationColorPickerView.h b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/ColorPicker/OTAnnotationColorPickerView.h new file mode 100644 index 0000000..6569f0d --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/ColorPicker/OTAnnotationColorPickerView.h @@ -0,0 +1,68 @@ +// +// OTAnnotationColorPickerView.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import + +@class OTAnnotationColorPickerView; + +@interface OTAnnotationColorPickerViewButton : UIButton +- (instancetype)initWithWhiteBorder; +@end + +/** + * The delegate of an OTAnnotationColorPickerView object must conform to the OTAnnotationColorPickerViewProtocol. + * Methods of the protocol allow the delegate to notify when color is selected. + */ +@protocol OTAnnotationColorPickerViewProtocol + +/** + * Notifies the delegate that the color picker view finished picking a color button from the avaiable colors. + * + * @param colorPickerView A color picker object containing the color picker view from which the user selects the desired color. + * @param button The button that was selected with the corresponding color + * @param selectedColor The selected color. + */ +- (void)colorPickerView:(OTAnnotationColorPickerView *)colorPickerView + didSelectColorButton:(OTAnnotationColorPickerViewButton *)button + selectedColor:(UIColor *)selectedColor; +@end + +typedef NS_ENUM(NSUInteger, OTAnnotationColorPickerViewOrientation) { + OTAnnotationColorPickerViewOrientationPortrait = 0, + OTAnnotationColorPickerViewOrientationLandscape +}; + +@interface OTAnnotationColorPickerView : UIView + +/** + * Creates a new color picker instance with the specified frame size. The color picker is initialized with the colors available for selection. + * + * @param frame The frame size for the color toolbar. + * + * @return An initialized color picker object. + */ +- (instancetype)initWithFrame:(CGRect)frame; + +/** + * The currently selected color. + */ +@property (readonly, nonatomic) UIColor *selectedColor; + +/** + * The orientation of this color picker view. + * The default value is OTAnnotationColorPickerViewOrientationPortrait. + * Set it to OTAnnotationColorPickerViewOrientationLandscape to have this color picker view vertical. + */ +@property (nonatomic) OTAnnotationColorPickerViewOrientation annotationColorPickerViewOrientation; + +/** + * The object that acts as the delegate of the color picker view. + * + * The delegate must adopt the OTAnnotationColorPickerViewProtocol protocol. The delegate is not retained. + */ +@property (nonatomic) id delegate; + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/ColorPicker/OTAnnotationColorPickerView.m b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/ColorPicker/OTAnnotationColorPickerView.m new file mode 100644 index 0000000..0dff636 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/ColorPicker/OTAnnotationColorPickerView.m @@ -0,0 +1,222 @@ +// +// OTAnnotationColorPickerView.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTAnnotationColorPickerView.h" +#import "UIButton+AutoLayoutHelper.h" +#import "UIView+Helper.h" +#import "Constants.h" +#import "LHToolbar.h" + +#pragma mark - ScreenShareColorPickerViewButton +@interface OTAnnotationColorPickerViewButton() +@property (nonatomic, strong) UIView *container; +@property (nonatomic, assign) BOOL whiteBorderIsOn; +@property (nonatomic, strong) NSArray *commonConstraints; +@property (nonatomic, strong) NSArray *bigConstraints; +@property (nonatomic, strong) NSArray *smallConstraints; +@end + +@implementation OTAnnotationColorPickerViewButton + +- (instancetype)init { + if (self = [super init]) { + self.translatesAutoresizingMaskIntoConstraints = NO; + self.backgroundColor = [UIColor colorWithRed:68.0/255.0f green:140.0/255.0f blue:230.0/255.0f alpha:1.0]; + + _container = [[UIView alloc] initWithFrame:CGRectZero]; + _container.layer.borderWidth = 2.0f; + _container.layer.borderColor = [UIColor clearColor].CGColor; + _container.clipsToBounds = YES; + _container.userInteractionEnabled = NO; + _container.translatesAutoresizingMaskIntoConstraints = NO; + _container.backgroundColor = ((UIColor *)[OTAnnotationColorPickerView.class blueColor]); + } + return self; +} + +- (instancetype)initWithWhiteBorder { + if (self = [self init]) { + _whiteBorderIsOn = true; + } + return self; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + self.container.layer.cornerRadius = CGRectGetWidth(self.container.bounds) / 2.0; +} + +- (void)didMoveToSuperview { + if (!self.superview) return; + if (![self.subviews containsObject:self.container]) { + [self addSubview:self.container]; + } + [self addCenterConstraints]; + + [NSLayoutConstraint activateConstraints:self.commonConstraints]; + + [self updateSelected:self.isSelected]; +} + +- (void)setBackgroundColor:(UIColor *)backgroundColor { + if (!_container) return; + self.container.backgroundColor = backgroundColor; +} + +- (UIColor *)backgroundColor { + if (!_container) return [UIColor clearColor]; + return self.container.backgroundColor; +} + +- (void)setSelected:(BOOL)selected { + [super setSelected:selected]; + [self updateSelected:selected]; +} + +- (void)updateSelected:(BOOL)selected { + if (selected || self.whiteBorderIsOn) { + self.container.layer.borderColor = [UIColor whiteColor].CGColor; + } + else { + self.container.layer.borderColor = [UIColor clearColor].CGColor; + } + + if (self.whiteBorderIsOn) { + [NSLayoutConstraint deactivateConstraints:self.bigConstraints]; + [NSLayoutConstraint activateConstraints:self.smallConstraints]; + } + else if (selected) { + [NSLayoutConstraint activateConstraints:self.bigConstraints]; + [NSLayoutConstraint deactivateConstraints:self.smallConstraints]; + } + else { + [NSLayoutConstraint deactivateConstraints:self.bigConstraints]; + [NSLayoutConstraint activateConstraints:self.smallConstraints]; + } +} + +- (NSArray *)commonConstraints { + if (!_commonConstraints) { + _commonConstraints = @[ + [NSLayoutConstraint constraintWithItem:self.container attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0], + [NSLayoutConstraint constraintWithItem:self.container attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0], + [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0] + ]; + } + return _commonConstraints; +} + +- (NSArray *)bigConstraints { + if (!_bigConstraints) { + _bigConstraints = @[ + [NSLayoutConstraint constraintWithItem:self.container attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeWidth multiplier:0.8 constant:0.0], + [NSLayoutConstraint constraintWithItem:self.container attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeHeight multiplier:0.8 constant:0.0] + ]; + } + return _bigConstraints; +} + +- (NSArray *)smallConstraints { + if (!_smallConstraints) { + _smallConstraints = @[ + [NSLayoutConstraint constraintWithItem:self.container attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeWidth multiplier:0.6 constant:0.0], + [NSLayoutConstraint constraintWithItem:self.container attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeHeight multiplier:0.6 constant:0.0] ]; + } + return _smallConstraints; +} + +@end + + +#pragma mark - ScreenShareColorPickerView +@interface OTAnnotationColorPickerView() +@property (nonatomic) OTAnnotationColorPickerViewButton *selectedButton; +@property (nonatomic) NSDictionary *colorDict; +@property (nonatomic) LHToolbar *colorToolbar; +@end + +@implementation OTAnnotationColorPickerView + +- (void)setAnnotationColorPickerViewOrientation:(OTAnnotationColorPickerViewOrientation)annotationColorPickerViewOrientation { + _annotationColorPickerViewOrientation = annotationColorPickerViewOrientation; + if (annotationColorPickerViewOrientation == OTAnnotationColorPickerViewOrientationPortrait) { + self.colorToolbar.orientation = LHToolbarOrientationHorizontal; + } + else if (annotationColorPickerViewOrientation == OTAnnotationColorPickerViewOrientationLandscape) { + self.colorToolbar.orientation = LHToolbarOrientationVertical; + } + [self.colorToolbar reloadToolbar]; +} + +- (UIColor *)selectedColor { + if (!self.selectedButton) return nil; + return self.selectedButton.backgroundColor; +} + +- (instancetype)initWithFrame:(CGRect)frame { + + if (self = [super initWithFrame:frame]) { + _colorDict = @{ + @1: [self.class blueColor], + @2: [UIColor colorWithRed:179.0/255.0f green:0/255.0f blue:223.0/255.0f alpha:1.0], + @3: [UIColor redColor], + @4: [UIColor colorWithRed:245.0/255.0f green:152.0/255.0f blue:0/255.0f alpha:1.0], + @5: [UIColor colorWithRed:247.0/255.0f green:234.0/255.0f blue:0/255.0f alpha:1.0], + @6: [UIColor colorWithRed:101.0/255.0f green:210.0/255.0f blue:0.0/255.0f alpha:1.0], + @7: [UIColor blackColor], + @8: [UIColor grayColor], + @9: [UIColor whiteColor] + }; + + + _colorToolbar = [[LHToolbar alloc] initWithNumberOfItems:self.colorDict.count]; + _colorToolbar.translatesAutoresizingMaskIntoConstraints = NO; + [self addSubview:_colorToolbar]; + [_colorToolbar addAttachedLayoutConstantsToSuperview]; + self.backgroundColor = [UIColor colorWithRed:38.0 / 255.0 green:38.0 / 255.0 blue:38.0 / 255.0 alpha:1.0]; + [self configureColorPickerButtons]; + } + return self; +} + ++ (UIColor *)blueColor { + return [UIColor colorWithRed:68.0/255.0f green:140.0/255.0f blue:230.0/255.0f alpha:1.0]; +} + +- (void)configureColorPickerButtons { + + // first button + OTAnnotationColorPickerViewButton *button = [[OTAnnotationColorPickerViewButton alloc] init]; + [button.container setBackgroundColor:self.colorDict[@(1)]]; + [button addTarget:self action:@selector(colorButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; + [self.colorToolbar setContentView:button atIndex:0]; + self.selectedButton = button; + + for (NSUInteger i = 2; i < 10; i++) { + + OTAnnotationColorPickerViewButton *button = [[OTAnnotationColorPickerViewButton alloc] init]; + [button.container setBackgroundColor:self.colorDict[@(i)]]; + [button addTarget:self action:@selector(colorButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; + [self.colorToolbar setContentView:button atIndex:i - 1]; + } + [self.colorToolbar reloadToolbar]; + [self.selectedButton setSelected:YES]; +} + +- (void)colorButtonPressed:(OTAnnotationColorPickerViewButton *)sender { + [self.selectedButton setSelected:NO]; + self.selectedButton = sender; + [self.selectedButton setSelected:YES]; + + if (self.delegate) { + [self.delegate colorPickerView:self + didSelectColorButton:self.selectedButton + selectedColor:self.selectedColor]; + } +} + + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/OTAnnotationScrollView.h b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/OTAnnotationScrollView.h new file mode 100644 index 0000000..bc31baf --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/OTAnnotationScrollView.h @@ -0,0 +1,77 @@ +// +// OTAnnotationScrollView.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTAnnotationView.h" +#import "OTAnnotationTextView.h" +#import "OTAnnotationToolbarView.h" +#import "OTAnnotationDataManager.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface OTAnnotationScrollView : UIView + +/** + * A boolean value to indicate whether the annotation scoll view is annotatable. + */ +@property (nonatomic, getter = isAnnotatable) BOOL annotatable; + +/** + * The associated annotation view. + */ +@property (readonly, nonatomic) OTAnnotationView *annotationView; + +/** + * The associated scroll view that enables annotating on scrollable content. + * + * @discussion Specifying the contentSize of this scroll view will install width/height constraints internally to make annotationView large enough to annotate. It's the same size with the annotationView otherwise. + */ +@property (readonly, nonatomic) UIScrollView *scrollView; + +/** + * Initialize an annotataion scroll view. + * + * @return A new OTAnnotationScrollView object. + */ +- (instancetype)init; + +/** + * Add the annotatable content with a given view. + * + * @param view The content view. + * + * @discussion Scrolling will not be enabled until you specify the contentSize of the associated scroll view. + */ +- (void)addContentView:(UIView *)view; + +#pragma mark - Tool bar +/** + * A pre-defined toolbar UI that has all essential operations to annotate. + * + * @discussion This is optional, all operations can be performed programmatically. This will be nil until initializeToolbarView gets called. + */ +@property (nullable, readonly, nonatomic) OTAnnotationToolbarView *toolbarView; + +/** + * Initialize the associated toolbar view that has iOS specific features. + */ +- (void)initializeToolbarView; + +/** + * Initialize the associated toolbar view that has all platform compatible features. + */ +- (void)initializeUniversalToolbarView; + +#pragma mark - Annotation +/** + * Add an adjusted text annotation to the annotation scroll view + * + * @param annotationTextView The text annotation + */ +- (void)addTextAnnotation:(OTAnnotationTextView *)annotationTextView; + +@end + +NS_ASSUME_NONNULL_END diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/OTAnnotationScrollView.m b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/OTAnnotationScrollView.m new file mode 100644 index 0000000..de06dde --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/OTAnnotationScrollView.m @@ -0,0 +1,156 @@ +// +// OTAnnotationScrollView.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTAnnotationScrollView.h" +#import "OTAnnotationPath.h" +#import "OTAnnotationView.h" +#import "OTAnnotationTextView.h" + +#import "OTAnnotationEditTextViewController.h" +#import "OTAnnotationScreenCaptureViewController.h" + +#import "OTAnnotationScrollView_Private.h" +#import "OTAnnotationToolbarView_Private.h" + +#import "UIViewController+Helper.h" +#import "UIView+Helper.h" +#import "AnnLoggingWrapper.h" + +#import "Constants.h" + +@interface OTAnnotationScrollView() +@property (nonatomic) OTAnnotationView *annotationView; +@property (nonatomic) UIView *scrollContentView; +@property (nonatomic) UIScrollView *scrollView; +@property (nonatomic) OTAnnotationToolbarView *toolbarView; + +@property (nonatomic) NSLayoutConstraint *annotationScrollViewWidth; +@property (nonatomic) NSLayoutConstraint *annotationScrollViewHeigth; +@end + +@implementation OTAnnotationScrollView +- (void)setAnnotatable:(BOOL)annotatable { + _annotatable = annotatable; + + if (!_annotatable) { + [self.annotationView setCurrentAnnotatable:nil]; + } + + self.scrollView.scrollEnabled = !_annotatable; +} + +- (instancetype)init { + + if (self = [super init]) { + + // scroll view + _scrollView = [[UIScrollView alloc] init]; + _scrollView.translatesAutoresizingMaskIntoConstraints = NO; + _scrollView.delegate = self; + [self addSubview:_scrollView]; + [_scrollView addAttachedLayoutConstantsToSuperview]; + + // scroll content view + _scrollContentView = [[UIView alloc] init]; + _scrollContentView.translatesAutoresizingMaskIntoConstraints = NO; + [_scrollView addSubview:_scrollContentView]; + [NSLayoutConstraint constraintWithItem:_scrollContentView + attribute:NSLayoutAttributeCenterX + relatedBy:NSLayoutRelationEqual + toItem:_scrollView + attribute:NSLayoutAttributeCenterX + multiplier:1.0 + constant:0.0].active = YES; + [NSLayoutConstraint constraintWithItem:_scrollContentView + attribute:NSLayoutAttributeCenterY + relatedBy:NSLayoutRelationEqual + toItem:_scrollView + attribute:NSLayoutAttributeCenterY + multiplier:1.0 + constant:0.0].active = YES; + _annotationScrollViewWidth = [NSLayoutConstraint constraintWithItem:_scrollContentView + attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:self.scrollView.contentSize.width]; + _annotationScrollViewWidth.active = YES; + _annotationScrollViewHeigth = [NSLayoutConstraint constraintWithItem:_scrollContentView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:self.scrollView.contentSize.height]; + _annotationScrollViewHeigth.active = YES; + + // annotation view + _annotationView = [[OTAnnotationView alloc] init]; + _annotationView.translatesAutoresizingMaskIntoConstraints = NO; + [_scrollContentView addSubview:_annotationView]; + [_annotationView addAttachedLayoutConstantsToSuperview]; + + self.annotatable = NO; + + [self.scrollView addObserver:self + forKeyPath:@"contentSize" + options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld + context:nil]; + } + return self; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { + + if (object == self.scrollView && [keyPath isEqualToString:@"contentSize"]) { + self.annotationScrollViewWidth.constant = self.scrollView.contentSize.width; + self.annotationScrollViewHeigth.constant = self.scrollView.contentSize.height; + } +} + +- (void)dealloc { + [self.scrollView removeObserver:self forKeyPath:@"contentSize"]; +} + +- (void)addSubview:(UIView *)view { + if ([view conformsToProtocol:@protocol(OTAnnotatable)] && !self.annotatable) return; + [super addSubview:view]; +} + +- (void)addContentView:(UIView *)view { + [self.scrollContentView insertSubview:view belowSubview:self.annotationView]; +} + +- (void)initializeToolbarView { + CGRect mainBounds = [UIScreen mainScreen].bounds; + self.toolbarView = [[OTAnnotationToolbarView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(mainBounds), DefaultToolbarHeight) + annotationScrollView:self]; + [[AnnLoggingWrapper sharedInstance].logger logEventAction:KLogActionUseToolbar variation:KLogVariationSuccess completion:nil]; +} + +- (void)initializeUniversalToolbarView { + CGRect mainBounds = [UIScreen mainScreen].bounds; + self.toolbarView = [[OTAnnotationToolbarView alloc] initUniversalWithFrame:CGRectMake(0, 0, CGRectGetWidth(mainBounds), DefaultToolbarHeight) + annotationScrollView:self]; + [[AnnLoggingWrapper sharedInstance].logger logEventAction:KLogActionUseToolbar variation:KLogVariationSuccess completion:nil]; +} + +- (void)addTextAnnotation:(OTAnnotationTextView *)annotationTextView { + annotationTextView.frame = CGRectMake(self.scrollView.contentOffset.x + LeadingPaddingOfAnnotationTextView, + self.scrollView.contentOffset.y + annotationTextView.frame.origin.y, + CGRectGetWidth(annotationTextView.bounds), + CGRectGetHeight(annotationTextView.bounds)); + [self.annotationView setCurrentAnnotatable:annotationTextView]; +} + +#pragma mark - UIScrollViewDelegate + +- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { + return self.scrollContentView; +} + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/OTAnnotationScrollView_Private.h b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/OTAnnotationScrollView_Private.h new file mode 100644 index 0000000..4ce3de1 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/OTAnnotationScrollView_Private.h @@ -0,0 +1,13 @@ +// +// OTAnnotationScrollView_Private.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import + +@interface OTAnnotationScrollView () + + + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/OTFullScreenAnnotationViewController.h b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/OTFullScreenAnnotationViewController.h new file mode 100644 index 0000000..8fce576 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/OTFullScreenAnnotationViewController.h @@ -0,0 +1,18 @@ +// +// OTFullScreenAnnotationViewController.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import + +@interface OTFullScreenAnnotationViewController : UIViewController + +/** + * Initializes a new `OTFullScreenAnnotationViewController` instsance. + * + * @return A new `OTFullScreenAnnotationViewController` instsance. + */ +- (instancetype)init; + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/OTFullScreenAnnotationViewController.m b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/OTFullScreenAnnotationViewController.m new file mode 100644 index 0000000..2d487f3 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/OTFullScreenAnnotationViewController.m @@ -0,0 +1,44 @@ +// +// OTFullScreenAnnotationViewController.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTFullScreenAnnotationViewController.h" +#import "OTAnnotationScrollView.h" +#import "OTAnnotationScrollView_Private.h" + +@interface OTFullScreenAnnotationViewController () + +@end + +@implementation OTFullScreenAnnotationViewController + +- (instancetype)init { + + if (self = [super init]) { + + OTAnnotationScrollView *annotationScrollView = [[OTAnnotationScrollView alloc] init]; + annotationScrollView.frame = CGRectMake(0, 0, CGRectGetWidth([UIScreen mainScreen].bounds), CGRectGetHeight([UIScreen mainScreen].bounds) - 50); + annotationScrollView.scrollView.contentSize = CGSizeMake(CGRectGetWidth([UIScreen mainScreen].bounds), CGRectGetHeight([UIScreen mainScreen].bounds) - 50); + + [annotationScrollView initializeToolbarView]; + annotationScrollView.toolbarView.frame = CGRectMake(0, CGRectGetHeight([UIScreen mainScreen].bounds) - 50, CGRectGetWidth([UIScreen mainScreen].bounds), 50); + annotationScrollView.toolbarView.toolbarViewDataSource = self; + + [self.view addSubview:annotationScrollView]; + [self.view addSubview:annotationScrollView.toolbarView]; + + self.providesPresentationContextTransitionStyle = YES; + self.definesPresentationContext = YES; + self.modalPresentationStyle = UIModalPresentationOverCurrentContext; + } + return self; +} + +#pragma mark - OTAnnotationToolbarViewDataSource +- (UIView *)annotationToolbarViewForRootViewForScreenShot:(OTAnnotationToolbarView *)toolbarView { + return [UIApplication sharedApplication].keyWindow.rootViewController.view; +} + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/ScreenCapture/OTAnnotationScreenCaptureView.h b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/ScreenCapture/OTAnnotationScreenCaptureView.h new file mode 100644 index 0000000..dcaa047 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/ScreenCapture/OTAnnotationScreenCaptureView.h @@ -0,0 +1,51 @@ +// +// OTAnnotationScreenCaptureView.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import + +@interface OTAnnotationScreenCaptureModel : NSObject + +/** + * The image resulting from the capture process of the current annotation on the screen. + */ +@property (readonly, nonatomic) UIImage *sharedImage; + +/** + * Initialize a new screen capture model instance with the capture image and the date when that + * image was captured. + * + * @param sharedImage The image captured from the current annotation screen. + * @param sharedDate The date the image was captured. + * + * @return the initialized screen capture model object. + */ +- (instancetype)initWithSharedImage:(UIImage *)sharedImage + sharedDate:(NSDate *)sharedDate; +@end + +@interface OTAnnotationScreenCaptureView : UIView + +/** + * Buttons from the view enabling the user to share or save the captured image. + */ +@property (readonly, weak, nonatomic) UIButton *shareButton; +@property (readonly, weak, nonatomic) UIButton *saveButton; + +/** + * Update the view with the shared image from the screen capture model object, and set the + * weight in kb of that image. + * + * @param shareModel An object containing the shared image. + */ +- (void)updateWithShareModel:(OTAnnotationScreenCaptureModel *)shareModel; + +/** + * Sets whether saving the image is enabled for the save button. + * + * @param enable YES if the save button is enabled; NO otherwise. + */ +- (void)enableSaveImageButton:(BOOL)enable; +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/ScreenCapture/OTAnnotationScreenCaptureView.m b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/ScreenCapture/OTAnnotationScreenCaptureView.m new file mode 100644 index 0000000..a493dcb --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/ScreenCapture/OTAnnotationScreenCaptureView.m @@ -0,0 +1,76 @@ +// +// OTAnnotationScreenCaptureView.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTAnnotationScreenCaptureView.h" + +@interface OTAnnotationScreenCaptureModel() +@property (nonatomic) UIImage *sharedImage; +@property (nonatomic) NSDate *sharedDate; +@end + +@implementation OTAnnotationScreenCaptureModel + +- (instancetype)initWithSharedImage:(UIImage *)sharedImage + sharedDate:(NSDate *)sharedDate { + + if (self = [super init]) { + _sharedImage = sharedImage; + _sharedDate = sharedDate; + } + return self; +} + +@end + + +@interface OTAnnotationScreenCaptureView() +@property (weak, nonatomic) IBOutlet UIView *contenView; +@property (weak, nonatomic) IBOutlet UIImageView *sharedImageView; +@property (weak, nonatomic) IBOutlet UILabel *dateLabel; +@property (weak, nonatomic) IBOutlet UILabel *imageSizeLabel; + +@property (weak, nonatomic) IBOutlet UIButton *shareButton; +@property (weak, nonatomic) IBOutlet UIButton *saveButton; +@end + +@implementation OTAnnotationScreenCaptureView + +- (void)awakeFromNib { + [super awakeFromNib]; + + [self.contenView.layer setCornerRadius:6.0f]; + [self.contenView.layer setShadowOpacity:0.6]; + [self.contenView.layer setShadowColor:[UIColor blackColor].CGColor]; + + [self.sharedImageView.layer setCornerRadius:6.0f]; + [self.sharedImageView.layer setBorderColor:[UIColor grayColor].CGColor]; + [self.sharedImageView.layer setBorderWidth:1.0f]; +} + +- (void)updateWithShareModel:(OTAnnotationScreenCaptureModel *)shareModel { + + if (!shareModel) return; + + self.sharedImageView.image = shareModel.sharedImage; + + NSData *sharedImageData = UIImageJPEGRepresentation(shareModel.sharedImage, 1.0); + if (sharedImageData) { + [self.imageSizeLabel setText:[NSString stringWithFormat:@"%ld KB", (unsigned long)[sharedImageData length] / 1024]]; + } +} + +- (void)enableSaveImageButton:(BOOL)enable { + if (enable) { + [self.saveButton setEnabled:YES]; + [self.saveButton setAlpha:1.0]; + } + else { + [self.saveButton setEnabled:NO]; + [self.saveButton setAlpha:0.6]; + } +} + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/ScreenCapture/OTAnnotationScreenCaptureViewController.h b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/ScreenCapture/OTAnnotationScreenCaptureViewController.h new file mode 100644 index 0000000..838870d --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/ScreenCapture/OTAnnotationScreenCaptureViewController.h @@ -0,0 +1,25 @@ +// +// OTAnnotationScreenCaptureViewController.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import + +@interface OTAnnotationScreenCaptureViewController : UIViewController + +/** + * The shared image from the screen capture model. + */ +@property (nonatomic) UIImage *sharedImage; + +/** + * Initialize a new controller instance with the shared image. + * + * @param sharedImage The result image of the current screen. + * + * @return A new instance of the screen capture view controller object. + */ +- (instancetype)initWithSharedImage:(UIImage *)sharedImage; + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/ScreenCapture/OTAnnotationScreenCaptureViewController.m b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/ScreenCapture/OTAnnotationScreenCaptureViewController.m new file mode 100644 index 0000000..d19bfac --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/ScreenCapture/OTAnnotationScreenCaptureViewController.m @@ -0,0 +1,98 @@ +// +// OTAnnotationScreenCaptureViewController.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTAnnotationScreenCaptureViewController.h" +#import "OTAnnotationScreenCaptureView.h" +#import "OTAnnotationKitBundle.h" + +@interface OTAnnotationScreenCaptureViewController () +@property (nonatomic) OTAnnotationScreenCaptureModel *captureModel; +@property (strong, nonatomic) OTAnnotationScreenCaptureView *captureView; +@property (nonatomic) UIActivityViewController *activityViewController; +@end + +@implementation OTAnnotationScreenCaptureViewController + +- (void)setSharedImage:(UIImage *)sharedImage { + _sharedImage = sharedImage; + _captureModel = [[OTAnnotationScreenCaptureModel alloc] initWithSharedImage:sharedImage sharedDate:[NSDate date]]; + [self.captureView updateWithShareModel:_captureModel]; +} + +- (UIActivityViewController *)activityViewController { + if (!_activityViewController) { + _activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[self.captureModel.sharedImage] applicationActivities:nil]; + } + return _activityViewController; +} + +- (instancetype)initWithSharedImage:(UIImage *)sharedImage { + + if (self = [super initWithNibName:NSStringFromClass([self class]) bundle:[OTAnnotationAcceleratorBundle annotationAcceleratorBundle]]) { + + _captureModel = [[OTAnnotationScreenCaptureModel alloc] initWithSharedImage:sharedImage sharedDate:[NSDate date]]; + + self.providesPresentationContextTransitionStyle = YES; + self.definesPresentationContext = YES; + self.modalPresentationStyle = UIModalPresentationOverCurrentContext; + } + return self; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.captureView = (OTAnnotationScreenCaptureView *)self.view; + if (self.captureModel) { + [self.captureView updateWithShareModel: self.captureModel]; + } +} + +- (IBAction)shareButtonPressed:(id)sender { + + if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) { + [self presentViewController:self.activityViewController animated:YES completion:nil]; + } + else { + self.activityViewController.modalPresentationStyle = UIModalPresentationPopover; + self.activityViewController.popoverPresentationController.sourceView = self.captureView.shareButton; + [self presentViewController:self.activityViewController animated:YES completion:nil]; + } +} + +- (IBAction)saveButtonPressed:(UIButton *)sender { + UIImageWriteToSavedPhotosAlbum(self.captureModel.sharedImage, + self, + @selector(finishSavingImage:error:contextInfo:), + nil); +} + +- (void)finishSavingImage:(UIImage *)savedImage + error:(NSError *)error + contextInfo:(void *)contextInfo { + + UIAlertController *alert; + if (error) { + alert = [UIAlertController alertControllerWithTitle:@"Error" message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert]; + } + else { + alert = [UIAlertController alertControllerWithTitle:@"Success" message:nil preferredStyle:UIAlertControllerStyleAlert]; + [self.captureView enableSaveImageButton:NO]; + } + [self presentViewController:alert animated:YES completion:^(){ + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [alert dismissViewControllerAnimated:YES completion:nil]; + }); + }]; +} + +- (IBAction)cancelButtonPressed:(id)sender { + [self dismissViewControllerAnimated:YES completion:^(){ + [self.captureView enableSaveImageButton:YES]; + }]; +} + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/Text/OTAnnotationEditTextViewController.h b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/Text/OTAnnotationEditTextViewController.h new file mode 100644 index 0000000..bf04a3c --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/Text/OTAnnotationEditTextViewController.h @@ -0,0 +1,68 @@ +// +// OTAnnotationEditTextViewController.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import +#import "OTAnnotationTextView.h" + +@class OTAnnotationEditTextViewController; + +/** + * The delegate of an OTAnnotationEditTextViewController object must conform to the OTAnnotationEditTextViewProtocol protocol. + * Methods of the protocol allow the delegate to notify that text editing is finished. + */ +@protocol OTAnnotationEditTextViewProtocol + +/** + * Notifies the delegate that the edit text view controller has finished editing the text. + * + * @param editTextViewController The edit text view controller object. + * @param annotationTextView The annotation text view object that checks if the user finished editing the text. + */ +- (void)annotationEditTextViewController:(OTAnnotationEditTextViewController *)editTextViewController + didFinishEditing:(OTAnnotationTextView *)annotationTextView; + +@end + + +@interface OTAnnotationEditTextViewController : UIViewController + +/** + * The object that acts as the delegate of the edit text view controller. + * + * The delegate must adopt the OTAnnotationEditTextViewProtocol protocol. The delegate is not retained. + */ +@property (weak, nonatomic) id delegate; + +/** + * Initializer method to set the default color for the text. + * + * @param textColor The default text color. + * + * @return A text object initialized with the default color. + */ +- (instancetype)initWithTextColor:(UIColor *)textColor; + +/** + * Initializer method to set the default color for the remote text. + * + * @param textColor The default text color. + * + * @return A text object initialized with the default color. + */ +- (instancetype)initRemoteWithTextColor:(UIColor *)textColor; + +/** + * Creates a new annotation edit text object initialized with text and color. + * + * @param text The text string. + * @param textColor The text color. + * + * @return The initialized edit text object. + */ +- (instancetype)initWithText:(NSString *)text + textColor:(UIColor *)textColor; + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/Text/OTAnnotationEditTextViewController.m b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/Text/OTAnnotationEditTextViewController.m new file mode 100644 index 0000000..b224652 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/Text/OTAnnotationEditTextViewController.m @@ -0,0 +1,114 @@ +// +// OTAnnotationEditTextViewController.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTAnnotationEditTextViewController.h" +#import "OTAnnotationTextView.h" +#import "OTAnnotationKitBundle.h" + +@interface OTAnnotationEditTextViewController() { + BOOL shouldStatusShowAfterDismissal; +} +@property (nonatomic) OTAnnotationTextView *annotationTextView; +@property (nonatomic) UIPickerView *changeFontPickerView; +@property (nonatomic) BOOL statusBarHidden; +@end + +@implementation OTAnnotationEditTextViewController + +- (instancetype)init { + return nil; +} + +- (instancetype)initWithTextColor:(UIColor *)textColor { + + return [[OTAnnotationEditTextViewController alloc] initWithText:nil textColor:textColor]; +} + +- (instancetype)initRemoteWithTextColor:(UIColor *)textColor { + + return [[OTAnnotationEditTextViewController alloc] initWithText:nil textColor:textColor fontSize:36.0f remote:YES]; +} + +- (instancetype)initWithText:(NSString *)text + textColor:(UIColor *)textColor { + + return [[OTAnnotationEditTextViewController alloc] initWithText:text textColor:textColor fontSize:36.0f remote:NO]; +} + +- (instancetype)initWithText:(NSString *)text + textColor:(UIColor *)textColor + fontSize:(CGFloat)fontSize + remote:(BOOL)isRemote { + + if (self = [super initWithNibName:NSStringFromClass([self class]) + bundle:[OTAnnotationAcceleratorBundle annotationAcceleratorBundle]]) { + + self.providesPresentationContextTransitionStyle = YES; + self.definesPresentationContext = YES; + self.modalPresentationStyle = UIModalPresentationOverCurrentContext; + + self.view.backgroundColor = [UIColor colorWithRed:10/255.0f green:104/255.0f blue:128/255.0f alpha:1.0]; + + if (!isRemote) { + _annotationTextView = [[OTAnnotationTextView alloc] initWithText:text textColor:textColor fontSize:fontSize]; + } + else { + _annotationTextView = [[OTAnnotationTextView alloc] initRemoteWithText:text textColor:textColor fontSize:fontSize]; + } + + _annotationTextView.annotationTextViewDelegate = self; + _annotationTextView.autocorrectionType = UITextAutocorrectionTypeNo; + _annotationTextView.returnKeyType = UIReturnKeyDone; + _annotationTextView.backgroundColor = [UIColor darkGrayColor]; + _annotationTextView.alpha = 0.8; + [_annotationTextView setUserInteractionEnabled:NO]; + [self.view addSubview:_annotationTextView]; + } + return self; +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + + shouldStatusShowAfterDismissal = ![UIApplication sharedApplication].isStatusBarHidden; + self.statusBarHidden = YES; + [self setNeedsStatusBarAppearanceUpdate]; + [self.annotationTextView becomeFirstResponder]; +} + +- (BOOL)prefersStatusBarHidden { + return YES; +} + +- (void)viewWillDisappear:(BOOL)animated { + [super viewWillDisappear:animated]; + if (shouldStatusShowAfterDismissal) { + self.statusBarHidden = NO; + [self setNeedsStatusBarAppearanceUpdate]; + } + [self.annotationTextView resignFirstResponder]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (IBAction)removeButtonPressed:(id)sender { + if (self.delegate) { + [self.delegate annotationEditTextViewController:self didFinishEditing:nil]; + } + [self dismissViewControllerAnimated:YES completion:nil]; +} + +#pragma mark - OTAnnotationTextViewDelegate +- (void)annotationTextViewDidAddText:(OTAnnotationTextView *)textView { + if (textView != self.annotationTextView) return; + + if (self.delegate) { + [self.delegate annotationEditTextViewController:self didFinishEditing:textView]; + } + [textView setUserInteractionEnabled:YES]; + [self dismissViewControllerAnimated:YES completion:nil]; +} + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/Toolbar/OTAnnotationToolbarView.h b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/Toolbar/OTAnnotationToolbarView.h new file mode 100644 index 0000000..f1755b7 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/Toolbar/OTAnnotationToolbarView.h @@ -0,0 +1,90 @@ +// +// OTAnnotationToolbarView.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import +#import "OTAnnotationPath.h" +#import "OTAnnotationTextView.h" + +extern NSString *const kOTAnnotationToolbarDidPressEraseButton; +extern NSString *const kOTAnnotationToolbarDidPressCleanButton; +extern NSString *const kOTAnnotationToolbarDidAddTextAnnotation; + +@class OTAnnotationToolbarView; +@protocol OTAnnotationToolbarViewDataSource + +@optional +- (UIView *)annotationToolbarViewForRootViewForScreenShot:(OTAnnotationToolbarView *)toolbarView; + +@end + +@protocol OTAnnotationToolbarViewDelegate + +@optional + +- (BOOL)annotationToolbarViewAttemptToPressDoneButton:(OTAnnotationToolbarView *)annotationToolbarView; + +- (void)annotationToolbarViewDidPressDoneButton:(OTAnnotationToolbarView *)annotationToolbarView; + +- (void)annotationToolbarViewDidSelectDrawButton:(OTAnnotationToolbarView *)annotationToolbarView + path:(OTAnnotationPath *)path; + +- (void)annotationToolbarViewDidPressEraseButton:(OTAnnotationToolbarView *)annotationToolbarView; + +- (void)annotationToolbarViewDidPressCleanButton:(OTAnnotationToolbarView *)annotationToolbarView; + +- (void)annotationToolbarViewDidStartTextAnnotation:(OTAnnotationToolbarView *)annotationToolbarView; + +- (void)annotationToolbarViewDidAddTextAnnotation:(OTAnnotationToolbarView *)annotationToolbarView + annotationTextView:(OTAnnotationTextView *)textView; + +- (void)annotationToolbarViewDidCancelTextAnnotation:(OTAnnotationToolbarView *)annotationToolbarView + annotationTextView:(OTAnnotationTextView *)textView; + +@end + +typedef NS_ENUM(NSUInteger, OTAnnotationToolbarViewOrientation) { + OTAnnotationToolbarViewOrientationPortraitlBottom = 0, + OTAnnotationToolbarViewOrientationLandscapeLeft, + OTAnnotationToolbarViewOrientationLandscapeRight +}; + +@interface OTAnnotationToolbarView : UIView + +/** + * The object that acts as the data source of the annotation toolbar view. + * + * The delegate must adopt the OTAnnotationToolbarViewDataSource protocol. The data source is not retained. + */ +@property (weak, nonatomic) id toolbarViewDataSource; + +/** + * The object that acts as the delegate object of the annotation toolbar view. + * + * The delegate must adopt the OTAnnotationToolbarViewDelegate protocol. The delegate object is not retained. + */ +@property (weak, nonatomic) id toolbarViewDelegate; + +/** + * The orientation of this annotation toolbar. + * + * @discussion The default value is OTAnnotationToolbarViewOrientationPortraitlBottom. It assumes the position of toolbar view is at the bottom and all assosiated animation will be performed upwards. + * Set it to OTAnnotationToolbarViewOrientationLandscapeLeft, it assumes the position of toolbar view is on the left and all assosiated animation will be performed towards right. + * Set it to OTAnnotationToolbarViewOrientationLandscapeRight, it assumes the position of toolbar view is on the right and all assosiated animation will be performed towards left. + * + */ +@property (nonatomic) OTAnnotationToolbarViewOrientation toolbarViewOrientation; + +@property (readonly, nonatomic) UIButton *doneButton; +@property (readonly, nonatomic) UIButton *annotateButton; +@property (readonly, nonatomic) UIButton *colorButton; +@property (readonly, nonatomic) UIButton *textButton; +@property (readonly, nonatomic) UIButton *screenshotButton; +@property (readonly, nonatomic) UIButton *eraseButton; +@property (readonly, nonatomic) UIButton *eraseAllButton; + +@property (nonatomic) BOOL showDoneButton; + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/Toolbar/OTAnnotationToolbarView.m b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/Toolbar/OTAnnotationToolbarView.m new file mode 100644 index 0000000..4384518 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/Toolbar/OTAnnotationToolbarView.m @@ -0,0 +1,503 @@ +// +// OTAnnotationToolbarView.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTAnnotationToolbarView.h" +#import "UIView+Helper.h" +#import "UIButton+AutoLayoutHelper.h" +#import "OTAcceleratorSession.h" + +NSString * const kOTAnnotationToolbarDidPressEraseButton = @"kOTAnnotationToolbarDidPressEraseButton"; +NSString * const kOTAnnotationToolbarDidPressCleanButton = @"kOTAnnotationToolbarDidPressCleanButton"; +NSString * const kOTAnnotationToolbarDidAddTextAnnotation = @"kOTAnnotationToolbarDidAddTextAnnotation"; + +#define kNumberOfButtons 6 + +@interface OTAnnotationToolbarButton : UIButton +@end + +@implementation OTAnnotationToolbarButton + +- (instancetype)init { + if (self = [super init]) { + self.translatesAutoresizingMaskIntoConstraints = NO; + self.imageEdgeInsets = UIEdgeInsetsMake(8, 8, 8, 8); + } + return self; +} + +- (void)setEnabled:(BOOL)enabled { + super.enabled = enabled; + + if (enabled) { + [self setAlpha:1.0]; + } + else { + [self setAlpha:0.6]; + } +} + +- (void)didMoveToSuperview { + if (!self.superview) return; + [self addCenterConstraints]; +} + +@end + +@interface OTAnnotationToolbarDoneButton : UIButton +@end + +@implementation OTAnnotationToolbarDoneButton + +- (instancetype)init { + if (self = [super init]) { + self.translatesAutoresizingMaskIntoConstraints = NO; + self.imageEdgeInsets = UIEdgeInsetsMake(12, 12, 12, 12); + } + return self; +} + +- (void)didMoveToSuperview { + if (!self.superview) return; + [self addAttachedLayoutConstantsToSuperview]; +} + +@end + +#import "OTAnnotationToolbarView_UserInterfaces.h" +#import "OTAnnotationToolbarView+Animation.h" +#import "OTAnnotationColorPickerView.h" +#import "OTAnnotationKitBundle.h" + +#import "AnnLoggingWrapper.h" + +#import "OTAnnotationScreenCaptureViewController.h" +#import "OTAnnotationEditTextViewController.h" +#import "UIViewController+Helper.h" +#import "Constants.h" +#import "LHToolbar.h" + +#import "OTAnnotationToolbarView_Private.h" + +@interface OTAnnotationToolbarView() { + BOOL isUniversal; +} +@property (nonatomic) LHToolbar *toolbar; +@property (weak, nonatomic) OTAnnotationScrollView *annotationScrollView; + +@property (nonatomic) OTAnnotationToolbarDoneButton *doneButton; +@property (nonatomic) OTAnnotationToolbarButton *annotateButton; +@property (nonatomic) OTAnnotationColorPickerViewButton *colorButton; +@property (nonatomic) OTAnnotationToolbarButton *textButton; +@property (nonatomic) OTAnnotationToolbarButton *screenshotButton; +@property (nonatomic) OTAnnotationToolbarButton *eraseButton; +@property (nonatomic) OTAnnotationToolbarButton *eraseAllButton; + +@property (nonatomic) OTAnnotationScreenCaptureViewController *captureViewController; +@end + +@implementation OTAnnotationToolbarView + +- (void)setAnnotationScrollView:(OTAnnotationScrollView *)annotationScrollView { + _annotationScrollView = annotationScrollView; +} + +- (void)setToolbarViewOrientation:(OTAnnotationToolbarViewOrientation)toolbarViewOrientation { + + if (_toolbarViewOrientation == toolbarViewOrientation) return; + + _toolbarViewOrientation = toolbarViewOrientation; + + if (toolbarViewOrientation == OTAnnotationToolbarViewOrientationPortraitlBottom) { + self.toolbar.orientation = LHToolbarOrientationHorizontal; + self.colorPickerView.annotationColorPickerViewOrientation = OTAnnotationColorPickerViewOrientationPortrait; + } + else if (toolbarViewOrientation == OTAnnotationToolbarViewOrientationLandscapeLeft || + toolbarViewOrientation == OTAnnotationToolbarViewOrientationLandscapeRight) { + self.toolbar.orientation = LHToolbarOrientationVertical; + self.colorPickerView.annotationColorPickerViewOrientation = OTAnnotationColorPickerViewOrientationLandscape; + } + + if (self.toolbarViewOrientation == OTAnnotationToolbarViewOrientationPortraitlBottom) { + [_toolbar setContentView:_annotateButton atIndex:0]; + [_toolbar setContentView:_colorButton atIndex:1]; + [_toolbar setContentView:_textButton atIndex:2]; + [_toolbar setContentView:_screenshotButton atIndex:3]; + [_toolbar setContentView:_eraseButton atIndex:4]; + [_toolbar setContentView:_eraseAllButton atIndex:5]; + } + else { + [_toolbar setContentView:_annotateButton atIndex:5]; + [_toolbar setContentView:_colorButton atIndex:4]; + [_toolbar setContentView:_textButton atIndex:3]; + [_toolbar setContentView:_screenshotButton atIndex:2]; + [_toolbar setContentView:_eraseButton atIndex:1]; + [_toolbar setContentView:_eraseAllButton atIndex:0]; + } + + [self.toolbar reloadToolbar]; +} + +- (void)setShowDoneButton:(BOOL)showDoneButton { + if (_showDoneButton == YES && showDoneButton == NO) { + [self done]; + } + _showDoneButton = showDoneButton; +} + +- (OTAnnotationColorPickerView *)colorPickerView { + if (!_colorPickerView) { + _colorPickerView = [[OTAnnotationColorPickerView alloc] initWithFrame:CGRectMake(self.frame.origin.x, self.frame.origin.y, CGRectGetWidth([UIScreen mainScreen].bounds), HeightOfColorPicker)]; + _colorPickerView.delegate = self; + _colorPickerView.backgroundColor = [UIColor colorWithRed:51.0f/255.0f green:51.0f/255.0f blue:51.0f/255 alpha:1.0f]; + } + return _colorPickerView; +} + +- (UIView *)separatorView { + if (!_separatorView) { + CGRect separatorFrame = CGRectMake(self.frame.origin.x, self.frame.origin.y, CGRectGetWidth([UIScreen mainScreen].bounds), HeightOfColorPicker); + _separatorView = [[UIView alloc] initWithFrame:separatorFrame]; + _separatorView.backgroundColor = [UIColor whiteColor]; + _separatorView.alpha = 0; + [self addSubview:_separatorView]; + } + return _separatorView; +} + +- (UIView *)selectionShadowView { + if (!_selectionShadowView) { + _selectionShadowView = [[UIView alloc] init]; + _selectionShadowView.backgroundColor = [UIColor blackColor]; + _selectionShadowView.alpha = 0.8; + } + return _selectionShadowView; +} + +- (UIButton *)doneButton { + if (!_doneButton && _showDoneButton) { + + _doneButton = [[OTAnnotationToolbarDoneButton alloc] init]; + [_doneButton setImage:[UIImage imageNamed:@"checkmark" + inBundle:[OTAnnotationAcceleratorBundle annotationAcceleratorBundle] + compatibleWithTraitCollection:nil] + forState:UIControlStateNormal]; + [_doneButton setBackgroundColor:[UIColor colorWithRed:158.0/255.0 green:206.0/255.0 blue:73.0/255.0 alpha:1.0]]; + [_doneButton addTarget:self action:@selector(toolbarButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; + } + return _doneButton; +} + +- (OTAnnotationScreenCaptureViewController *)captureViewController { + if (!_captureViewController) { + _captureViewController = [[OTAnnotationScreenCaptureViewController alloc] initWithSharedImage:nil]; + } + return _captureViewController; +} + +- (instancetype)initWithFrame:(CGRect)frame + annotationScrollView:(OTAnnotationScrollView *)annotationScrollView { + + if (!annotationScrollView) return nil; + + if (self = [super initWithFrame:frame]) { + _toolbar = [[LHToolbar alloc] initWithNumberOfItems:kNumberOfButtons]; + _toolbar.translatesAutoresizingMaskIntoConstraints = NO; + _showDoneButton = YES; + [self configureToolbarButtons]; + [self addSubview:_toolbar]; + [_toolbar addAttachedLayoutConstantsToSuperview]; + + self.backgroundColor = [UIColor colorWithRed:69.0/255.0 green:69.0/255.0 blue:69.0/255.0 alpha:1.0]; + _annotationScrollView = annotationScrollView; + } + return self; +} + +- (instancetype)initUniversalWithFrame:(CGRect)frame + annotationScrollView:(OTAnnotationScrollView *)annotationScrollView{ + + OTAnnotationToolbarView *toolbarView = [self initWithFrame:frame annotationScrollView:annotationScrollView]; + if (!toolbarView) return nil; + isUniversal = YES; + return toolbarView; +} + +- (void)didMoveToSuperview { + if (!self.superview) { + self.annotationScrollView.annotatable = NO; + [self.colorPickerView removeFromSuperview]; + [self.separatorView removeFromSuperview]; + } +} + +- (void)done { + + if (!_showDoneButton) { + return; + } + + if (self.toolbarViewDelegate && [self.toolbarViewDelegate respondsToSelector:@selector(annotationToolbarViewAttemptToPressDoneButton:)]) { + + if (![self.toolbarViewDelegate annotationToolbarViewAttemptToPressDoneButton:self]) { + return; + } + } + + if ([self.annotationScrollView.annotationView.currentAnnotatable isKindOfClass:[OTAnnotationTextView class]]) { + OTAnnotationTextView *textView = (OTAnnotationTextView *)self.annotationScrollView.annotationView.currentAnnotatable; + [textView removeFromSuperview]; + } + self.annotationScrollView.annotatable = NO; + [self dismissColorPickerViewWithAniamtion:YES]; + + if (self.toolbarViewOrientation == OTAnnotationToolbarViewOrientationPortraitlBottom) { + [self.toolbar removeContentViewAtIndex:0]; + } + else { + [self.toolbar removeContentViewAtIndex:6]; + } + [self moveSelectionShadowViewTo:nil]; + [self resetToolbarButtons]; + if (self.toolbarViewDelegate && [self.toolbarViewDelegate respondsToSelector:@selector(annotationToolbarViewDidPressDoneButton:)]) { + [self.toolbarViewDelegate annotationToolbarViewDidPressDoneButton:self]; + } + [[AnnLoggingWrapper sharedInstance].logger logEventAction:KLogActionDone variation:KLogVariationSuccess completion:nil]; +} + +- (void)configureToolbarButtons { + + NSBundle *frameworkBundle = [OTAnnotationAcceleratorBundle annotationAcceleratorBundle]; + + _annotateButton = [[OTAnnotationToolbarButton alloc] init]; + _annotateButton.imageEdgeInsets = UIEdgeInsetsMake(10, 9, 10, 9); + [_annotateButton setImage:[UIImage imageNamed:@"annotate" inBundle:frameworkBundle compatibleWithTraitCollection:nil] forState:UIControlStateNormal]; + [_annotateButton addTarget:self action:@selector(toolbarButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; + + _colorButton = [[OTAnnotationColorPickerViewButton alloc] initWithWhiteBorder]; + [_colorButton addTarget:self action:@selector(toolbarButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; + + _textButton = [[OTAnnotationToolbarButton alloc] init]; + _textButton.imageEdgeInsets = UIEdgeInsetsMake(10, 9, 10, 9); + [_textButton setImage:[UIImage imageNamed:@"text" inBundle:frameworkBundle compatibleWithTraitCollection:nil] forState:UIControlStateNormal]; + [_textButton addTarget:self action:@selector(toolbarButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; + + _screenshotButton = [[OTAnnotationToolbarButton alloc] init]; + [_screenshotButton setImage:[UIImage imageNamed:@"screenshot" inBundle:frameworkBundle compatibleWithTraitCollection:nil] forState:UIControlStateNormal]; + _screenshotButton.imageEdgeInsets = UIEdgeInsetsMake(12, 12, 12, 12); + [_screenshotButton addTarget:self action:@selector(toolbarButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; + + _eraseButton = [[OTAnnotationToolbarButton alloc] init]; + _eraseButton.imageEdgeInsets = UIEdgeInsetsMake(11, 12, 11, 12); + [_eraseButton setImage:[UIImage imageNamed:@"erase" inBundle:frameworkBundle compatibleWithTraitCollection:nil] forState:UIControlStateNormal]; + [_eraseButton addTarget:self action:@selector(toolbarButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; + + _eraseAllButton = [[OTAnnotationToolbarButton alloc] init]; + _eraseAllButton.imageEdgeInsets = UIEdgeInsetsMake(11, 12, 11, 12); + [_eraseAllButton setImage:[UIImage imageNamed:@"trashcan" inBundle:frameworkBundle compatibleWithTraitCollection:nil] forState:UIControlStateNormal]; + [_eraseAllButton addTarget:self action:@selector(toolbarButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; + + + if (self.toolbarViewOrientation == OTAnnotationToolbarViewOrientationPortraitlBottom) { + [_toolbar setContentView:_annotateButton atIndex:0]; + [_toolbar setContentView:_colorButton atIndex:1]; + [_toolbar setContentView:_textButton atIndex:2]; + [_toolbar setContentView:_screenshotButton atIndex:3]; + [_toolbar setContentView:_eraseButton atIndex:4]; + [_toolbar setContentView:_eraseAllButton atIndex:5]; + } + else { + [_toolbar setContentView:_annotateButton atIndex:5]; + [_toolbar setContentView:_colorButton atIndex:4]; + [_toolbar setContentView:_textButton atIndex:3]; + [_toolbar setContentView:_screenshotButton atIndex:2]; + [_toolbar setContentView:_eraseButton atIndex:1]; + [_toolbar setContentView:_eraseAllButton atIndex:0]; + } + + [_toolbar reloadToolbar]; +} + +- (void)toolbarButtonPressed:(UIButton *)sender { + + if (sender == self.doneButton) { + [self done]; + } + else if (sender == self.annotateButton) { + self.annotationScrollView.annotatable = YES; + [self dismissColorPickerViewWithAniamtion:YES]; + if (self.showDoneButton && ![self.toolbar containedContentView:self.doneButton]) { + if (self.toolbarViewOrientation == OTAnnotationToolbarViewOrientationPortraitlBottom) { + [self.toolbar insertContentView:self.doneButton atIndex:0]; + } + else { + [self.toolbar insertContentView:self.doneButton atIndex:6]; + } + } + OTAnnotationPath *path = [[OTAnnotationPath alloc] initWithStrokeColor:self.colorPickerView.selectedColor]; + [self.annotationScrollView.annotationView setCurrentAnnotatable:path]; + [self disableButtons:@[self.annotateButton]]; + + if (self.toolbarViewDelegate && [self.toolbarViewDelegate respondsToSelector:@selector(annotationToolbarViewDidSelectDrawButton:path:)]) { + [self.toolbarViewDelegate annotationToolbarViewDidSelectDrawButton:self + path:path]; + } + } + else if (sender == self.textButton) { + + self.annotationScrollView.annotatable = YES; + [self moveSelectionShadowViewTo:nil]; + [self dismissColorPickerViewWithAniamtion:NO]; + if (self.showDoneButton && ![self.toolbar containedContentView:self.doneButton]) { + if (self.toolbarViewOrientation == OTAnnotationToolbarViewOrientationPortraitlBottom) { + [self.toolbar insertContentView:self.doneButton atIndex:0]; + } + else { + [self.toolbar insertContentView:self.doneButton atIndex:6]; + } + } + + OTAnnotationEditTextViewController *editTextViewController; + if (!isUniversal) { + editTextViewController = [[OTAnnotationEditTextViewController alloc] initWithTextColor:self.colorButton.backgroundColor]; + } + else { + editTextViewController = [[OTAnnotationEditTextViewController alloc] initRemoteWithTextColor:self.colorButton.backgroundColor]; + } + editTextViewController.delegate = self; + UIViewController *topViewController = [UIViewController topViewControllerWithRootViewController]; + [topViewController presentViewController:editTextViewController animated:YES completion:nil]; + [self disableButtons:@[self.annotateButton]]; + if (self.toolbarViewDelegate && [self.toolbarViewDelegate respondsToSelector:@selector(annotationToolbarViewDidStartTextAnnotation:)]) { + [self.toolbarViewDelegate annotationToolbarViewDidStartTextAnnotation:self]; + } + } + else if (sender == self.colorButton) { + [self showColorPickerView]; + } + else if (sender == self.eraseButton) { + id annotatableToRemove = [self.annotationScrollView.annotationView undoAnnotatable]; + if (annotatableToRemove) { + if (self.toolbarViewDelegate && [self.toolbarViewDelegate respondsToSelector:@selector(annotationToolbarViewDidPressEraseButton:)]) { + [self.toolbarViewDelegate annotationToolbarViewDidPressEraseButton:self]; + } + [[NSNotificationCenter defaultCenter] postNotificationName:kOTAnnotationToolbarDidPressEraseButton + object:self + userInfo:@{@"annotation":annotatableToRemove}]; + } + } + else if (sender == self.eraseAllButton) { + [self.annotationScrollView.annotationView removeAllAnnotatables]; + if (self.toolbarViewDelegate && [self.toolbarViewDelegate respondsToSelector:@selector(annotationToolbarViewDidPressCleanButton:)]) { + [self.toolbarViewDelegate annotationToolbarViewDidPressCleanButton:self]; + } + [[NSNotificationCenter defaultCenter] postNotificationName:kOTAnnotationToolbarDidPressCleanButton + object:self + userInfo:nil]; + } + else if (sender == self.screenshotButton) { + + [self dismissColorPickerViewWithAniamtion:NO]; + [self moveSelectionShadowViewTo:nil]; + + if (self.toolbarViewDataSource) { + self.captureViewController.sharedImage = [self.annotationScrollView.annotationView captureScreenWithView:[self.toolbarViewDataSource annotationToolbarViewForRootViewForScreenShot:self]]; + } + else { + self.captureViewController.sharedImage = [self.annotationScrollView.annotationView captureScreenWithView:_annotationScrollView]; + } + UIViewController *topViewController = [UIViewController topViewControllerWithRootViewController]; + [topViewController presentViewController:self.captureViewController animated:YES completion:nil]; + } + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + if (sender != self.textButton && sender != self.screenshotButton && sender != self.eraseButton && sender != self.eraseAllButton) { + [self moveSelectionShadowViewTo:sender]; + } + }); +} + +- (void)resetToolbarButtons { + + [self.annotateButton setEnabled:YES]; + [self.colorButton setEnabled:YES]; + [self.textButton setEnabled:YES]; + [self.screenshotButton setEnabled:YES]; + [self.eraseButton setEnabled:YES]; + [self.eraseAllButton setEnabled:YES]; +} + +- (void)disableButtons:(NSArray *)array { + for (UIButton *button in array) { + [button setEnabled:NO]; + } +} + +#pragma mark - OTAnnotationTextViewDelegate +- (void)annotationTextViewDidFinishEditing:(OTAnnotationTextView *)textView { + + [self.annotateButton setEnabled:YES]; + [self moveSelectionShadowViewTo:self.annotateButton]; + [self.annotationScrollView.annotationView commitCurrentAnnotatable]; + + if (self.toolbarViewDelegate && [self.toolbarViewDelegate respondsToSelector:@selector(annotationToolbarViewDidAddTextAnnotation:annotationTextView:)]) { + [self.toolbarViewDelegate annotationToolbarViewDidAddTextAnnotation:self annotationTextView:textView]; + } + [[NSNotificationCenter defaultCenter] postNotificationName:kOTAnnotationToolbarDidAddTextAnnotation + object:self + userInfo:@{@"annotation":textView}]; +} + +- (void)annotationTextViewDidCancel:(OTAnnotationTextView *)textView { + + [self.annotateButton setEnabled:YES]; + [self moveSelectionShadowViewTo:self.annotateButton]; + [self.annotationScrollView.annotationView commitCurrentAnnotatable]; + + if (self.toolbarViewDelegate && [self.toolbarViewDelegate respondsToSelector:@selector(annotationToolbarViewDidCancelTextAnnotation:annotationTextView:)]) { + [self.toolbarViewDelegate annotationToolbarViewDidCancelTextAnnotation:self annotationTextView:textView]; + } +} + +#pragma mark - OTAnnotationEditTextViewProtocol + +- (void)annotationEditTextViewController:(OTAnnotationEditTextViewController *)editTextViewController + didFinishEditing:(OTAnnotationTextView *)annotationTextView { + + if (annotationTextView) { + [self.annotationScrollView addContentView:annotationTextView]; + [self.annotationScrollView addTextAnnotation:annotationTextView]; + annotationTextView.annotationTextViewDelegate = self; + } + else { + [self done]; + } +} + +#pragma mark - OTAnnotationColorPickerViewProtocol + +- (void)colorPickerView:(OTAnnotationColorPickerView *)colorPickerView + didSelectColorButton:(OTAnnotationColorPickerViewButton *)button + selectedColor:(UIColor *)selectedColor { + + [[AnnLoggingWrapper sharedInstance].logger logEventAction:KLogActionPickerColor variation:KLogVariationSuccess completion:nil]; + [self.colorButton setBackgroundColor:selectedColor]; + if (self.annotationScrollView.isAnnotatable) { + if ([self.annotationScrollView.annotationView.currentAnnotatable isKindOfClass:[OTAnnotationTextView class]]) { + + OTAnnotationTextView *textView = (OTAnnotationTextView *)self.annotationScrollView.annotationView.currentAnnotatable; + textView.textColor = selectedColor; + } + else { + + OTAnnotationPath *path = [[OTAnnotationPath alloc] initWithStrokeColor:selectedColor]; + [self.annotationScrollView.annotationView setCurrentAnnotatable:path]; + + [self toolbarButtonPressed:self.annotateButton]; + } + } +} + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/Toolbar/OTAnnotationToolbarView_Private.h b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/Toolbar/OTAnnotationToolbarView_Private.h new file mode 100644 index 0000000..1040325 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/Toolbar/OTAnnotationToolbarView_Private.h @@ -0,0 +1,15 @@ +// +// OTAnnotationToolbarView_Private.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTAnnotationScrollView.h" + +@interface OTAnnotationToolbarView () +- (instancetype)initWithFrame:(CGRect)frame + annotationScrollView:(OTAnnotationScrollView *)annotationScrollView; + +- (instancetype)initUniversalWithFrame:(CGRect)frame + annotationScrollView:(OTAnnotationScrollView *)annotationScrollView; +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/Toolbar/OTAnnotationToolbarView_UserInterfaces.h b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/Toolbar/OTAnnotationToolbarView_UserInterfaces.h new file mode 100644 index 0000000..8b216f1 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotationUI/Toolbar/OTAnnotationToolbarView_UserInterfaces.h @@ -0,0 +1,15 @@ +// +// OTAnnotationToolbarView_UserInterfaces.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTAnnotationColorPickerView.h" + +@interface OTAnnotationToolbarView () + +@property (nonatomic) UIView *selectionShadowView; +@property (nonatomic) OTAnnotationColorPickerView *colorPickerView; +@property (nonatomic) UIView *separatorView; + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotator.h b/OTAcceleratorCore/OTAnnotation/OTAnnotator.h new file mode 100644 index 0000000..aa9335a --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotator.h @@ -0,0 +1,125 @@ +// +// OTAnnotator.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import +#import "OTAcceleratorSession.h" +#import "OTAnnotationScrollView.h" + +typedef NS_ENUM(NSUInteger, OTAnnotationSignal) { + OTAnnotationSessionDidConnect = 0, + OTAnnotationSessionDidDisconnect, + OTAnnotationConnectionCreated, + OTAnnotationConnectionDestroyed, + OTAnnotationSessionDidBeginReconnecting, + OTAnnotationSessionDidReconnect, + OTAnnotationSessionDidFail +}; + +typedef void (^OTAnnotationBlock)(OTAnnotationSignal signal, NSError *error); + +typedef void (^OTAnnotationDataSendingBlock)(NSArray *data, NSError * error); + +typedef void (^OTAnnotationDataReceivingBlock)(NSArray *data); + +@class OTAnnotator; + +@protocol OTAnnotatorDataSource +- (OTAcceleratorSession *)sessionOfOTAnnotator:(OTAnnotator *)annotator; +@end + + +@protocol AnnotationDelegate + +- (void)annotator:(OTAnnotator *)annotator + signal:(OTAnnotationSignal)signal + error:(NSError *)error; + +- (void)annotator:(OTAnnotator *)annotator sendAnnotationWithData:(NSArray *)data error:(NSError *)error; + +- (void)annotator:(OTAnnotator *)annotator receivedAnnotationData:(NSArray *)data; + +@end + +@interface OTAnnotator : NSObject + +/** + * The object that acts as the data source of the text chat. + * + * The delegate must adopt the OTAnnotatorDataSource protocol. The delegate is not retained. + */ +@property (weak, nonatomic) id dataSource; + +/** + * Registers to the shared session: [OTAcceleratorSession] and connect. + * + * @return An error to indicate whether it connects successfully, non-nil if it fails. + */ +- (NSError *)connect; + +/** + * An alternative connect method with a completion block handler. + * + * @param handler The completion handler to call with the change. + */ +- (void)connectWithCompletionHandler:(OTAnnotationBlock)handler; + +/** + * De-registers to the shared session: [OTAcceleratorSession] and stops publishing/subscriber. + * + * @return An error to indicate whether it disconnects successfully, non-nil if it fails. + */ +- (NSError *)disconnect; + +/** + * The completion handler to call with the sending data. + */ +@property (copy, nonatomic) OTAnnotationDataSendingBlock dataSendingHandler; + +/** + * The completion handler to call with the receiving data. + */ +@property (copy, nonatomic) OTAnnotationDataReceivingBlock dataReceivingHandler; + +/** + * The object that acts as the delegate of the annotator. + * + * The delegate must adopt the AnnotationDelegate protocol. The delegate is not retained. + */ +@property (weak, nonatomic) id delegate; + +/** + * The associated annotation scroll view. + * + * @discussion This will be nil until OTAnnotationSessionDidConnect being signaled. + * + * ****************************************************************************************** + * The annotationScrollView.scrollView.contentSize is a critical data for remote annotation + * ****************************************************************************************** + */ +@property (readonly, nonatomic) OTAnnotationScrollView *annotationScrollView; + +@property (nonatomic) BOOL stopSendingAnnotation; + +@property (nonatomic) BOOL stopReceivingAnnotation; + +- (void)cleanRemoteCanvas; + +#pragma mark - advanced +/** + * Manually subscribe to a stream with a specfieid name. + * + * @return An error to indicate whether it subscribes successfully, non-nil if it fails. + */ +- (NSError *)subscribeToStreamWithName:(NSString *)name; + +/** + * Manually subscribe to a stream with a specfieid stream id. + * + * @return An error to indicate whether it subscribes successfully, non-nil if it fails. + */ +- (NSError *)subscribeToStreamWithStreamId:(NSString *)streamId; + +@end diff --git a/OTAcceleratorCore/OTAnnotation/OTAnnotator.m b/OTAcceleratorCore/OTAnnotation/OTAnnotator.m new file mode 100644 index 0000000..f3d8a36 --- /dev/null +++ b/OTAcceleratorCore/OTAnnotation/OTAnnotator.m @@ -0,0 +1,658 @@ +// +// OTAnnotator.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +// defines for image scaling +// From https://bugs.chromium.org/p/webrtc/issues/detail?id=4643#c7 : +// Don't send any image larger than 1280px on either edge. Additionally, don't +// send any image with dimensions %16 != 0 +#define MAX_EDGE_SIZE_LIMIT 1280.0f +#define EDGE_DIMENSION_COMMON_FACTOR 16.0f + +#import "OTAcceleratorSession.h" +#import "OTAnnotator.h" +#import "OTAnnotationToolbarView_UserInterfaces.h" +#import "UIColor+HexString.h" +#import "JSON.h" + +@interface OTAnnotator() { + NSMutableDictionary *signalingPoint; + NSMutableArray *signalingPoints; + OTStream *latestScreenShareStream; + + NSMutableArray *ovalPoints; +} + +@property (nonatomic) OTAnnotationScrollView *annotationScrollView; +@property (nonatomic) OTAcceleratorSession *session; +@property (strong, nonatomic) OTAnnotationBlock handler; + +@end + +@implementation OTAnnotator + +- (void)setDataSource:(id)dataSource { + _dataSource = dataSource; + _session = [_dataSource sessionOfOTAnnotator:self]; +} + +- (NSError *)connect { + if (!self.delegate && !self.handler) return nil; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(eraseButtonPressed:) name:kOTAnnotationToolbarDidPressEraseButton object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cleanButtonPressed:) name:kOTAnnotationToolbarDidPressCleanButton object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidAdd:) name:kOTAnnotationToolbarDidAddTextAnnotation object:nil]; + return [self.session registerWithAccePack:self]; +} + +- (void)connectWithCompletionHandler:(OTAnnotationBlock)handler { + self.handler = handler; + [self connect]; +} + +- (NSError *)disconnect { + if (self.annotationScrollView) { + [self.annotationScrollView.annotationView removeAllAnnotatables]; + [self.annotationScrollView.annotationView removeAllRemoteAnnotatables]; + } + [[NSNotificationCenter defaultCenter] removeObserver:self]; + return [self.session deregisterWithAccePack:self]; +} + +- (void)notifiyAllWithSignal:(OTAnnotationSignal)signal error:(NSError *)error { + + if (self.handler) { + self.handler(signal, error); + } + + if (self.delegate) { + [self.delegate annotator:self signal:signal error:error]; + } +} + +- (void) sessionDidConnect:(OTSession *)session { + + self.annotationScrollView = [[OTAnnotationScrollView alloc] init]; + self.annotationScrollView.scrollView.contentSize = self.annotationScrollView.bounds.size; + self.annotationScrollView.annotationView.annotationViewDelegate = self; + [self notifiyAllWithSignal:OTAnnotationSessionDidConnect + error:nil]; +} + +- (void) sessionDidDisconnect:(OTSession *)session { + self.annotationScrollView = nil; + + [self notifiyAllWithSignal:OTAnnotationSessionDidDisconnect + error:nil]; +} + +- (void)session:(OTSession *)session streamCreated:(OTStream *)stream { + if (stream.videoType == OTStreamVideoTypeScreen) { + latestScreenShareStream = stream; + } + + if (!latestScreenShareStream) { + latestScreenShareStream = stream; + } +} + +- (void)session:(OTSession *)session streamDestroyed:(OTStream *)stream { + if ([latestScreenShareStream.streamId isEqualToString:stream.streamId]) { + latestScreenShareStream = nil; + } +} + +- (void)session:(OTSession *)session didFailWithError:(OTError *)error { + [self notifiyAllWithSignal:OTAnnotationSessionDidFail + error:error]; +} + +- (void)session:(OTSession *)session connectionCreated:(OTConnection *)connection { + [self notifiyAllWithSignal:OTAnnotationConnectionCreated + error:nil]; +} + +- (void)session:(OTSession *)session connectionDestroyed:(OTStream *)stream { + [self notifiyAllWithSignal:OTAnnotationConnectionDestroyed + error:nil]; +} + +- (void)sessionDidBeginReconnecting:(OTSession *)session { + [self notifiyAllWithSignal:OTAnnotationSessionDidBeginReconnecting + error:nil]; +} + +- (void)sessionDidReconnect:(OTSession *)session { + [self notifiyAllWithSignal:OTAnnotationSessionDidReconnect + error:nil]; +} + +- (void)cleanRemoteCanvas { + [self cleanButtonPressed:nil]; +} + +// OPENTOK SIGNALING +- (void)session:(OTSession*)session +receivedSignalType:(NSString*)type + fromConnection:(OTConnection*)connection + withString:(NSString*)string { + + if (self.stopReceivingAnnotation) return; + + // TODO for the next person who sees this: a workaround for making the web annotation work + if ([type isEqualToString:@"otAnnotation_requestPlatform"]) { + [self.session signalWithType:@"otAnnotation_mobileScreenShare" string:[JSON stringify:@{@"platform":@"ios"}] connection:nil error:nil]; + return; + } + + if (![type isEqualToString:@"otAnnotation_pen"] && + ![type isEqualToString:@"otAnnotation_text"] && + ![type isEqualToString:@"otAnnotation_undo"] && + ![type isEqualToString:@"otAnnotation_clear"]) { + + return; + } + + if (self.session.sessionConnectionStatus == OTSessionConnectionStatusConnected && + ![self.session.connection.connectionId isEqualToString:connection.connectionId]) { + + // make sure contentSize is up-to-date +// self.annotationScrollView.scrollView.contentSize = self.annotationScrollView.bounds.size; + + NSArray *jsonArray = [JSON parseJSON:string]; + + // notify receving data + if (self.dataReceivingHandler) { + self.dataReceivingHandler(jsonArray); + } + + if (self.delegate) { + [self.delegate annotator:self receivedAnnotationData:jsonArray]; + } + + if ([type isEqualToString:@"otAnnotation_undo"]) { + + if (jsonArray.count == 1 && [jsonArray.firstObject isEqual:[NSNull null]]) { + [self.annotationScrollView.annotationView removeRemoteAnnotatableWithGUID:nil]; + return; + } + + for (NSString *guid in jsonArray) { + [self.annotationScrollView.annotationView removeRemoteAnnotatableWithGUID:guid]; + } + return; + } + + if ([type isEqualToString:@"otAnnotation_clear"]) { + [self.annotationScrollView.annotationView removeAllRemoteAnnotatables]; + return; + } + + if (jsonArray.count == 0) return; + + // draw text + if ([type isEqualToString:@"otAnnotation_text"] && jsonArray.count == 1) { + [self drawText:[jsonArray firstObject]]; + return; + } + + // set path attributes + NSDictionary *firstJsonObject = [jsonArray firstObject]; + if (firstJsonObject[@"color"] && firstJsonObject[@"guid"] && firstJsonObject[@"lineWidth"]) { + NSString *remoteGUID = firstJsonObject[@"guid"]; + UIColor *drawingColor = [UIColor colorFromHexString:[jsonArray firstObject][@"color"]]; + CGFloat lineWidth = [[jsonArray firstObject][@"lineWidth"] floatValue]; + + self.annotationScrollView.annotationView.remoteAnnotatable = [[OTRemoteAnnotationPath alloc] initWithStrokeColor:drawingColor + remoteGUID:remoteGUID]; + OTRemoteAnnotationPath *currentPath = (OTRemoteAnnotationPath *)self.annotationScrollView.annotationView.currentAnnotatable; + currentPath.lineWidth = lineWidth; + } + else { + self.annotationScrollView.annotationView.remoteAnnotatable = [[OTRemoteAnnotationPath alloc] initWithStrokeColor:nil]; + } + + // draw oval + if ([jsonArray.lastObject isKindOfClass:[NSDictionary class]] && [jsonArray.lastObject[@"selectedItem"][@"id"] isEqualToString:@"OT_oval"]) { + + if (!ovalPoints) { + ovalPoints = [NSMutableArray arrayWithCapacity:10]; + } + + [ovalPoints addObjectsFromArray:jsonArray]; + + // this is a workaround because an oval data array is composed by two signals. one has seven elements and another has three elements. + if (ovalPoints.count == 10) { + jsonArray = [NSArray arrayWithArray:ovalPoints]; + ovalPoints = nil; + } + else { + return; + } + } + + // calculate drawing position + for (NSDictionary *json in jsonArray) { + + // this is the unique property from web + NSString *platform = json[@"platform"]; + if (platform && [platform isEqualToString:@"web"]) { + [self drawOnFitModeWithJson:json path:(OTRemoteAnnotationPath *)self.annotationScrollView.annotationView.remoteAnnotatable]; + continue; + } + + // the size of remote canvas(same with the ss size) + CGFloat remoteCanvasWidth = [json[@"canvasWidth"] floatValue]; + CGFloat remoteCanvasHeight = [json[@"canvasHeight"] floatValue]; + + // video W/H(width/height produced by the core codes of SS and Opentok) + CGFloat videoWidth = [json[@"videoWidth"] floatValue]; + CGFloat videoHeight = [json[@"videoHeight"] floatValue]; + + // the size of the current canvas + CGFloat thisCanvasWidth = CGRectGetWidth(self.annotationScrollView.annotationView.bounds); + CGFloat thisCanvasHeight = CGRectGetHeight(self.annotationScrollView.annotationView.bounds); + + // the aspect ratio of the remote/current canvas + CGFloat remoteCanvasAspectRatio = remoteCanvasWidth / remoteCanvasHeight; + CGFloat thisCanvasAspectRatio = thisCanvasWidth / thisCanvasHeight; + + if ((remoteCanvasWidth == videoWidth && remoteCanvasHeight == videoHeight) || thisCanvasAspectRatio == remoteCanvasAspectRatio) { + // draw on the fill mode or on the same aspect ratio + [self drawOnFillModeWithJson:json path:(OTRemoteAnnotationPath *)self.annotationScrollView.annotationView.remoteAnnotatable]; + } + else { + // draw on irregular aspect ratio + [self drawOnFitModeWithJson:json path:(OTRemoteAnnotationPath *)self.annotationScrollView.annotationView.remoteAnnotatable]; + } + } + } +} + +- (void)eraseButtonPressed:(NSNotification *)notification { + + if (!notification || !notification.object || !latestScreenShareStream) return; + + NSString *jsonString; + + if ([notification.userInfo[@"annotation"] isMemberOfClass:[OTAnnotationPath class]] ) { + OTAnnotationPath *path = (OTAnnotationPath *)notification.userInfo[@"annotation"]; + jsonString = [JSON stringify:@[path.uuid]]; + } + else if ([notification.userInfo[@"annotation"] isMemberOfClass:[OTAnnotationTextView class]]) { + jsonString = [JSON stringify:@[[NSNull null]]]; + } + + if (jsonString) { + NSError *error; + [self.session signalWithType:@"otAnnotation_undo" string:jsonString connection:latestScreenShareStream.connection error:&error]; + if (error) { + NSLog(@"remoteEraseButtonPressed: %@", error); + } + } +} + +- (void)cleanButtonPressed:(NSNotification *)notification { + + if (!latestScreenShareStream) return; + + NSError *error; + [self.session signalWithType:@"otAnnotation_clear" string:nil connection:latestScreenShareStream.connection error:&error]; + if (error) { + NSLog(@"remoteCleanButtonPressed: %@", error); + } +} + +- (void)textDidAdd:(NSNotification *)notification { + + if (!latestScreenShareStream) return; + if (![notification.userInfo[@"annotation"] isMemberOfClass:[OTAnnotationTextView class]]) return; + + OTAnnotationTextView *textView = (OTAnnotationTextView *)notification.userInfo[@"annotation"]; + NSDictionary *data = @{ + @"id": latestScreenShareStream.connection.connectionId, + @"fromId": self.session.connection.connectionId, + @"fromX": @(textView.frame.origin.x), + @"fromY": @(textView.frame.origin.y), + @"videoWidth": @(latestScreenShareStream.videoDimensions.width), + @"videoHeight": @(latestScreenShareStream.videoDimensions.height), + @"canvasWidth": @(self.annotationScrollView.scrollView.contentSize.width), + @"canvasHeight": @(self.annotationScrollView.scrollView.contentSize.height), + @"mirrored": @(NO), + @"platform": @"ios", + @"text": textView.text, + @"color": [UIColor hexStringFromColor:textView.textColor], + @"font": [NSString stringWithFormat:@"%@px Arial", @(textView.font.pointSize * [UIScreen mainScreen].scale)] + }; + NSError *error; + NSString *jsonString = [JSON stringify:@[data]]; + + [self.session signalWithType:@"otAnnotation_text" string:jsonString connection:latestScreenShareStream.connection error:&error]; + if (error) { + NSLog(@"remoteCleanButtonPressed: %@", error); + } +} + +- (void)drawText:(NSDictionary *)json { + + CGFloat remoteCanvasWidth = [json[@"canvasWidth"] floatValue]; + CGFloat remoteCanvasHeight = [json[@"canvasHeight"] floatValue]; + CGFloat thisCanvasWidth = CGRectGetWidth(self.annotationScrollView.annotationView.bounds); + CGFloat thisCanvasHeight = CGRectGetHeight(self.annotationScrollView.annotationView.bounds); + + // apply scale factor + // Based on this: http://www.iosres.com/index-legacy.html + // iPhone 4&4s aspect ratio is 3:2 = 0.666 + // iPhone 5&5s&6&6s aspect ratio is 16:9 = 0.5625 + // iPad aspect ratio is 4:3 = 0.75 + + CGFloat scale = 1.0f; + if (thisCanvasWidth < thisCanvasHeight || remoteCanvasWidth < remoteCanvasHeight) { + scale = thisCanvasHeight / remoteCanvasHeight; + } + else { + scale = thisCanvasWidth / remoteCanvasWidth; + } + + remoteCanvasWidth *= scale; + remoteCanvasHeight *= scale; + + // remote x and y + CGFloat fromX = [json[@"fromX"] floatValue] * scale; + CGFloat fromY = [json[@"fromY"] floatValue] * scale; + + OTAnnotationPoint *pt; + + if (thisCanvasWidth < thisCanvasHeight || remoteCanvasWidth < remoteCanvasHeight) { + + // letter boxing is produced on horizontal level + CGFloat actualDrawingFromX = fromX - (remoteCanvasWidth / 2 - self.annotationScrollView.annotationView.center.x); + pt = [OTAnnotationPoint pointWithX:actualDrawingFromX andY:fromY]; + } + else { + + // letter boxing is produced on vertical level + CGFloat actualDrawingFromY = fromY - (remoteCanvasHeight / 2 - self.annotationScrollView.annotationView.center.y); + pt = [OTAnnotationPoint pointWithX:fromX andY:actualDrawingFromY]; + } + + CGPoint cgPt = [pt cgPoint]; + NSString *text = json[@"text"]; + UIColor *color = [UIColor colorFromHexString:json[@"color"]]; + + NSString *fontSizeString = json[@"font"]; + NSArray *fontSizeStringArray = [fontSizeString componentsSeparatedByString:@" "]; + NSUInteger fontSize = [[fontSizeStringArray firstObject] integerValue]; + + if (!text || !color || !fontSizeString || !fontSizeStringArray || fontSizeStringArray.count != 2) return; + + OTRemoteAnnotationTextView *annotationTextView = [[OTRemoteAnnotationTextView alloc] initWithText:text + textColor:color + fontSize:fontSize * scale]; + + // add text annotation + [self.annotationScrollView addContentView:annotationTextView]; + [self.annotationScrollView addTextAnnotation:annotationTextView]; + [annotationTextView commit]; + + // reset position + annotationTextView.frame = CGRectMake(cgPt.x, + cgPt.y, + CGRectGetWidth(annotationTextView.bounds), + CGRectGetHeight(annotationTextView.bounds)); + [annotationTextView sizeToFit]; +} + +- (void)drawOnFillModeWithJson:(NSDictionary *)json + path:(OTRemoteAnnotationPath *)path { + + CGFloat remoteCanvasWidth = [json[@"canvasWidth"] floatValue]; + CGFloat remoteCanvasHeight = [json[@"canvasHeight"] floatValue]; + CGFloat xScaleFactor = self.annotationScrollView.bounds.size.width / remoteCanvasWidth; + CGFloat yScaleFactor = self.annotationScrollView.bounds.size.height / remoteCanvasHeight; + + CGFloat fromX = [json[@"fromX"] floatValue] * xScaleFactor; + CGFloat fromY = [json[@"fromY"] floatValue] * yScaleFactor; + CGFloat toX = [json[@"toX"] floatValue] * xScaleFactor; + CGFloat toY = [json[@"toY"] floatValue] * yScaleFactor; + + OTAnnotationPoint *pt1 = [OTAnnotationPoint pointWithX:fromX andY:fromY]; + OTAnnotationPoint *pt2 = [OTAnnotationPoint pointWithX:toX andY:toY]; + + if ([json[@"smoothed"] boolValue]) { + + [path drawCurveFrom:pt1 to:pt2]; + + if ([json[@"endPoint"] boolValue]) { + [path drawToPoint:pt2]; + } + } + else { + if (path.points.count == 0) { + [path startAtPoint:pt1]; + [path drawToPoint:pt2]; + } + else { + [path drawToPoint:pt1]; + [path drawToPoint:pt2]; + } + } +} + +// this method is always work when web annotations as a subscriber +- (void)drawOnFitModeWithJson:(NSDictionary *)json + path:(OTRemoteAnnotationPath *)path { + + CGFloat remoteCanvasWidth = [json[@"canvasWidth"] floatValue]; + CGFloat remoteCanvasHeight = [json[@"canvasHeight"] floatValue]; + CGFloat thisCanvasWidth = CGRectGetWidth(self.annotationScrollView.annotationView.bounds); + CGFloat thisCanvasHeight = CGRectGetHeight(self.annotationScrollView.annotationView.bounds); + + // apply scale factor + // Based on this: http://www.iosres.com/index-legacy.html + // iPhone 4&4s aspect ratio is 3:2 = 0.666 + // iPhone 5&5s&6&6s aspect ratio is 16:9 = 0.5625 + // iPad aspect ratio is 4:3 = 0.75 + + CGFloat scale = 1.0f; + if (thisCanvasWidth < thisCanvasHeight || remoteCanvasWidth < remoteCanvasHeight) { + scale = thisCanvasHeight / remoteCanvasHeight; + } + else { + scale = thisCanvasWidth / remoteCanvasWidth; + } + + remoteCanvasWidth *= scale; + remoteCanvasHeight *= scale; + + // remote x and y + CGFloat fromX = [json[@"fromX"] floatValue] * scale; + CGFloat fromY = [json[@"fromY"] floatValue] * scale; + CGFloat toX = [json[@"toX"] floatValue] * scale; + CGFloat toY = [json[@"toY"] floatValue] * scale; + + OTAnnotationPoint *pt1; + OTAnnotationPoint *pt2; + + if (thisCanvasWidth < thisCanvasHeight || remoteCanvasWidth < remoteCanvasHeight) { + + // letter boxing is produced on horizontal level + CGFloat actualDrawingFromX = fromX - (remoteCanvasWidth / 2 - self.annotationScrollView.annotationView.center.x); + CGFloat actualDrawingToX = toX - (remoteCanvasWidth / 2 - self.annotationScrollView.annotationView.center.x); + pt1 = [OTAnnotationPoint pointWithX:actualDrawingFromX andY:fromY]; + pt2 = [OTAnnotationPoint pointWithX:actualDrawingToX andY:toY]; + } + else { + + // letter boxing is produced on vertical level + CGFloat actualDrawingFromY = fromY - (remoteCanvasHeight / 2 - self.annotationScrollView.annotationView.center.y); + CGFloat actualDrawingToY = toY - (remoteCanvasHeight / 2 - self.annotationScrollView.annotationView.center.y); + pt1 = [OTAnnotationPoint pointWithX:fromX andY:actualDrawingFromY]; + pt2 = [OTAnnotationPoint pointWithX:toX andY:actualDrawingToY]; + } + + if ([json[@"smoothed"] boolValue]) { + + [path drawCurveFrom:pt1 to:pt2]; + + if ([json[@"endPoint"] boolValue]) { + [path drawToPoint:pt2]; + } + } + else { + if (path.points.count == 0) { + [path startAtPoint:pt1]; + [path drawToPoint:pt2]; + } + else { + [path drawToPoint:pt1]; + [path drawToPoint:pt2]; + } + } +} + +#pragma mark - OTAnnotationViewDelegate + +- (void)annotationView:(OTAnnotationView *)annotationView + touchBegan:(UITouch *)touch + withEvent:(UIEvent *)event { + + if (self.stopSendingAnnotation) return; + + signalingPoints = [[NSMutableArray alloc] init]; + + // update this to ensure color property is not affected by remote annotation data + if (self.annotationScrollView.toolbarView) { + OTAnnotationPath *path = (OTAnnotationPath *)self.annotationScrollView.annotationView.currentAnnotatable; + path.strokeColor = self.annotationScrollView.toolbarView.colorPickerView.selectedColor; + } + + [self signalAnnotatable:annotationView.currentAnnotatable + touch:touch + addtionalInfo:@{@"startPoint":@(YES), @"endPoint":@(NO)}]; +} + +- (void)annotationView:(OTAnnotationView *)annotationView + touchMoved:(UITouch *)touch + withEvent:(UIEvent *)event { + + if (self.stopSendingAnnotation) return; + + if (!signalingPoints) { + signalingPoints = [[NSMutableArray alloc] init]; + } + + [self signalAnnotatable:annotationView.currentAnnotatable + touch:touch + addtionalInfo:@{@"startPoint":@(NO), @"endPoint":@(NO)}]; + + if (self.dataReceivingHandler) { + self.dataReceivingHandler(signalingPoints); + } + + if (self.delegate) { + [self.delegate annotator:self receivedAnnotationData:signalingPoints]; + } +} + +- (void)annotationView:(OTAnnotationView *)annotationView + touchEnded:(UITouch *)touch + withEvent:(UIEvent *)event { + + if (self.stopSendingAnnotation) return; + + if (signalingPoint) { + [self signalAnnotatable:annotationView.currentAnnotatable + touch:touch + addtionalInfo:@{@"startPoint":@(NO), @"endPoint":@(YES)}]; // the `endPoint` is not `NO` here because web does not recognize it, we can change this later. + } + else { + NSMutableDictionary *lastPoint = (NSMutableDictionary *)[signalingPoints lastObject]; + lastPoint[@"startPoint"] = @(NO); + lastPoint[@"endPoint"] = @(YES); + } + + //Need this condition strictly for drawing straight lines + if (signalingPoints.count == 1) { + NSMutableDictionary *lastPoint = (NSMutableDictionary *)[signalingPoints lastObject]; + lastPoint[@"endPoint"] = @(YES); + } + + NSError *error; + NSString *jsonString = [JSON stringify:signalingPoints]; + [self.session signalWithType:@"otAnnotation_pen" string:jsonString connection:nil error:&error]; + + // notify sending data + if (self.dataReceivingHandler) { + self.dataReceivingHandler(signalingPoints); + } + + if (self.delegate) { + [self.delegate annotator:self receivedAnnotationData:signalingPoints]; + } + + [signalingPoints addObject:touch]; + + signalingPoints = nil; +} + +- (void)signalAnnotatable:(id)annotatable + touch:(UITouch *)touch + addtionalInfo:(NSDictionary *)info { + + if ([annotatable isKindOfClass:[OTAnnotationPath class]]) { + + CGPoint touchPoint = [touch locationInView:touch.view]; + if (!signalingPoint) { + + OTAnnotationPath *path = (OTAnnotationPath *)self.annotationScrollView.annotationView.currentAnnotatable; + + signalingPoint = [NSMutableDictionary dictionaryWithDictionary:info]; + signalingPoint[@"id"] = latestScreenShareStream.connection.connectionId; // receiver id + signalingPoint[@"platform"] = @"ios"; + signalingPoint[@"fromId"] = self.session.connection.connectionId; // sender id + signalingPoint[@"fromX"] = @(touchPoint.x); + signalingPoint[@"fromY"] = @(touchPoint.y); + signalingPoint[@"videoWidth"] = @(latestScreenShareStream.videoDimensions.width); + signalingPoint[@"videoHeight"] = @(latestScreenShareStream.videoDimensions.height); + signalingPoint[@"canvasWidth"] = @(self.annotationScrollView.scrollView.contentSize.width); + signalingPoint[@"canvasHeight"] = @(self.annotationScrollView.scrollView.contentSize.height); + signalingPoint[@"lineWidth"] = @(path.lineWidth * [UIScreen mainScreen].scale); + signalingPoint[@"mirrored"] = @(NO); + signalingPoint[@"guid"] = path.uuid; + signalingPoint[@"smoothed"] = @(YES); // this is to enable drawing smoothly + signalingPoint[@"color"] = [UIColor hexStringFromColor:path.strokeColor]; + } + else { + signalingPoint[@"toX"] = @(touchPoint.x); + signalingPoint[@"toY"] = @(touchPoint.y); + [signalingPoints addObject:signalingPoint]; + signalingPoint = nil; + } + } +} + +#pragma mark - advanced +- (NSError *)subscribeToStreamWithName:(NSString *)name { + for (OTStream *stream in self.session.streams.allValues) { + if ([stream.name isEqualToString:name]) { + latestScreenShareStream = stream; + } + } + + return [NSError errorWithDomain:NSCocoaErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"There is no such stream with name: %@", name]}]; +} + +- (NSError *)subscribeToStreamWithStreamId:(NSString *)streamId { + for (OTStream *stream in self.session.streams.allValues) { + if ([stream.streamId isEqualToString:streamId]) { + latestScreenShareStream = stream; + } + } + + return [NSError errorWithDomain:NSCocoaErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"There is no such stream with streamId: %@", streamId]}]; +} + +@end diff --git a/OTAcceleratorCore/TextChat/Constant.h b/OTAcceleratorCore/TextChat/Constant.h new file mode 100644 index 0000000..7934d1c --- /dev/null +++ b/OTAcceleratorCore/TextChat/Constant.h @@ -0,0 +1,25 @@ +// +// Constant.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#ifndef Constant_h +#define Constant_h + +// Analytics constants +static NSString* const kLogComponentIdentifier = @"textChatAccPack"; +static NSString* const KLogClientVersion = @"ios-vsol-2.0.0"; +static NSString* const KLogActionInitialize = @"Init"; +static NSString* const KLogActionStart = @"Start"; +static NSString* const KLogActionEnd = @"End"; +static NSString* const KLogActionOpen = @"OpenTC"; +static NSString* const KLogActionClose = @"CloseTC"; +static NSString* const KLogActionSendMessage = @"SendMessage"; +static NSString* const KLogActionReceiveMessage = @"ReceiveMessage"; +static NSString* const KLogActionSetMaxLength = @"SetMaxLength"; +static NSString* const KLogVariationAttempt = @"Attempt"; +static NSString* const KLogVariationSuccess = @"Success"; +static NSString* const KLogVariationFailure = @"Failure"; + +#endif /* Constant_h */ diff --git a/OTAcceleratorCore/TextChat/OTTextChat.h b/OTAcceleratorCore/TextChat/OTTextChat.h new file mode 100644 index 0000000..b691ecd --- /dev/null +++ b/OTAcceleratorCore/TextChat/OTTextChat.h @@ -0,0 +1,117 @@ +// +// OTTextChat.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import +#import "OTAcceleratorSession.h" +#import "OTTextMessage.h" + +/** + * @typedef TextChatViewEventSignal NS_ENUM for the signal generated by the text chat. + * @brief this enum describes the types for event signals send by the text chat + * @constant TextChatViewEventSignalDidSendMessage The component sent a message. + * @constant TextChatViewEventSignalDidReceiveMessage The component received a new message. + * @constant TextChatViewEventSignalDidConnect A disconnect was requested and succeeded. + * @constant TextChatViewEventSignalDidDisconnect A new connection was requested and succeeded. + */ +typedef NS_ENUM(NSUInteger, OTTextChatConnectionEventSignal) { + OTTextChatConnectionEventSignalDidConnect = 0, + OTTextChatConnectionEventSignalDidDisconnect, + OTTextChatConnectionEventSignalConnectionCreated, + OTTextChatConnectionEventSignalConnectionDestroyed +}; + +typedef NS_ENUM(NSUInteger, OTTextChatMessageEventSignal) { + OTTextChatMessageEventSignalDidSendMessage = 0, + OTTextChatMessageEventSignalDidReceiveMessage +}; + +@class OTTextChat; +@class OTTextChatViewController; + +/** + * TextChatEventBlock type for the various connection signals. + * + * @param signal NS_ENUM send with one of the signal defined for TextChatEventSignal. + * @param connection The connection created or destroyed. + * @param error The error object indicating there is a problem when sending the signal. + */ +typedef void (^OTTextChatConnectionBlock)(OTTextChatConnectionEventSignal signal, OTConnection *connection, NSError *); + +/** + * TextChatEventBlock type for the various text message signals. + * + * @param signal NS_ENUM send with one of the signal defined for TextChatEventSignal. + * @param textMessage The current message sent or received. + * @param error The error object indicating there is a problem when sending the signal. + */ +typedef void (^OTTextChatMessageBlock)(OTTextChatMessageEventSignal signal, OTTextMessage *textMessage, NSError *); + +@protocol OTTextChatDataSource +- (OTAcceleratorSession *)sessionOfOTTextChat:(OTTextChat *)textChat; +@end + +@interface OTTextChat : NSObject + +/** + * The object that acts as the data source of the screen sharer. + * + * The delegate must adopt the OTTextChatDataSource protocol. The delegate is not retained. + */ +@property (weak, nonatomic) id dataSource; + +/** + * Initialize a new `OTTextChat` instsance. + * + * @return A new `OTTextChat` instsance. + */ +- (instancetype)init; + +/** + * Establishes a text chat connection with completion. + * + * @param handler NS_ENUM for the various event signals. + */ +- (void)connectWithHandler:(OTTextChatConnectionBlock)handler + messageHandler:(OTTextChatMessageBlock)messageHandler; + +/** + * Stops a text chat connection. + */ +- (void)disconnect; + +/** + * The alias of the sender client. + */ +@property (nonatomic) NSString *alias; + +/** + * The connection object of the sender client. + */ +@property (readonly, nonatomic) OTConnection *selfConnection; + +/** + * Conveniently broadcast a message with a given text. + */ +- (void)sendMessage:(NSString *)text; + +/** + * Conveniently send a message with a given text to a given connection. + */ +- (void)sendMessage:(NSString *)text + toConnection:(OTConnection *)connection; + +/** + * Broadcast a message with a OTTextMessage object. + */ +- (void)sendCustomMessage:(OTTextMessage *)textMessage; + +/** + * Send a message with a OTTextMessage object to a given connection. + */ +- (void)sendCustomMessage:(OTTextMessage *)textMessage + toConnection:(OTConnection *)connection; + +@end diff --git a/OTAcceleratorCore/TextChat/OTTextChat.m b/OTAcceleratorCore/TextChat/OTTextChat.m new file mode 100644 index 0000000..98d9fdf --- /dev/null +++ b/OTAcceleratorCore/TextChat/OTTextChat.m @@ -0,0 +1,273 @@ +// +// OTTextChatter.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTTextChat.h" + +#import + +#import "OTTextMessage.h" +#import "OTTextMessage_Private.h" + +#import "Constant.h" + +static NSString* const kTextChatType = @"text-chat"; + +@interface OTTextChat() + +@property (nonatomic) OTAcceleratorSession *session; +@property (nonatomic) OTKLogger *logger; +@property (nonatomic) OTConnection *selfConnection; + +@property (copy, nonatomic) OTTextChatConnectionBlock connectionHandler; +@property (copy, nonatomic) OTTextChatMessageBlock messageHandler; + +@end + +@implementation OTTextChat + +- (void)setDataSource:(id)dataSource { + _dataSource = dataSource; + _session = [_dataSource sessionOfOTTextChat:self]; +} + +- (instancetype)init { + + if (self = [super init]) { + + _logger = [[OTKLogger alloc] initWithClientVersion:KLogClientVersion + source:[[NSBundle mainBundle] bundleIdentifier] + componentId:kLogComponentIdentifier + guid:[[NSUUID UUID] UUIDString]]; + [_logger logEventAction:KLogActionInitialize variation:KLogVariationSuccess completion:nil]; + } + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)connect { + + [_logger logEventAction:KLogActionStart + variation:KLogVariationAttempt + completion:nil]; + + NSError *connectionError = [self.session registerWithAccePack:self]; + if(connectionError){ + + [_logger logEventAction:KLogActionStart + variation:KLogVariationFailure + completion:nil]; + + if (self.connectionHandler) { + self.connectionHandler(OTTextChatConnectionEventSignalDidConnect, nil, connectionError); + } + } + else { + + [_logger logEventAction:KLogActionStart + variation:KLogVariationSuccess + completion:nil]; + } +} + +- (void)connectWithHandler:(OTTextChatConnectionBlock)handler + messageHandler:(OTTextChatMessageBlock)messageHandler +{ + self.connectionHandler = handler; + self.messageHandler = messageHandler; + [self connect]; +} + +- (void)disconnect { + + [self.logger logEventAction:KLogActionEnd + variation:KLogVariationAttempt + completion:nil]; + + NSError *disconnectionError = [self.session deregisterWithAccePack:self]; + if(disconnectionError){ + + [self.logger logEventAction:KLogActionEnd + variation:KLogVariationFailure + completion:nil]; + + if (self.connectionHandler) { + self.connectionHandler(OTTextChatConnectionEventSignalDidDisconnect, nil, disconnectionError); + } + } + else { + + [self.logger logEventAction:KLogActionEnd + variation:KLogVariationSuccess + completion:nil]; + } +} + +- (void)sendMessage:(NSString *)text { + [self sendCustomMessage:[OTTextMessage messageWithSenderId:self.selfConnection.connectionId alias:self.alias text:text]]; +} + +- (void)sendMessage:(NSString *)text + toConnection:(OTConnection *)connection { + [self sendCustomMessage:[OTTextMessage messageWithSenderId:self.selfConnection.connectionId alias:self.alias text:text] toConnection:nil]; +} + +- (void)sendCustomMessage:(OTTextMessage *)textMessage { + [self sendCustomMessage:textMessage toConnection:nil]; +} + +- (void)sendCustomMessage:(OTTextMessage *)textMessage + toConnection:(OTConnection *)connection { + + NSError *error; + + [self.logger logEventAction:KLogActionSendMessage variation:KLogVariationAttempt completion:nil]; + + if (!textMessage.text || !textMessage.text.length) { + error = [NSError errorWithDomain:NSCocoaErrorDomain + code:-1 + userInfo:@{NSLocalizedDescriptionKey:@"Message format is wrong. Text is empty or null"}]; + + if (self.messageHandler) { + self.messageHandler(OTTextChatMessageEventSignalDidSendMessage, nil, error); + } + + [self.logger logEventAction:KLogActionSendMessage variation:KLogVariationFailure completion:nil]; + return; + } + + if (self.session.sessionId) { + + NSString *jsonString = [textMessage getTextChatSignalJSONString]; + if (!jsonString) { + + if (self.messageHandler) { + NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain + code:-1 + userInfo:@{NSLocalizedDescriptionKey:@"Error in parsing sender data"}]; + self.messageHandler(OTTextChatMessageEventSignalDidSendMessage, nil, error); + } + + [self.logger logEventAction:KLogActionSendMessage variation:KLogVariationFailure completion:nil]; + return; + } + + [self.session signalWithType:kTextChatType + string:jsonString + connection:connection + error:&error]; + + if (error) { + + [self.logger logEventAction:KLogActionSendMessage variation:KLogVariationFailure completion:nil]; + if (self.messageHandler) { + self.messageHandler(OTTextChatMessageEventSignalDidSendMessage, nil, error); + } + return; + } + + [self.logger logEventAction:KLogActionSendMessage variation:KLogVariationSuccess completion:nil]; + + if (self.messageHandler) { + self.messageHandler(OTTextChatMessageEventSignalDidSendMessage, textMessage, nil); + } + } + else { + error = [NSError errorWithDomain:NSCocoaErrorDomain + code:-1 + userInfo:@{NSLocalizedDescriptionKey:@"OTSession did not connect"}]; + + [self.logger logEventAction:KLogActionSendMessage variation:KLogVariationFailure completion:nil]; + + if (self.messageHandler) { + self.messageHandler(OTTextChatMessageEventSignalDidSendMessage, nil, error); + } + } +} + +#pragma mark - OTSessionDelegate +- (void)sessionDidConnect:(OTSession*)session { + + NSLog(@"TextChatComponent sessionDidConnect"); + + [_logger setSessionId:session.sessionId connectionId:session.connection.connectionId partnerId:@([self.session.apiKey integerValue])]; + + self.selfConnection = session.connection; + + if (self.connectionHandler) { + self.connectionHandler(OTTextChatConnectionEventSignalDidConnect, nil, nil); + } +} + +- (void)sessionDidDisconnect:(OTSession*)session { + + NSLog(@"TextChatComponent sessionDidDisconnect"); + + if (self.connectionHandler) { + self.connectionHandler(OTTextChatConnectionEventSignalDidDisconnect, nil, nil); + } +} + +- (void)session:(OTSession*)session didFailWithError:(OTError*)error { + NSLog(@"didFailWithError: (%@)", error); + + if (self.connectionHandler) { + self.connectionHandler(OTTextChatConnectionEventSignalDidConnect, nil, error); + } +} + +- (void)session:(OTSession *)session streamCreated:(OTStream *)stream {} +- (void)session:(OTSession *)session streamDestroyed:(OTStream *)stream{} + +- (void)session:(OTSession*) session connectionCreated:(OTConnection*)connection { + + OTConnection *textChatConnection = session.connection; + + if (self.connectionHandler) { + self.connectionHandler(OTTextChatConnectionEventSignalConnectionCreated, textChatConnection, nil); + } +} + +- (void)session:(OTSession *)session connectionDestroyed:(OTConnection *)connection { + + OTConnection *textChatConnection = session.connection; + + if (self.connectionHandler) { + self.connectionHandler(OTTextChatConnectionEventSignalConnectionDestroyed, textChatConnection, nil); + } +} + +- (void)session:(OTSession*)session +receivedSignalType:(NSString*)type + fromConnection:(OTConnection*)connection + withString:(NSString*)string { + + if (![type isEqualToString:kTextChatType]) return; + + if (![connection.connectionId isEqualToString:self.session.connection.connectionId]) { + + [self.logger logEventAction:KLogActionReceiveMessage variation:KLogVariationAttempt completion:nil]; + + OTTextMessage *textMessage = [[OTTextMessage alloc] initWithJSONString:string]; + + if (textMessage) { + + if (self.messageHandler) { + self.messageHandler(OTTextChatMessageEventSignalDidReceiveMessage, textMessage, nil); + } + + [self.logger logEventAction:KLogActionReceiveMessage variation:KLogVariationSuccess completion:nil]; + } + else { + + [self.logger logEventAction:KLogActionReceiveMessage variation:KLogVariationFailure completion:nil]; + } + } +} + +@end diff --git a/OTAcceleratorCore/TextChat/OTTextMessage.h b/OTAcceleratorCore/TextChat/OTTextMessage.h new file mode 100644 index 0000000..de769f5 --- /dev/null +++ b/OTAcceleratorCore/TextChat/OTTextMessage.h @@ -0,0 +1,43 @@ +// +// OTTextMessage.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// +#import + +/** + * A data model describing information used in individual text chat messages. + */ +@interface OTTextMessage : NSObject +/** + * The alias of the sender or receiver. + */ +@property (copy, nonatomic, readonly) NSString *alias; +/** + * A unique identifier for the sender of the message. + */ +@property (copy, nonatomic, readonly) NSString *senderId; +/** + * The content of the text message. + */ +@property (copy, nonatomic, readonly) NSString *text; +/** + * The date and time when the message was sent (UNIXTIMESTAMP format). + */ +@property (copy, nonatomic, readonly) NSDate *dateTime; + +/** + * The extra user information data carried by the message. + */ +@property (copy, nonatomic) NSDictionary *customData; + ++ (instancetype)messageWithSenderId:(NSString *)senderId + alias:(NSString *)alias + text:(NSString *)text; + +- (instancetype)initWithSenderId:(NSString *)senderId + alias:(NSString *)alias + dateTime:(NSDate *)dateTime + text:(NSString *)text; + +@end diff --git a/OTAcceleratorCore/TextChat/OTTextMessage.m b/OTAcceleratorCore/TextChat/OTTextMessage.m new file mode 100644 index 0000000..aba9c55 --- /dev/null +++ b/OTAcceleratorCore/TextChat/OTTextMessage.m @@ -0,0 +1,107 @@ +// +// OTTextMessage.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTTextMessage.h" +#import "OTTextMessage_Private.h" + +static NSString * const kText = @"text"; + +static NSString * const kSender = @"sender"; +static NSString * const kSenderAlias = @"alias"; +static NSString * const kSenderId = @"id"; +static NSString * const kSendOn = @"sentOn"; +static NSString * const kCustomData = @"customData"; + +@implementation OTTextMessage + ++ (instancetype)messageWithSenderId:(NSString *)senderId + alias:(NSString *)alias + text:(NSString *)text { + return [[self alloc] initWithSenderId:senderId + alias:alias + dateTime:[NSDate date] + text:text]; +} + +- (instancetype)initWithSenderId:(NSString *)senderId + alias:(NSString *)alias + dateTime:(NSDate *)dateTime + text:(NSString *)text { + + if (!senderId || !alias || !dateTime || !text) return nil; + + if (self = [super init]) { + _senderId = [senderId copy]; + _alias = [alias copy]; + _dateTime = [dateTime copy]; + _text = [text copy]; + _type = TCMessageTypesSent; + } + return self; +} + + +- (instancetype)initWithJSONString:(NSString *)jsonString { + + if (!jsonString || !jsonString.length) return nil; + + if (self = [super init]) { + + NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding]; + NSError *jsonError; + NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingAllowFragments error:&jsonError]; + + if (jsonError) { + NSLog(@"Error to read JSON data"); + return nil; + } + + if (dict[kText] && [dict[kText] isKindOfClass:[NSString class]]) { + _text = dict[kText]; + } + + if (dict[kSender] && dict[kSender][kSenderAlias] && [dict[kSender][kSenderAlias] isKindOfClass:[NSString class]]) { + _alias = dict[kSender][kSenderAlias]; + } + + if (dict[kSender] && dict[kSender][kSenderId] && [dict[kSender][kSenderId] isKindOfClass:[NSString class]]) { + _senderId = dict[kSender][kSenderId]; + } + + if (dict[kSendOn] && [dict[kSendOn] isKindOfClass:[NSNumber class]]) { + _dateTime = [NSDate dateWithTimeIntervalSince1970:([dict[kSendOn] doubleValue] / 1000.0f)]; + } + + if (dict[kCustomData] && [dict[kCustomData] isKindOfClass:[NSDictionary class]]) { + _customData = dict[kCustomData]; + } + + _type = TCMessageTypesReceived; + } + return self; +} + +- (NSString *)getTextChatSignalJSONString { + + if (!self.alias || !self.senderId || !self.text) return nil; + + NSError *jsonError; + NSMutableDictionary *json = [NSMutableDictionary dictionary]; + json[kText] = self.text; + json[kSender] = @{kSenderAlias: self.alias, kSenderId: self.senderId}; + json[kSendOn] = @([self.dateTime timeIntervalSince1970] * 1000.0f); + if (self.customData) { + json[kCustomData] = self.customData; + } + + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:json options:NSJSONWritingPrettyPrinted error:&jsonError]; + if (jsonError) { + NSLog(@"Error to parse JSON data"); + return nil; + } + return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; +} +@end diff --git a/OTAcceleratorCore/TextChat/OTTextMessage_Private.h b/OTAcceleratorCore/TextChat/OTTextMessage_Private.h new file mode 100644 index 0000000..db72a6d --- /dev/null +++ b/OTAcceleratorCore/TextChat/OTTextMessage_Private.h @@ -0,0 +1,45 @@ +// +// OTTextMessage_Private.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTTextMessage.h" + +@interface OTTextMessage () + +/** + * @typedef TCMessageTypes Type of message useful to add the respective UI to differenciate between a + * recent send message or a message follow by another message send by the same person + * @brief TCMessageTypesSent, TCMessageTypesSentShort, TCMessageTypesReceived, TCMessageTypesReceivedShort + * @constant TCMessageTypesSent The message was send by the sender + * @constant TCMessageTypesSentShort This is when the sender send a consecutive message + * @constant TCMessageTypesReceived This is the message send by receiver + * @constant TCMessageTypesReceivedShort When the receiver sends consecutive messages + */ +typedef NS_ENUM(NSUInteger, TCMessageTypes) { + TCMessageTypesSent = 0, + TCMessageTypesSentShort, + TCMessageTypesReceived, + TCMessageTypesReceivedShort, +}; + +@property (nonatomic) TCMessageTypes type; + +/** + * Initialize the received message as a JSON string to ensure communication interoperability. + * + * @param jsonString A JSON string, sent by the other application, containing all the required data for the message object. + * + * @return The initialized message object. + */ +- (instancetype)initWithJSONString:(NSString *)jsonString; + +/** + * Retrieve the current message as a JSON string to ensure communication interoperability. + * + * @return A string containing all the message object data. + */ +- (NSString *)getTextChatSignalJSONString; + +@end diff --git a/OTAcceleratorCore/TextChat/UI/OTTextChatAcceleratorBundle.h b/OTAcceleratorCore/TextChat/UI/OTTextChatAcceleratorBundle.h new file mode 100644 index 0000000..4061c4b --- /dev/null +++ b/OTAcceleratorCore/TextChat/UI/OTTextChatAcceleratorBundle.h @@ -0,0 +1,18 @@ +// +// OTTextChatAcceleratorBundle.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import + +@interface OTTextChatAcceleratorBundle : NSObject + +/** + * Retrieves the text chat bundle and ensures the bundle is loaded. + * + * @return The text chat bundle if found; otherwise returns nil. + */ ++ (NSBundle *)textChatAcceleratorBundle; + +@end diff --git a/OTAcceleratorCore/TextChat/UI/OTTextChatAcceleratorBundle.m b/OTAcceleratorCore/TextChat/UI/OTTextChatAcceleratorBundle.m new file mode 100644 index 0000000..260229e --- /dev/null +++ b/OTAcceleratorCore/TextChat/UI/OTTextChatAcceleratorBundle.m @@ -0,0 +1,36 @@ +// +// OTTextChatAcceleratorBundle.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTTextChatAcceleratorBundle.h" +#import "OTTextChatViewController.h" +#import "OTAcceleratorCoreBundle.h" + +@implementation OTTextChatAcceleratorBundle + ++ (NSBundle *)textChatAcceleratorBundle { + + NSURL *textChatKitBundleURL = [[NSBundle mainBundle] URLForResource:@"OTTextChatSampleBundle" withExtension:@"bundle"]; + if (textChatKitBundleURL){ + NSBundle *textChatViewBundle = [NSBundle bundleWithURL:textChatKitBundleURL]; + if (!textChatViewBundle.isLoaded) { + [textChatViewBundle load]; + } + return textChatViewBundle; + } + + textChatKitBundleURL = [[NSBundle bundleForClass:[OTTextChatViewController class]] URLForResource:@"OTTextChatSampleBundle" withExtension:@"bundle"]; + if (textChatKitBundleURL) { + NSBundle *textChatViewBundle = [NSBundle bundleWithURL:textChatKitBundleURL]; + if (!textChatViewBundle.isLoaded) { + [textChatViewBundle load]; + } + return textChatViewBundle; + } + + return [OTAcceleratorCoreBundle acceleratorCoreBundle]; +} + +@end diff --git a/OTAcceleratorCore/TextChat/UI/OTTextChatTableViewCells/OTTextChatTableViewCell.h b/OTAcceleratorCore/TextChat/UI/OTTextChatTableViewCells/OTTextChatTableViewCell.h new file mode 100644 index 0000000..15278f5 --- /dev/null +++ b/OTAcceleratorCore/TextChat/UI/OTTextChatTableViewCells/OTTextChatTableViewCell.h @@ -0,0 +1,34 @@ +// +// OTTextChatTableViewCell.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import +#import "OTTextMessage.h" + +@interface OTTextChatTableViewCell : UITableViewCell + +/** + * The message text displayed in the bubble. + */ +@property (readonly, weak, nonatomic) UITextView *messageTextView; + +/** + * The time at which the message was received or sent. + */ +@property (readonly, weak, nonatomic) UILabel *userInfoLabel; + +/** + * View containing the first letter of the sender's name. + */ +@property (readonly, weak, nonatomic) UIView *avatarHolder; + +/** + * Update the cell with the specified text chat information, and apply UI customization if available. + * + * @param textChat The message being sent or received. + */ +-(void)updateCellFromTextChat:(OTTextMessage *)textChat; + +@end diff --git a/OTAcceleratorCore/TextChat/UI/OTTextChatTableViewCells/OTTextChatTableViewCell.m b/OTAcceleratorCore/TextChat/UI/OTTextChatTableViewCells/OTTextChatTableViewCell.m new file mode 100644 index 0000000..3ff353c --- /dev/null +++ b/OTAcceleratorCore/TextChat/UI/OTTextChatTableViewCells/OTTextChatTableViewCell.m @@ -0,0 +1,81 @@ +// +// OTTextChatTableViewCell.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTTextChatTableViewCell.h" +#import "OTTextMessage_Private.h" + +@interface OTTextChatTableViewCell() +@property (weak, nonatomic) IBOutlet UITextView *messageTextView; +@property (weak, nonatomic) IBOutlet UILabel *userInfoLabel; +@property (weak, nonatomic) IBOutlet UIView *avatarHolder; +@property (weak, nonatomic) IBOutlet UIView *cornerUpRightView; +@property (weak, nonatomic) IBOutlet UIView *cornerUpLeftView; +@property (nonatomic) UILabel *userInitLable; +@end + +@implementation OTTextChatTableViewCell + +-(void)awakeFromNib { + + [super awakeFromNib]; + + [self styleUI]; + [self drawBubble]; +} + +- (void)styleUI { + self.layer.cornerRadius = 6.0f; + self.messageTextView.layer.cornerRadius = 6.0f; + self.messageTextView.textContainer.lineFragmentPadding = 20; + self.userInitLable = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.avatarHolder.bounds), CGRectGetHeight(self.avatarHolder.bounds))]; + self.userInitLable.textColor = [UIColor whiteColor]; + self.userInitLable.font = [UIFont systemFontOfSize:32.0]; + self.userInitLable.textAlignment = NSTextAlignmentCenter; + self.userInitLable.layer.cornerRadius = CGRectGetWidth(self.userInitLable.bounds) / 2; + self.userInitLable.layer.masksToBounds = YES; + [self.avatarHolder addSubview:self.userInitLable]; + self.avatarHolder.layer.cornerRadius = CGRectGetWidth(self.avatarHolder.bounds) / 2; +} + +- (void)drawBubble { + UIBezierPath *pathRight = [UIBezierPath new]; + [pathRight moveToPoint:(CGPoint){0, 0}]; + [pathRight addLineToPoint:(CGPoint){0, 30}]; + [pathRight addLineToPoint:(CGPoint){30, 0}]; + [pathRight addLineToPoint:(CGPoint){0, 0}]; + + UIBezierPath *pathleft = [UIBezierPath new]; + [pathleft moveToPoint:(CGPoint){0, 0}]; + [pathleft addLineToPoint:(CGPoint){30, 0}]; + [pathleft addLineToPoint:(CGPoint){30, 30}]; + [pathleft addLineToPoint:(CGPoint){0, 0}]; + + self.cornerUpRightView = [self cornerMaker:self.cornerUpRightView andWithPath:pathRight]; + self.cornerUpLeftView = [self cornerMaker:self.cornerUpLeftView andWithPath:pathleft]; +} + +-(UIView *)cornerMaker: (UIView *)view andWithPath:(UIBezierPath *)path { + CAShapeLayer *maskleft = [CAShapeLayer new]; + maskleft.frame = view.bounds; + maskleft.path = path.CGPath; + view.layer.mask = maskleft; + return view; +} + +-(void)updateCellFromTextChat:(OTTextMessage *)textChat { + + if (!textChat) return; + + NSDate *current_date = textChat.dateTime == nil ? [NSDate date] : textChat.dateTime; + NSDateFormatter *timeFormatter = [[NSDateFormatter alloc]init]; + timeFormatter.dateFormat = @"hh:mm a"; + NSString *msg_sender = [textChat.alias length] > 0 ? textChat.alias : @" "; + self.userInitLable.text = [msg_sender substringToIndex:1]; + self.userInfoLabel.text = [NSString stringWithFormat:@"%@, %@", msg_sender, [timeFormatter stringFromDate: current_date]]; + self.messageTextView.text = textChat.text; +} + +@end diff --git a/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatInputView.h b/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatInputView.h new file mode 100644 index 0000000..951b448 --- /dev/null +++ b/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatInputView.h @@ -0,0 +1,15 @@ +// +// OTTextChatInputView.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import + +@interface OTTextChatInputView : UIView + +@property (readonly, weak, nonatomic) UIButton *sendButton; + +@property (readonly, weak, nonatomic) UITextField *textField; + +@end diff --git a/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatInputView.m b/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatInputView.m new file mode 100644 index 0000000..eb87d4b --- /dev/null +++ b/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatInputView.m @@ -0,0 +1,16 @@ +// +// OTTextChatInputView.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTTextChatInputView.h" + +@interface OTTextChatInputView() +@property (weak, nonatomic) IBOutlet UITextField *textField; +@property (weak, nonatomic) IBOutlet UIButton *sendButton; +@end + +@implementation OTTextChatInputView + +@end diff --git a/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatNavigationBar.h b/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatNavigationBar.h new file mode 100644 index 0000000..a2376af --- /dev/null +++ b/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatNavigationBar.h @@ -0,0 +1,11 @@ +// +// OTTextChatNavigationBar.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import + +@interface OTTextChatNavigationBar : UINavigationBar + +@end diff --git a/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatNavigationBar.m b/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatNavigationBar.m new file mode 100644 index 0000000..10f7050 --- /dev/null +++ b/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatNavigationBar.m @@ -0,0 +1,93 @@ +// +// OTTextChatNavigationBar.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTTextChatNavigationBar.h" +#import "OTTextChatNavigationBar_Private.h" +#import "OTTextChatAcceleratorBundle.h" + +@interface OTTextChatNavigationBar() +@property (nonatomic) NSLayoutConstraint *topLayoutConstraint; +@property (nonatomic) NSLayoutConstraint *leftLayoutConstraint; +@property (nonatomic) NSLayoutConstraint *rightLayoutCostraint; +@property (nonatomic) NSLayoutConstraint *heightLayoutConstraint; +@end + +@implementation OTTextChatNavigationBar +@synthesize navigationBarHeight = _navigationBarHeight; + +- (CGFloat)navigationBarHeight { + if (!_heightLayoutConstraint) return 0; + return self.heightLayoutConstraint.constant; +} + +- (void)setNavigationBarHeight:(CGFloat)navigationBarHeight { + _navigationBarHeight = navigationBarHeight; + if (self.heightLayoutConstraint) { + self.heightLayoutConstraint.constant = navigationBarHeight; + } +} + +- (instancetype)init { + if (self = [super init]) { + + self.backgroundColor = [UIColor clearColor]; + self.barTintColor = [UIColor colorWithRed:70/255.0f green:156/255.0f blue:178/255.0f alpha:1.0f]; + self.tintColor = [UIColor whiteColor]; + self.translatesAutoresizingMaskIntoConstraints = NO; + } + return self; +} + +- (void)didMoveToSuperview { + + if (!self.superview) { + [NSLayoutConstraint deactivateConstraints:@[self.topLayoutConstraint, self.leftLayoutConstraint, self.rightLayoutCostraint, self.heightLayoutConstraint]]; + self.topLayoutConstraint = nil; + self.leftLayoutConstraint = nil; + self.rightLayoutCostraint = nil; + self.heightLayoutConstraint = nil; + return; + } + + + // add top constraint + self.topLayoutConstraint = [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.superview + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:0.0]; + + self.leftLayoutConstraint = [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeLeft + relatedBy:NSLayoutRelationEqual + toItem:self.superview + attribute:NSLayoutAttributeLeft + multiplier:1.0 + constant:0.0]; + + + self.rightLayoutCostraint = [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeRight + relatedBy:NSLayoutRelationEqual + toItem:self.superview + attribute:NSLayoutAttributeRight + multiplier:1.0 + constant:0.0]; + + self.heightLayoutConstraint = [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:self.navigationBarHeight]; + + [NSLayoutConstraint activateConstraints:@[self.topLayoutConstraint, self.leftLayoutConstraint, self.rightLayoutCostraint, self.heightLayoutConstraint]]; +} + +@end diff --git a/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatNavigationBar_Private.h b/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatNavigationBar_Private.h new file mode 100644 index 0000000..d6da099 --- /dev/null +++ b/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatNavigationBar_Private.h @@ -0,0 +1,11 @@ +// +// OTTextChatNavigationBar_Private.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import + +@interface OTTextChatNavigationBar () +@property (nonatomic) CGFloat navigationBarHeight; +@end diff --git a/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatTableView.h b/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatTableView.h new file mode 100644 index 0000000..c6b4419 --- /dev/null +++ b/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatTableView.h @@ -0,0 +1,35 @@ +// +// OTTextChatTableView.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import +#import "OTTextMessage.h" + +typedef NS_ENUM(NSUInteger, OTTextChatViewType) { + OTTextChatViewTypeDefault = 0, + OTTextChatViewTypeTokbox, + OTTextChatViewTypeCustom +}; + +@class OTTextChatTableView; +@protocol OTTextChatTableViewDataSource + +- (OTTextChatViewType)typeOfTextChatTableView:(OTTextChatTableView *)tableView; + +- (NSInteger)textChatTableView:(OTTextChatTableView *)tableView + numberOfRowsInSection:(NSInteger)section; + +- (OTTextMessage *)textChatTableView:(OTTextChatTableView *)tableView + textMessageItemAtIndexPath:(NSIndexPath *)indexPath; + +- (UITableViewCell *)textChatTableView:(OTTextChatTableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath; +@end + +@interface OTTextChatTableView : UITableView + +@property (weak, nonatomic) id textChatTableViewDelegate; + +@end diff --git a/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatTableView.m b/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatTableView.m new file mode 100644 index 0000000..1b28b81 --- /dev/null +++ b/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatTableView.m @@ -0,0 +1,11 @@ +// +// OTTextChatTableView.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTTextChatTableView.h" + +@implementation OTTextChatTableView +@dynamic dataSource; +@end diff --git a/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatViewController.h b/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatViewController.h new file mode 100644 index 0000000..32514b7 --- /dev/null +++ b/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatViewController.h @@ -0,0 +1,27 @@ +// +// OTTextChatView.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTTextMessage.h" +#import "OTTextChatTableView.h" +#import "OTTextChatInputView.h" +#import "OTTextChatNavigationBar.h" + +@interface OTTextChatViewController : UIViewController + +@property (readonly, nonatomic) OTTextChatNavigationBar *textChatNavigationBar; + +@property (readonly, weak, nonatomic) OTTextChatTableView *tableView; + +@property (readonly, weak, nonatomic) OTTextChatInputView *textChatInputView; + +/** + * @return Returns an initialized text chat view controller object. + */ ++ (instancetype)textChatViewController; + +- (void)scrollTextChatTableViewToBottom; + +@end diff --git a/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatViewController.m b/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatViewController.m new file mode 100644 index 0000000..9483497 --- /dev/null +++ b/OTAcceleratorCore/TextChat/UI/OTTextChatView/OTTextChatViewController.m @@ -0,0 +1,268 @@ +// +// OTTextChatView.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTTextMessage.h" +#import "OTTextMessage_Private.h" +#import "OTTextChatViewController.h" +#import "OTTextChatTableViewCell.h" +#import "OTTextChatNavigationBar_Private.h" + +#import "OTAcceleratorSession.h" + +#import "OTTextChatAcceleratorBundle.h" +#import "Constant.h" + +@interface OTTextChatViewController() { + OTTextChatViewType typeOfTextChatTableView; + NSInteger numberOfRowsInSection; +} + +@property (nonatomic) OTTextChatNavigationBar *textChatNavigationBar; +@property (weak, nonatomic) IBOutlet OTTextChatTableView *tableView; +@property (weak, nonatomic) IBOutlet OTTextChatInputView *textChatInputView; +@property (weak, nonatomic) IBOutlet NSLayoutConstraint *topLayoutConstraint; +@property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomViewLayoutConstraint; + +@end + +@implementation OTTextChatViewController ++ (instancetype)textChatViewController { + NSBundle *textChatViewBundle = [OTTextChatAcceleratorBundle textChatAcceleratorBundle]; + return [[OTTextChatViewController alloc] initWithNibName:NSStringFromClass([OTTextChatViewController class]) bundle:textChatViewBundle]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + UINib *textChatViewControllerNib = [UINib nibWithNibName:NSStringFromClass([OTTextChatViewController class]) + bundle:[OTTextChatAcceleratorBundle textChatAcceleratorBundle]]; + [textChatViewControllerNib instantiateWithOwner:self options:nil]; + [self configureTextChatViewController]; +} + +- (void)configureTextChatViewController { + + self.tableView.delegate = self; + self.tableView.dataSource = self; + self.tableView.rowHeight = UITableViewAutomaticDimension; + self.tableView.estimatedRowHeight = 130.0; + + self.textChatInputView.textField.textColor = [UIColor darkGrayColor]; + + [self loadTableViewCells]; + typeOfTextChatTableView = [self.tableView.textChatTableViewDelegate typeOfTextChatTableView:self.tableView]; + if (typeOfTextChatTableView == OTTextChatViewTypeDefault && !self.navigationController) { + [self configureNavigationBar]; + } + [self setupKeyboardNotification]; +} + +- (void)loadTableViewCells { + NSBundle *textChatViewBundle = [OTTextChatAcceleratorBundle textChatAcceleratorBundle]; + [self.tableView registerNib:[UINib nibWithNibName:@"TextChatSentTableViewCell" + bundle:textChatViewBundle] + forCellReuseIdentifier:@"SentChatMessage"]; + + [self.tableView registerNib:[UINib nibWithNibName:@"TextChatSentShortTableViewCell" + bundle:textChatViewBundle] + forCellReuseIdentifier:@"SentChatMessageShort"]; + + [self.tableView registerNib:[UINib nibWithNibName:@"TextChatReceivedTableViewCell" + bundle:textChatViewBundle] + forCellReuseIdentifier:@"RecvChatMessage"]; + + [self.tableView registerNib:[UINib nibWithNibName:@"TextChatReceivedShortTableViewCell" + bundle:textChatViewBundle] + forCellReuseIdentifier:@"RecvChatMessageShort"]; + + [self.tableView registerNib:[UINib nibWithNibName:@"TextChatComponentDivTableViewCell" + bundle:textChatViewBundle] + forCellReuseIdentifier:@"Divider"]; +} + +- (void)configureNavigationBar { + self.textChatNavigationBar = [[OTTextChatNavigationBar alloc] init]; + self.textChatNavigationBar.navigationBarHeight = 64.0f; + + [self.view addSubview:self.textChatNavigationBar]; + + UINavigationItem *cancelNavigationItem = [[UINavigationItem alloc] init]; + UIBarButtonItem *cancelBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"close_x_white" inBundle:[OTTextChatAcceleratorBundle textChatAcceleratorBundle] compatibleWithTraitCollection:nil] style:UIBarButtonItemStylePlain target:self action:@selector(dismiss)]; + cancelNavigationItem.rightBarButtonItem = cancelBarButtonItem; + self.textChatNavigationBar.items = @[cancelNavigationItem]; + + // add top constraint + self.topLayoutConstraint.active = NO; + self.topLayoutConstraint = nil; + self.topLayoutConstraint = [NSLayoutConstraint constraintWithItem:self.tableView + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.textChatNavigationBar + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:0.0]; + self.topLayoutConstraint.active = YES; +} + +- (void)setupKeyboardNotification { + __weak OTTextChatViewController *weakSelf = self; + [[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardWillShowNotification + object:nil + queue:[NSOperationQueue currentQueue] + usingBlock:^(NSNotification *notification) { + + NSDictionary* info = [notification userInfo]; + CGSize kbSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size; + double duration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]; + [UIView animateWithDuration:duration animations:^{ + + weakSelf.bottomViewLayoutConstraint.constant = kbSize.height + self.view.safeAreaInsets.bottom; + } completion:^(BOOL finished) { + + [weakSelf scrollTextChatTableViewToBottom]; + }]; + }]; + + [[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardWillHideNotification + object:nil + queue:[NSOperationQueue currentQueue] + usingBlock:^(NSNotification *notification) { + + NSDictionary* info = [notification userInfo]; + double duration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]; + [UIView animateWithDuration:duration animations:^{ + + weakSelf.bottomViewLayoutConstraint.constant = self.view.safeAreaInsets.bottom; + }]; + }]; +} + +- (void)viewSafeAreaInsetsDidChange { + [super viewSafeAreaInsetsDidChange]; + self.bottomViewLayoutConstraint.constant = self.view.safeAreaInsets.bottom; +} + +- (void)viewDidLayoutSubviews { + [super viewDidLayoutSubviews]; + + UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; + if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone && + (orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight)) { + + self.textChatNavigationBar.navigationBarHeight = 44.0f; + } + else { + + self.textChatNavigationBar.navigationBarHeight = 64.0f; + } +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)dismiss { + [self dismissViewControllerAnimated:YES completion:nil]; +} + +#pragma mark - Private methods + +- (void)scrollTextChatTableViewToBottom { + + if (numberOfRowsInSection == 0) { + return; + } + + NSIndexPath *lastIndexPath = [NSIndexPath indexPathForItem:(numberOfRowsInSection - 1) inSection:0]; + + // this is the workaround for iOS 8 + // so it won't have jerky scrolling once you update table view + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self.tableView scrollToRowAtIndexPath:lastIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES]; + }); +} + +#pragma mark - UITableViewDataSource + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + if (![tableView isKindOfClass:[OTTextChatTableView class]]) return 0; + OTTextChatTableView *textChatTableView = (OTTextChatTableView *)tableView; + typeOfTextChatTableView = [self.tableView.textChatTableViewDelegate typeOfTextChatTableView:self.tableView]; + numberOfRowsInSection = [textChatTableView.textChatTableViewDelegate textChatTableView:textChatTableView numberOfRowsInSection:section]; + return numberOfRowsInSection; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + + if (![tableView isKindOfClass:[OTTextChatTableView class]]) return 0; + OTTextChatTableView *textChatTableView = (OTTextChatTableView *)tableView; + + // check if final divider + OTTextMessage *textMessage = [textChatTableView.textChatTableViewDelegate textChatTableView:textChatTableView textMessageItemAtIndexPath:indexPath]; + + if (typeOfTextChatTableView == OTTextChatViewTypeDefault) { + + // determine text message cell type + if (numberOfRowsInSection > 1 && indexPath.row == numberOfRowsInSection - 1) { + + OTTextMessage *textMessage = [textChatTableView.textChatTableViewDelegate textChatTableView:textChatTableView textMessageItemAtIndexPath:indexPath]; + NSIndexPath *prevIndexPath = [NSIndexPath indexPathForRow:numberOfRowsInSection - 2 inSection:0]; + OTTextMessage *prevTextMessage = [textChatTableView.textChatTableViewDelegate textChatTableView:textChatTableView textMessageItemAtIndexPath:prevIndexPath]; + + if (textMessage.type == TCMessageTypesReceived && (prevTextMessage.type == TCMessageTypesSent || prevTextMessage.type == TCMessageTypesSentShort)) { + + } + else if (textMessage.type == TCMessageTypesSent && (prevTextMessage.type == TCMessageTypesReceived || prevTextMessage.type == TCMessageTypesReceivedShort)) { + + } + else { + + // not sure why 120 + if ([textMessage.dateTime timeIntervalSinceDate:prevTextMessage.dateTime] < 120 && + [textMessage.senderId isEqualToString:prevTextMessage.senderId]) { + + if (textMessage.type == TCMessageTypesReceived) { + textMessage.type = TCMessageTypesReceivedShort; + } + else if (textMessage.type == TCMessageTypesSent) { + textMessage.type = TCMessageTypesSentShort; + } + } + } + } + + NSString *cellId; + switch (textMessage.type) { + case TCMessageTypesSent: + cellId = @"SentChatMessage"; + break; + case TCMessageTypesSentShort: + cellId = @"SentChatMessageShort"; + break; + case TCMessageTypesReceived: + cellId = @"RecvChatMessage"; + break; + case TCMessageTypesReceivedShort: + cellId = @"RecvChatMessageShort"; + break; + default: + break; + } + + OTTextChatTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId + forIndexPath:indexPath]; + [cell updateCellFromTextChat:textMessage]; + return cell; + } + + return [textChatTableView.textChatTableViewDelegate textChatTableView:textChatTableView cellForRowAtIndexPath:indexPath]; +} + +#pragma mark - UIScrollViewDelegate +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { + [self.textChatInputView.textField resignFirstResponder]; +} +@end diff --git a/OTAcceleratorCoreSample/.DS_Store b/OTAcceleratorCoreSample/.DS_Store new file mode 100644 index 0000000..0e3d960 Binary files /dev/null and b/OTAcceleratorCoreSample/.DS_Store differ diff --git a/OTAnnotationSample/AnnotationBlankCanvasViewController.swift b/OTAnnotationSample/AnnotationBlankCanvasViewController.swift new file mode 100644 index 0000000..9f03d4c --- /dev/null +++ b/OTAnnotationSample/AnnotationBlankCanvasViewController.swift @@ -0,0 +1,67 @@ +// +// AnnotationBlankViewController.swift +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// +import UIKit + +class AnnotationBlankCanvasViewController: UIViewController { + + let topOffset: CGFloat = 20 + 44 + let topOffsetForLandscape: CGFloat = 44 + let heightOfToolbar = CGFloat(50) + let widthOfToolbar = CGFloat(50) + let annotationScrollView = OTAnnotationScrollView() + + override func viewDidLoad() { + super.viewDidLoad() + + navigationController?.navigationBar.isTranslucent = false + + annotationScrollView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height - heightOfToolbar - topOffset) + annotationScrollView.scrollView.contentSize = CGSize(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height - heightOfToolbar - topOffset) + + annotationScrollView.initializeToolbarView() + annotationScrollView.toolbarView!.frame = CGRect(x: 0, y: UIScreen.main.bounds.height - heightOfToolbar - topOffset, width: UIScreen.main.bounds.width, height: heightOfToolbar) + + view.addSubview(annotationScrollView) + view.addSubview(annotationScrollView.toolbarView!) + } + + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + + let screenRect = UIScreen.main.bounds; + var safeAreaOffset: CGFloat = 0 + + if #available(iOS 11.0, *) { + safeAreaOffset = self.view.safeAreaInsets.bottom + } + + // portrait + if screenRect.width < screenRect.height { + annotationScrollView.frame = CGRect(x: 0, y: 0, width: screenRect.width, height: screenRect.height - heightOfToolbar - topOffset) + annotationScrollView.scrollView.contentSize = CGSize(width: screenRect.width, height: screenRect.height - heightOfToolbar - topOffset) + + annotationScrollView.toolbarView!.frame = CGRect(x: 0, y: screenRect.height - heightOfToolbar - topOffset - safeAreaOffset , width: screenRect.width, height: heightOfToolbar) + annotationScrollView.toolbarView?.toolbarViewOrientation = .portraitlBottom + } + else { + + // landscape left +// annotationScrollView.frame = CGRectMake(widthOfToolbar, 0, CGRectGetWidth(screenRect) - widthOfToolbar, CGRectGetHeight(screenRect) - topOffsetForLandscape) +// annotationScrollView.scrollView.contentSize = CGSizeMake(CGRectGetWidth(screenRect) - widthOfToolbar, CGRectGetHeight(screenRect) - topOffsetForLandscape) +// +// annotationScrollView.toolbarView!.frame = CGRectMake(0, 0, widthOfToolbar, CGRectGetHeight(screenRect) - topOffsetForLandscape) +// annotationScrollView.toolbarView?.toolbarViewOrientation = .LandscapeLeft + + // landscape right + annotationScrollView.frame = CGRect(x: 0, y: 0, width: screenRect.width - widthOfToolbar, height: screenRect.height - topOffsetForLandscape) + annotationScrollView.scrollView.contentSize = CGSize(width: screenRect.width - widthOfToolbar, height: screenRect.height - topOffsetForLandscape) + + annotationScrollView.toolbarView!.frame = CGRect(x: screenRect.width - widthOfToolbar, y: -safeAreaOffset, width: widthOfToolbar, height: screenRect.height - topOffsetForLandscape) + annotationScrollView.toolbarView?.toolbarViewOrientation = .landscapeRight + } + } +} diff --git a/OTAnnotationSample/AnnotationOnScreenViewController.swift b/OTAnnotationSample/AnnotationOnScreenViewController.swift new file mode 100644 index 0000000..03cdfb6 --- /dev/null +++ b/OTAnnotationSample/AnnotationOnScreenViewController.swift @@ -0,0 +1,50 @@ +// +// AnnotationOnScreenViewController.swift +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +import UIKit + +class AnnotationOnScreenViewController: UIViewController { + private let annotationOverContentViewController = OTFullScreenAnnotationViewController() + private var statusBarHidden = false + @IBOutlet weak var holderView: UIView! + + override func viewDidLoad() { + super.viewDidLoad() + + self.navigationController?.navigationBar.isTranslucent = false + self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Annotate", style: .plain, target: self, action: #selector(startAnnotation)) + + let statusButton = UIButton(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 20)) + statusButton.addTarget(self, action: #selector(stopAnnotation), for: .touchUpInside) + statusButton.backgroundColor = UIColor(red: 118.0/255.0, green: 206.0/255.0, blue: 31.0/255.0, alpha: 1.0) + statusButton.setTitle("You are annotating your screen, Tap here to dismiss", for: .normal) + statusButton.titleLabel!.font = UIFont.systemFont(ofSize: 12.0) + annotationOverContentViewController?.view.addSubview(statusButton) + } + + @IBAction func viewPressed(_ sender: UIView!) { + holderView.bringSubviewToFront(sender) + } + + override var prefersStatusBarHidden: Bool { + return statusBarHidden + } + + @objc func startAnnotation() { + self.statusBarHidden = true + self.setNeedsStatusBarAppearanceUpdate() + let navigationBarFrame = navigationController!.navigationBar.frame + let newFrame = CGRect(x: navigationBarFrame.origin.x, y: navigationBarFrame.origin.y, width: navigationBarFrame.size.width, height: 64) + navigationController!.navigationBar.frame = newFrame + self.present(annotationOverContentViewController!, animated: true, completion: nil) + } + + @objc func stopAnnotation() { + self.statusBarHidden = false + self.setNeedsStatusBarAppearanceUpdate() + self.dismiss(animated: true, completion: nil) + } +} diff --git a/OTAnnotationSample/AppDelegate.h b/OTAnnotationSample/AppDelegate.h new file mode 100644 index 0000000..2c8b1b9 --- /dev/null +++ b/OTAnnotationSample/AppDelegate.h @@ -0,0 +1,19 @@ +// +// AppDelegate.h +// AnnotationAccPackKit +// +// Created by Xi Huang on 6/28/16. +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import +#import "OTAcceleratorSession.h" + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + +- (OTAcceleratorSession *)getSharedAcceleratorSession; + +@end + diff --git a/OTAnnotationSample/AppDelegate.m b/OTAnnotationSample/AppDelegate.m new file mode 100644 index 0000000..4ec9b22 --- /dev/null +++ b/OTAnnotationSample/AppDelegate.m @@ -0,0 +1,53 @@ +// +// AppDelegate.m +// AnnotationAccPackKit +// +// Created by Xi Huang on 6/28/16. +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "AppDelegate.h" +#import "OTAcceleratorSession.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + +static OTAcceleratorSession *sharedSession; + +- (OTAcceleratorSession *)getSharedAcceleratorSession { + return sharedSession; +} + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Override point for customization after application launch. +#warning - replace your OpenTok credentials here +// sharedSession = [[OTAcceleratorSession alloc] initWithOpenTokApiKey:<#api key#> sessionId:<#session id#> token:<#token#>]; + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application { + // 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. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. +} + +- (void)applicationDidEnterBackground:(UIApplication *)application { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. +} + +- (void)applicationWillEnterForeground:(UIApplication *)application { + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + // 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. +} + +- (void)applicationWillTerminate:(UIApplication *)application { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. +} + +@end diff --git a/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Contents.json b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..c6f1e22 --- /dev/null +++ b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,186 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "filename" : "Icon-40@2x-1.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "Icon-40@3x-1.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "filename" : "Icon-40@2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "Icon-40@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "filename" : "Icon-60@2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "filename" : "Icon-60@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "Icon-Small.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "Icon-40@2x-2.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "Icon-40.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "filename" : "Icon-40@2x-3.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "Icon-76.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "filename" : "Icon-76@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "filename" : "Icon-83.5@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + }, + { + "idiom" : "watch", + "role" : "notificationCenter", + "scale" : "2x", + "size" : "24x24", + "subtype" : "38mm" + }, + { + "idiom" : "watch", + "role" : "notificationCenter", + "scale" : "2x", + "size" : "27.5x27.5", + "subtype" : "42mm" + }, + { + "filename" : "Icon-Small@2x.png", + "idiom" : "watch", + "role" : "companionSettings", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "Icon-Small@3x.png", + "idiom" : "watch", + "role" : "companionSettings", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "40x40", + "subtype" : "38mm" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "44x44", + "subtype" : "40mm" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "50x50", + "subtype" : "44mm" + }, + { + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "86x86", + "subtype" : "38mm" + }, + { + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "98x98", + "subtype" : "42mm" + }, + { + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "108x108", + "subtype" : "44mm" + }, + { + "idiom" : "watch-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-40.png b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-40.png new file mode 100644 index 0000000..1b341c4 Binary files /dev/null and b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-40.png differ diff --git a/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-1.png b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-1.png new file mode 100644 index 0000000..baf7e36 Binary files /dev/null and b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-1.png differ diff --git a/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-2.png b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-2.png new file mode 100644 index 0000000..baf7e36 Binary files /dev/null and b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-2.png differ diff --git a/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-3.png b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-3.png new file mode 100644 index 0000000..4e7e051 Binary files /dev/null and b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-3.png differ diff --git a/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png new file mode 100644 index 0000000..4e7e051 Binary files /dev/null and b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png differ diff --git a/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-40@3x-1.png b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-40@3x-1.png new file mode 100644 index 0000000..b82133a Binary files /dev/null and b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-40@3x-1.png differ diff --git a/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png new file mode 100644 index 0000000..3451273 Binary files /dev/null and b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png differ diff --git a/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png new file mode 100644 index 0000000..3451273 Binary files /dev/null and b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png differ diff --git a/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png new file mode 100644 index 0000000..bd9b0c0 Binary files /dev/null and b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png differ diff --git a/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-76.png b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-76.png new file mode 100644 index 0000000..4375342 Binary files /dev/null and b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-76.png differ diff --git a/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png new file mode 100644 index 0000000..17e2924 Binary files /dev/null and b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png differ diff --git a/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png new file mode 100644 index 0000000..a611c6b Binary files /dev/null and b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png differ diff --git a/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-Small.png b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-Small.png new file mode 100644 index 0000000..b61a7ef Binary files /dev/null and b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-Small.png differ diff --git a/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png new file mode 100644 index 0000000..51a49a2 Binary files /dev/null and b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png differ diff --git a/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png new file mode 100644 index 0000000..f0edce6 Binary files /dev/null and b/OTAnnotationSample/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png differ diff --git a/OTAnnotationSample/Info.plist b/OTAnnotationSample/Info.plist new file mode 100644 index 0000000..198facd --- /dev/null +++ b/OTAnnotationSample/Info.plist @@ -0,0 +1,55 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 2 + LSRequiresIPhoneOS + + NSCameraUsageDescription + $(PRODUCT_NAME) uses camera + NSMicrophoneUsageDescription + $(PRODUCT_NAME) uses microphone + NSPhotoLibraryUsageDescription + $(PRODUCT_NAME) uses photo library + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/OTAnnotationSample/LaunchScreen.storyboard b/OTAnnotationSample/LaunchScreen.storyboard new file mode 100644 index 0000000..f62fd5f --- /dev/null +++ b/OTAnnotationSample/LaunchScreen.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTAnnotationSample/Main.storyboard b/OTAnnotationSample/Main.storyboard new file mode 100644 index 0000000..c5f79c0 --- /dev/null +++ b/OTAnnotationSample/Main.storyboard @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTAnnotationSample/OTAnnotationSample-Bridging-Header.h b/OTAnnotationSample/OTAnnotationSample-Bridging-Header.h new file mode 100644 index 0000000..9ddaaac --- /dev/null +++ b/OTAnnotationSample/OTAnnotationSample-Bridging-Header.h @@ -0,0 +1,9 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + +#import "AppDelegate.h" +#import "OTAnnotator.h" +#import "OTAnnotationScrollView.h" +#import "OTOneToOneCommunicator.h" +#import "OTFullScreenAnnotationViewController.h" diff --git a/OTAnnotationSample/OTAnnotationSampleBundle.bundle/Assets.car b/OTAnnotationSample/OTAnnotationSampleBundle.bundle/Assets.car new file mode 100644 index 0000000..3ed401b Binary files /dev/null and b/OTAnnotationSample/OTAnnotationSampleBundle.bundle/Assets.car differ diff --git a/OTAnnotationSample/OTAnnotationSampleBundle.bundle/Info.plist b/OTAnnotationSample/OTAnnotationSampleBundle.bundle/Info.plist new file mode 100644 index 0000000..9cf349d Binary files /dev/null and b/OTAnnotationSample/OTAnnotationSampleBundle.bundle/Info.plist differ diff --git a/OTAnnotationSample/OTAnnotationSampleBundle.bundle/OTAnnotationEditTextViewController.nib b/OTAnnotationSample/OTAnnotationSampleBundle.bundle/OTAnnotationEditTextViewController.nib new file mode 100644 index 0000000..488cb9e Binary files /dev/null and b/OTAnnotationSample/OTAnnotationSampleBundle.bundle/OTAnnotationEditTextViewController.nib differ diff --git a/OTAnnotationSample/OTAnnotationSampleBundle.bundle/OTAnnotationScreenCaptureViewController.nib b/OTAnnotationSample/OTAnnotationSampleBundle.bundle/OTAnnotationScreenCaptureViewController.nib new file mode 100644 index 0000000..64de608 Binary files /dev/null and b/OTAnnotationSample/OTAnnotationSampleBundle.bundle/OTAnnotationScreenCaptureViewController.nib differ diff --git a/OTAnnotationSample/OTAnnotationSampleBundle.bundle/_CodeSignature/CodeDirectory b/OTAnnotationSample/OTAnnotationSampleBundle.bundle/_CodeSignature/CodeDirectory new file mode 100644 index 0000000..c0f8c0e Binary files /dev/null and b/OTAnnotationSample/OTAnnotationSampleBundle.bundle/_CodeSignature/CodeDirectory differ diff --git a/OTAnnotationSample/OTAnnotationSampleBundle.bundle/_CodeSignature/CodeRequirements b/OTAnnotationSample/OTAnnotationSampleBundle.bundle/_CodeSignature/CodeRequirements new file mode 100644 index 0000000..dbf9d61 Binary files /dev/null and b/OTAnnotationSample/OTAnnotationSampleBundle.bundle/_CodeSignature/CodeRequirements differ diff --git a/OTAnnotationSample/OTAnnotationSampleBundle.bundle/_CodeSignature/CodeRequirements-1 b/OTAnnotationSample/OTAnnotationSampleBundle.bundle/_CodeSignature/CodeRequirements-1 new file mode 100644 index 0000000..f403951 Binary files /dev/null and b/OTAnnotationSample/OTAnnotationSampleBundle.bundle/_CodeSignature/CodeRequirements-1 differ diff --git a/OTAnnotationSample/OTAnnotationSampleBundle.bundle/_CodeSignature/CodeResources b/OTAnnotationSample/OTAnnotationSampleBundle.bundle/_CodeSignature/CodeResources new file mode 100644 index 0000000..2c71d39 --- /dev/null +++ b/OTAnnotationSample/OTAnnotationSampleBundle.bundle/_CodeSignature/CodeResources @@ -0,0 +1,143 @@ + + + + + files + + Assets.car + + D/gyPfjNSSoNofVXqE3y5e02uJE= + + OTAnnotationEditTextViewController.nib + + ij3+EB+orY/rYW9dLS8RxEXalNg= + + OTAnnotationScreenCaptureViewController.nib + + RhraMrFWEVM5IbMm3jofYjMRRfs= + + + files2 + + Assets.car + + hash + + D/gyPfjNSSoNofVXqE3y5e02uJE= + + hash2 + + KIzMLVseZ5RdjingMlCouUX5QK2YC2rBrXtFQzi9UmU= + + + OTAnnotationEditTextViewController.nib + + hash + + ij3+EB+orY/rYW9dLS8RxEXalNg= + + hash2 + + BzUd6O52s3l3dr5UPgh6Rrxk5pH5PK+f5WNnkVvghEg= + + + OTAnnotationScreenCaptureViewController.nib + + hash + + RhraMrFWEVM5IbMm3jofYjMRRfs= + + hash2 + + 54ylmnouG5DFJQa3cODcjmKW7cUNpPtbMFLz9CD8NFI= + + + + rules + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/OTAnnotationSample/OTAnnotationSampleBundle.bundle/_CodeSignature/CodeSignature b/OTAnnotationSample/OTAnnotationSampleBundle.bundle/_CodeSignature/CodeSignature new file mode 100644 index 0000000..e69de29 diff --git a/OTAnnotationSample/ReceiveAnnotationViewController.h b/OTAnnotationSample/ReceiveAnnotationViewController.h new file mode 100644 index 0000000..f2c119d --- /dev/null +++ b/OTAnnotationSample/ReceiveAnnotationViewController.h @@ -0,0 +1,11 @@ +// +// ReceiveAnnotationViewController.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import + +@interface ReceiveAnnotationViewController : UIViewController + +@end diff --git a/OTAnnotationSample/ReceiveAnnotationViewController.m b/OTAnnotationSample/ReceiveAnnotationViewController.m new file mode 100644 index 0000000..a96047f --- /dev/null +++ b/OTAnnotationSample/ReceiveAnnotationViewController.m @@ -0,0 +1,89 @@ +// +// ReceiveAnnotationViewController.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "ReceiveAnnotationViewController.h" +#import "OTAnnotator.h" +#import "OTOneToOneCommunicator.h" + +#import "AppDelegate.h" + +@interface ReceiveAnnotationViewController () +@property (nonatomic) OTAnnotator *annotator; +@property (nonatomic) OTOneToOneCommunicator *sharer; + +@property (weak, nonatomic) IBOutlet UIView *toolbarContainerView; +@property (weak, nonatomic) IBOutlet UIView *shareView; + +@end + +@implementation ReceiveAnnotationViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.title = @"Receive Annotation"; + self.navigationController.interactivePopGestureRecognizer.enabled = NO; +} + +- (void)viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + + self.sharer = [[OTOneToOneCommunicator alloc] initWithView:self.shareView]; + self.sharer.dataSource = self; + __weak ReceiveAnnotationViewController *weakSelf = self; + [self.sharer connectWithHandler:^(OTCommunicationSignal signal, NSError *error) { + if (!error) { + + if (signal == OTPublisherCreated) { + weakSelf.sharer.publishAudio = NO; + weakSelf.sharer.subscribeToAudio = NO; + + weakSelf.annotator = [[OTAnnotator alloc] init]; + weakSelf.annotator.dataSource = weakSelf; + [weakSelf.annotator connectWithCompletionHandler:^(OTAnnotationSignal signal, NSError *error) { + if (signal == OTAnnotationSessionDidConnect){ + weakSelf.annotator.annotationScrollView.frame = self.shareView.bounds; + weakSelf.annotator.annotationScrollView.scrollView.contentSize = self.shareView.bounds.size; + [self.shareView addSubview:weakSelf.annotator.annotationScrollView]; + + [weakSelf.annotator.annotationScrollView initializeToolbarView]; + weakSelf.annotator.annotationScrollView.toolbarView.toolbarViewDataSource = weakSelf; + + // using frame and self.view to contain toolbarView is for having more space to interact with color picker + weakSelf.annotator.annotationScrollView.toolbarView.frame = self.toolbarContainerView.frame; + [weakSelf.view addSubview:weakSelf.annotator.annotationScrollView.toolbarView]; + } + }]; + + weakSelf.annotator.dataReceivingHandler = ^(NSArray *data) { + NSLog(@"%@", data); + }; + } + } + }]; +} + +- (void)viewWillDisappear:(BOOL)animated { + [super viewWillDisappear:animated]; + [self.annotator disconnect]; + self.annotator = nil; + [self.sharer disconnect]; + self.sharer = nil; +} + +- (UIView *)annotationToolbarViewForRootViewForScreenShot:(OTAnnotationToolbarView *)toolbarView { + return self.shareView; +} + +- (OTAcceleratorSession *)sessionOfOTAnnotator:(OTAnnotator *)annotator { + return [(AppDelegate*)[[UIApplication sharedApplication] delegate] getSharedAcceleratorSession]; +} + +- (OTAcceleratorSession *)sessionOfOTOneToOneCommunicator:(OTOneToOneCommunicator *)oneToOneCommunicator { + return [(AppDelegate*)[[UIApplication sharedApplication] delegate] getSharedAcceleratorSession]; +} + +@end diff --git a/OTAnnotationSample/SendAnnotationViewController.swift b/OTAnnotationSample/SendAnnotationViewController.swift new file mode 100644 index 0000000..3183c1c --- /dev/null +++ b/OTAnnotationSample/SendAnnotationViewController.swift @@ -0,0 +1,79 @@ +// +// SendAnnotationViewController.swift +// OTAnnotationAccPackKit +// +// Created by Xi Huang on 9/10/16. +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +import UIKit +import Foundation + +class SendAnnotationViewController: UIViewController, OTOneToOneCommunicatorDataSource, OTAnnotatorDataSource, OTAnnotationToolbarViewDataSource { + + let annotator = OTAnnotator() + var sharer: OTOneToOneCommunicator? + + @IBOutlet weak var shareView: UIView! + @IBOutlet weak var toolbarContainer: UIView! + + override func viewDidLoad() { + super.viewDidLoad() + + navigationController?.navigationBar.isTranslucent = false + sharer = OTOneToOneCommunicator(view: shareView) + sharer?.dataSource = self + sharer?.connect{ + (signal, error) in + if error == nil { + if signal == .publisherCreated { + self.sharer?.isPublishAudio = false + self.sharer?.isPublishVideo = false + } + else if signal == .subscriberReady { + self.sharer?.subscriberView.frame = self.shareView.bounds + self.shareView.addSubview(self.sharer!.subscriberView) + + self.annotator.dataSource = self + self.annotator.connect { + (signal, error) in + if error == nil { + if signal == .sessionDidConnect { + self.annotator.annotationScrollView.frame = self.shareView.bounds + self.annotator.annotationScrollView.scrollView.contentSize = self.shareView.bounds.size + self.shareView.addSubview(self.annotator.annotationScrollView) + self.annotator.annotationScrollView.annotationView.currentAnnotatable = OTAnnotationPath.init(stroke: UIColor.yellow) + + self.annotator.annotationScrollView.initializeToolbarView() + self.annotator.annotationScrollView.toolbarView?.toolbarViewDataSource = self + self.annotator.annotationScrollView.toolbarView?.frame = self.toolbarContainer.frame + self.view.addSubview(self.annotator.annotationScrollView.toolbarView!) + } + } + } + } + } + } + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + annotator.annotationScrollView?.removeFromSuperview() + annotator.annotationScrollView?.toolbarView?.removeFromSuperview() + annotator.disconnect() + sharer?.subscriberView?.removeFromSuperview() + let _ = sharer?.disconnect() + } + + func annotationToolbarViewForRootView(forScreenShot toolbarView: OTAnnotationToolbarView!) -> UIView! { + return shareView + } + + func sessionOfOTOne(_ oneToOneCommunicator: OTOneToOneCommunicator!) -> OTAcceleratorSession! { + return (UIApplication.shared.delegate as? AppDelegate)?.getSharedAcceleratorSession() + } + + func session(of annotator: OTAnnotator!) -> OTAcceleratorSession! { + return (UIApplication.shared.delegate as? AppDelegate)?.getSharedAcceleratorSession() + } +} diff --git a/OTAnnotationSample/main.m b/OTAnnotationSample/main.m new file mode 100644 index 0000000..67def6b --- /dev/null +++ b/OTAnnotationSample/main.m @@ -0,0 +1,16 @@ +// +// main.m +// AnnotationAccPackKit +// +// Created by Xi Huang on 6/28/16. +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/OTAnnotationSample/mvc.png b/OTAnnotationSample/mvc.png new file mode 100644 index 0000000..576948a Binary files /dev/null and b/OTAnnotationSample/mvc.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/Contents.json b/OTAnnotationSampleBundle/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/OTAnnotationSampleBundle/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/OTAnnotationSampleBundle/Assets.xcassets/annotate.imageset/Contents.json b/OTAnnotationSampleBundle/Assets.xcassets/annotate.imageset/Contents.json new file mode 100644 index 0000000..c4752d2 --- /dev/null +++ b/OTAnnotationSampleBundle/Assets.xcassets/annotate.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "annotate.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "annotate@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "annotate@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/OTAnnotationSampleBundle/Assets.xcassets/annotate.imageset/annotate.png b/OTAnnotationSampleBundle/Assets.xcassets/annotate.imageset/annotate.png new file mode 100644 index 0000000..56fe616 Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/annotate.imageset/annotate.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/annotate.imageset/annotate@2x.png b/OTAnnotationSampleBundle/Assets.xcassets/annotate.imageset/annotate@2x.png new file mode 100644 index 0000000..97dd6f4 Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/annotate.imageset/annotate@2x.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/annotate.imageset/annotate@3x.png b/OTAnnotationSampleBundle/Assets.xcassets/annotate.imageset/annotate@3x.png new file mode 100644 index 0000000..346f588 Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/annotate.imageset/annotate@3x.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/checkmark.imageset/Contents.json b/OTAnnotationSampleBundle/Assets.xcassets/checkmark.imageset/Contents.json new file mode 100644 index 0000000..bc3e152 --- /dev/null +++ b/OTAnnotationSampleBundle/Assets.xcassets/checkmark.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "checkmark.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "checkmark@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "checkmark@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/OTAnnotationSampleBundle/Assets.xcassets/checkmark.imageset/checkmark.png b/OTAnnotationSampleBundle/Assets.xcassets/checkmark.imageset/checkmark.png new file mode 100644 index 0000000..ea3e45e Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/checkmark.imageset/checkmark.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/checkmark.imageset/checkmark@2x.png b/OTAnnotationSampleBundle/Assets.xcassets/checkmark.imageset/checkmark@2x.png new file mode 100644 index 0000000..6b4a2cc Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/checkmark.imageset/checkmark@2x.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/checkmark.imageset/checkmark@3x.png b/OTAnnotationSampleBundle/Assets.xcassets/checkmark.imageset/checkmark@3x.png new file mode 100644 index 0000000..c3097f9 Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/checkmark.imageset/checkmark@3x.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/delete icon.imageset/Contents.json b/OTAnnotationSampleBundle/Assets.xcassets/delete icon.imageset/Contents.json new file mode 100644 index 0000000..2fbfa1a --- /dev/null +++ b/OTAnnotationSampleBundle/Assets.xcassets/delete icon.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "delete icon.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "delete icon@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "delete icon@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/OTAnnotationSampleBundle/Assets.xcassets/delete icon.imageset/delete icon.png b/OTAnnotationSampleBundle/Assets.xcassets/delete icon.imageset/delete icon.png new file mode 100644 index 0000000..b5bb499 Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/delete icon.imageset/delete icon.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/delete icon.imageset/delete icon@2x.png b/OTAnnotationSampleBundle/Assets.xcassets/delete icon.imageset/delete icon@2x.png new file mode 100644 index 0000000..dd1235e Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/delete icon.imageset/delete icon@2x.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/delete icon.imageset/delete icon@3x.png b/OTAnnotationSampleBundle/Assets.xcassets/delete icon.imageset/delete icon@3x.png new file mode 100644 index 0000000..a808787 Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/delete icon.imageset/delete icon@3x.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/erase.imageset/Contents.json b/OTAnnotationSampleBundle/Assets.xcassets/erase.imageset/Contents.json new file mode 100644 index 0000000..4d9df98 --- /dev/null +++ b/OTAnnotationSampleBundle/Assets.xcassets/erase.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "erase.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "erase@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "erase@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/OTAnnotationSampleBundle/Assets.xcassets/erase.imageset/erase.png b/OTAnnotationSampleBundle/Assets.xcassets/erase.imageset/erase.png new file mode 100644 index 0000000..43fa833 Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/erase.imageset/erase.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/erase.imageset/erase@2x.png b/OTAnnotationSampleBundle/Assets.xcassets/erase.imageset/erase@2x.png new file mode 100644 index 0000000..de3089f Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/erase.imageset/erase@2x.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/erase.imageset/erase@3x.png b/OTAnnotationSampleBundle/Assets.xcassets/erase.imageset/erase@3x.png new file mode 100644 index 0000000..8769b9a Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/erase.imageset/erase@3x.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/resize icon.imageset/Contents.json b/OTAnnotationSampleBundle/Assets.xcassets/resize icon.imageset/Contents.json new file mode 100644 index 0000000..6e2b528 --- /dev/null +++ b/OTAnnotationSampleBundle/Assets.xcassets/resize icon.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "resize icon.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "resize icon@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "resize icon@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/OTAnnotationSampleBundle/Assets.xcassets/resize icon.imageset/resize icon.png b/OTAnnotationSampleBundle/Assets.xcassets/resize icon.imageset/resize icon.png new file mode 100644 index 0000000..ce70e9c Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/resize icon.imageset/resize icon.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/resize icon.imageset/resize icon@2x.png b/OTAnnotationSampleBundle/Assets.xcassets/resize icon.imageset/resize icon@2x.png new file mode 100644 index 0000000..a81af8a Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/resize icon.imageset/resize icon@2x.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/resize icon.imageset/resize icon@3x.png b/OTAnnotationSampleBundle/Assets.xcassets/resize icon.imageset/resize icon@3x.png new file mode 100644 index 0000000..abb904e Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/resize icon.imageset/resize icon@3x.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/rotate icon.imageset/Contents.json b/OTAnnotationSampleBundle/Assets.xcassets/rotate icon.imageset/Contents.json new file mode 100644 index 0000000..3589444 --- /dev/null +++ b/OTAnnotationSampleBundle/Assets.xcassets/rotate icon.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "rotate icon.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "rotate icon@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "rotate icon@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/OTAnnotationSampleBundle/Assets.xcassets/rotate icon.imageset/rotate icon.png b/OTAnnotationSampleBundle/Assets.xcassets/rotate icon.imageset/rotate icon.png new file mode 100644 index 0000000..4672cf1 Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/rotate icon.imageset/rotate icon.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/rotate icon.imageset/rotate icon@2x.png b/OTAnnotationSampleBundle/Assets.xcassets/rotate icon.imageset/rotate icon@2x.png new file mode 100644 index 0000000..e2b89cc Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/rotate icon.imageset/rotate icon@2x.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/rotate icon.imageset/rotate icon@3x.png b/OTAnnotationSampleBundle/Assets.xcassets/rotate icon.imageset/rotate icon@3x.png new file mode 100644 index 0000000..409a5f4 Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/rotate icon.imageset/rotate icon@3x.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/screenshare.imageset/Contents.json b/OTAnnotationSampleBundle/Assets.xcassets/screenshare.imageset/Contents.json new file mode 100644 index 0000000..1bad5a3 --- /dev/null +++ b/OTAnnotationSampleBundle/Assets.xcassets/screenshare.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "screenshare.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "screenshare@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "screenshare@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/OTAnnotationSampleBundle/Assets.xcassets/screenshare.imageset/screenshare.png b/OTAnnotationSampleBundle/Assets.xcassets/screenshare.imageset/screenshare.png new file mode 100644 index 0000000..a92c9cc Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/screenshare.imageset/screenshare.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/screenshare.imageset/screenshare@2x.png b/OTAnnotationSampleBundle/Assets.xcassets/screenshare.imageset/screenshare@2x.png new file mode 100644 index 0000000..8265279 Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/screenshare.imageset/screenshare@2x.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/screenshare.imageset/screenshare@3x.png b/OTAnnotationSampleBundle/Assets.xcassets/screenshare.imageset/screenshare@3x.png new file mode 100644 index 0000000..5ce949e Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/screenshare.imageset/screenshare@3x.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/screenshot.imageset/Contents.json b/OTAnnotationSampleBundle/Assets.xcassets/screenshot.imageset/Contents.json new file mode 100644 index 0000000..1e1dc81 --- /dev/null +++ b/OTAnnotationSampleBundle/Assets.xcassets/screenshot.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "screenshot.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "screenshot@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "screenshot@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/OTAnnotationSampleBundle/Assets.xcassets/screenshot.imageset/screenshot.png b/OTAnnotationSampleBundle/Assets.xcassets/screenshot.imageset/screenshot.png new file mode 100644 index 0000000..b9a60bc Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/screenshot.imageset/screenshot.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/screenshot.imageset/screenshot@2x.png b/OTAnnotationSampleBundle/Assets.xcassets/screenshot.imageset/screenshot@2x.png new file mode 100644 index 0000000..3a91cc7 Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/screenshot.imageset/screenshot@2x.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/screenshot.imageset/screenshot@3x.png b/OTAnnotationSampleBundle/Assets.xcassets/screenshot.imageset/screenshot@3x.png new file mode 100644 index 0000000..bf249c0 Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/screenshot.imageset/screenshot@3x.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/smallClose.imageset/Contents.json b/OTAnnotationSampleBundle/Assets.xcassets/smallClose.imageset/Contents.json new file mode 100644 index 0000000..eac54ea --- /dev/null +++ b/OTAnnotationSampleBundle/Assets.xcassets/smallClose.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "smallClose.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "smallClose@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "smallClose@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/OTAnnotationSampleBundle/Assets.xcassets/smallClose.imageset/smallClose.png b/OTAnnotationSampleBundle/Assets.xcassets/smallClose.imageset/smallClose.png new file mode 100644 index 0000000..ddff526 Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/smallClose.imageset/smallClose.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/smallClose.imageset/smallClose@2x.png b/OTAnnotationSampleBundle/Assets.xcassets/smallClose.imageset/smallClose@2x.png new file mode 100644 index 0000000..0b020d0 Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/smallClose.imageset/smallClose@2x.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/smallClose.imageset/smallClose@3x.png b/OTAnnotationSampleBundle/Assets.xcassets/smallClose.imageset/smallClose@3x.png new file mode 100644 index 0000000..d582767 Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/smallClose.imageset/smallClose@3x.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/text.imageset/Contents.json b/OTAnnotationSampleBundle/Assets.xcassets/text.imageset/Contents.json new file mode 100644 index 0000000..af8fcbb --- /dev/null +++ b/OTAnnotationSampleBundle/Assets.xcassets/text.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "text.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "text@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "text@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/OTAnnotationSampleBundle/Assets.xcassets/text.imageset/text.png b/OTAnnotationSampleBundle/Assets.xcassets/text.imageset/text.png new file mode 100644 index 0000000..7d5d94b Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/text.imageset/text.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/text.imageset/text@2x.png b/OTAnnotationSampleBundle/Assets.xcassets/text.imageset/text@2x.png new file mode 100644 index 0000000..18203d0 Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/text.imageset/text@2x.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/text.imageset/text@3x.png b/OTAnnotationSampleBundle/Assets.xcassets/text.imageset/text@3x.png new file mode 100644 index 0000000..00e5459 Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/text.imageset/text@3x.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/trashcan.imageset/Contents.json b/OTAnnotationSampleBundle/Assets.xcassets/trashcan.imageset/Contents.json new file mode 100644 index 0000000..838ecd2 --- /dev/null +++ b/OTAnnotationSampleBundle/Assets.xcassets/trashcan.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "trashcan.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "trashcan@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "trashcan@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/OTAnnotationSampleBundle/Assets.xcassets/trashcan.imageset/trashcan.png b/OTAnnotationSampleBundle/Assets.xcassets/trashcan.imageset/trashcan.png new file mode 100644 index 0000000..bc497b2 Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/trashcan.imageset/trashcan.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/trashcan.imageset/trashcan@2x.png b/OTAnnotationSampleBundle/Assets.xcassets/trashcan.imageset/trashcan@2x.png new file mode 100644 index 0000000..24f1f8b Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/trashcan.imageset/trashcan@2x.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/trashcan.imageset/trashcan@3x.png b/OTAnnotationSampleBundle/Assets.xcassets/trashcan.imageset/trashcan@3x.png new file mode 100644 index 0000000..c0a985e Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/trashcan.imageset/trashcan@3x.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/whiteCross.imageset/Contents.json b/OTAnnotationSampleBundle/Assets.xcassets/whiteCross.imageset/Contents.json new file mode 100644 index 0000000..19e4b84 --- /dev/null +++ b/OTAnnotationSampleBundle/Assets.xcassets/whiteCross.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "whiteCross.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "whiteCross@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "whiteCross@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/OTAnnotationSampleBundle/Assets.xcassets/whiteCross.imageset/whiteCross.png b/OTAnnotationSampleBundle/Assets.xcassets/whiteCross.imageset/whiteCross.png new file mode 100644 index 0000000..9ff6bde Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/whiteCross.imageset/whiteCross.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/whiteCross.imageset/whiteCross@2x.png b/OTAnnotationSampleBundle/Assets.xcassets/whiteCross.imageset/whiteCross@2x.png new file mode 100644 index 0000000..1570095 Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/whiteCross.imageset/whiteCross@2x.png differ diff --git a/OTAnnotationSampleBundle/Assets.xcassets/whiteCross.imageset/whiteCross@3x.png b/OTAnnotationSampleBundle/Assets.xcassets/whiteCross.imageset/whiteCross@3x.png new file mode 100644 index 0000000..75ada05 Binary files /dev/null and b/OTAnnotationSampleBundle/Assets.xcassets/whiteCross.imageset/whiteCross@3x.png differ diff --git a/OTAnnotationSampleBundle/Info.plist b/OTAnnotationSampleBundle/Info.plist new file mode 100644 index 0000000..11e1c44 --- /dev/null +++ b/OTAnnotationSampleBundle/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + NSHumanReadableCopyright + Copyright © 2016 Tokbox, Inc. All rights reserved. + NSPrincipalClass + + + diff --git a/OTAnnotationSampleBundle/OTAnnotationEditTextViewController.xib b/OTAnnotationSampleBundle/OTAnnotationEditTextViewController.xib new file mode 100644 index 0000000..f22fbda --- /dev/null +++ b/OTAnnotationSampleBundle/OTAnnotationEditTextViewController.xib @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTAnnotationSampleBundle/OTAnnotationScreenCaptureViewController.xib b/OTAnnotationSampleBundle/OTAnnotationScreenCaptureViewController.xib new file mode 100644 index 0000000..579bf75 --- /dev/null +++ b/OTAnnotationSampleBundle/OTAnnotationScreenCaptureViewController.xib @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTAnnotationTests/Info.plist b/OTAnnotationTests/Info.plist new file mode 100644 index 0000000..ba72822 --- /dev/null +++ b/OTAnnotationTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/OTAnnotationTests/OTAnnotationColorPickerViewTests.m b/OTAnnotationTests/OTAnnotationColorPickerViewTests.m new file mode 100644 index 0000000..3e32379 --- /dev/null +++ b/OTAnnotationTests/OTAnnotationColorPickerViewTests.m @@ -0,0 +1,35 @@ +// +// OTAnnotationColorPickerViewTests.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import +#import +#import "OTAnnotationColorPickerView.h" + +SPEC_BEGIN(OTAnnotationColorPickerViewTests) + +context(@"Initialization of OTAnnotationColorPickerView", ^(){ + + describe(@"An instance of OTAnnotationColorPickerView", ^(){ + + OTAnnotationColorPickerView *annotationColorPickerView = [[OTAnnotationColorPickerView alloc] init]; + + it(@"should not be nil", ^{ + [[annotationColorPickerView shouldNot] beNil]; + }); + }); + + describe(@"OTAnnotationColorPickerView", ^{ + + OTAnnotationColorPickerView *annotationColorPickerView = [[OTAnnotationColorPickerView alloc] init]; + + it(@"should be able to return background color", ^{ + [[[annotationColorPickerView selectedColor] shouldNot] beNil]; + }); + }); + +}); + +SPEC_END diff --git a/OTAnnotationTests/OTAnnotationDataManagerTests.m b/OTAnnotationTests/OTAnnotationDataManagerTests.m new file mode 100644 index 0000000..8993c15 --- /dev/null +++ b/OTAnnotationTests/OTAnnotationDataManagerTests.m @@ -0,0 +1,29 @@ +// +// OTAnnotationDataManagerTests.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import +#import +#import "OTAnnotationDataManager.h" + +SPEC_BEGIN(OTAnnotationDataManagerTests) + +describe(@"Initialization of OTAnnotationDataManager", ^(){ + + context(@"An instance of OTAnnotationDataManager", ^(){ + + OTAnnotationDataManager *dataManager = [[OTAnnotationDataManager alloc] init]; + + it(@"should not be nil", ^{ + [[dataManager shouldNot] beNil]; + }); + + it(@"Annotation array should not be nil", ^{ + [[dataManager.annotatable shouldNot] beNil]; + }); + }); +}); + +SPEC_END diff --git a/OTAnnotationTests/OTAnnotationEditTextViewControllerTests.m b/OTAnnotationTests/OTAnnotationEditTextViewControllerTests.m new file mode 100644 index 0000000..ae4ade2 --- /dev/null +++ b/OTAnnotationTests/OTAnnotationEditTextViewControllerTests.m @@ -0,0 +1,31 @@ +// +// OTAnnotationEditTextViewControllerTests.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import +#import +#import "OTAnnotationEditTextViewController.h" + +SPEC_BEGIN(OTAnnotationEditTextViewControllerTests) + +context(@"Initialization of OTAnnotationEditTextViewController", ^(){ + + describe(@"An instance of OTAnnotationEditTextViewController", ^(){ + + it(@"should not be nil", ^{ + [[[[OTAnnotationEditTextViewController alloc] init] should] beNil]; + }); + + it(@"init with text and color should not be nil", ^{ + [[[[OTAnnotationEditTextViewController alloc] initWithText:@"OTAnnotationEditTextViewController" textColor:[UIColor blueColor]] shouldNot] beNil]; + }); + + it(@"init with color should not be nil", ^{ + [[[[OTAnnotationEditTextViewController alloc] initWithTextColor:[UIColor blackColor]] shouldNot] beNil]; + }); + }); +}); + +SPEC_END diff --git a/OTAnnotationTests/OTAnnotationKitTests.m b/OTAnnotationTests/OTAnnotationKitTests.m new file mode 100644 index 0000000..12d1bf8 --- /dev/null +++ b/OTAnnotationTests/OTAnnotationKitTests.m @@ -0,0 +1,23 @@ +// +// OTAnnotationKitTests.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import +#import +#import "OTAnnotationKitBundle.h" + +SPEC_BEGIN(OTAnnotationKitTests) + +describe(@"Initialization of OTAnnotationKitBundle", ^(){ + + context(@"An instance of OTAnnotationKitBundle", ^(){ + + it(@"AnnotationBundle Factory should not be nil", ^{ + [[[OTAnnotationAcceleratorBundle annotationAcceleratorBundle] shouldNot] beNil]; + }); + }); +}); + +SPEC_END diff --git a/OTAnnotationTests/OTAnnotationPathTests.m b/OTAnnotationTests/OTAnnotationPathTests.m new file mode 100644 index 0000000..5ea7515 --- /dev/null +++ b/OTAnnotationTests/OTAnnotationPathTests.m @@ -0,0 +1,51 @@ +// +// OTAnnotationPathTests.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import +#import +#import "OTAnnotationPath.h" + +SPEC_BEGIN(OTAnnotationPathTests) + +describe(@"Initialization of OTAnnotationPath", ^(){ + + context(@"An instance of OTAnnotationPath with pathWithStrokeColor", ^(){ + + OTAnnotationPath *annotationPath = [[OTAnnotationPath alloc] initWithStrokeColor:[UIColor yellowColor]]; + it(@"should not be nil", ^{ + [[annotationPath shouldNot] beNil]; + }); + + it(@"stroke should not be nil", ^{ + [[annotationPath.strokeColor shouldNot] beNil]; + }); + + it(@"should had a stroke color yellow", ^{ + [[annotationPath.strokeColor should] equal:[UIColor yellowColor]]; + }); + + }); + + context(@"An instance of OTAnnotationPath with pathWithPoints", ^(){ + + OTAnnotationPath *annotationPath = [[OTAnnotationPath alloc] initWithPoints:@[] strokeColor:[UIColor greenColor]]; + + it(@"should not be nil", ^{ + [[annotationPath shouldNot] beNil]; + }); + + it(@"stroke should not be nil", ^{ + [[annotationPath.strokeColor shouldNot] beNil]; + }); + + it(@"should had a stroke color yellow", ^{ + [[annotationPath.strokeColor shouldNot] equal:[UIColor yellowColor]]; + }); + + }); +}); + +SPEC_END diff --git a/OTAnnotationTests/OTAnnotationScreenCaptureViewTests.m b/OTAnnotationTests/OTAnnotationScreenCaptureViewTests.m new file mode 100644 index 0000000..e89fefc --- /dev/null +++ b/OTAnnotationTests/OTAnnotationScreenCaptureViewTests.m @@ -0,0 +1,48 @@ +// +// OTAnnotationScreenCaptureView.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import +#import +#import "OTAnnotationScreenCaptureView.h" +#import "OTAnnotationScreenCaptureViewController.h" + +SPEC_BEGIN(OTAnnotationScreenCaptureViewTests) + +context(@"Initialization of OTAnnotationScreenCaptureView", ^(){ + + describe(@"An instance of OTAnnotationScreenCaptureView", ^(){ + + it(@"should not be nil", ^{ + [[[[OTAnnotationScreenCaptureModel alloc] init] shouldNot] beNil]; + }); + + it(@"should be able to init with Image", ^{ + UIImage *image = [[UIImage alloc] init]; + OTAnnotationScreenCaptureModel *screenCaptureModel = [[OTAnnotationScreenCaptureModel alloc] initWithSharedImage:image sharedDate:[NSDate date]]; + [[screenCaptureModel shouldNot] beNil]; + [[screenCaptureModel.sharedImage should] equal:image]; + }); + }); +}); + +context(@"Initialization of OTAnnotationScreenCaptureViewController", ^(){ + + describe(@"An instance of OTAnnotationScreenCaptureViewController", ^(){ + OTAnnotationScreenCaptureViewController *screenCaptureCtr = [[OTAnnotationScreenCaptureViewController alloc] init]; + UIImage *image = [[UIImage alloc] init]; + + it(@"should not be nil", ^{ + [[screenCaptureCtr shouldNot] beNil]; + }); + + it(@"should be able to set Image", ^{ + [screenCaptureCtr setSharedImage:image]; + [[screenCaptureCtr.sharedImage should] equal:image]; + }); + }); +}); + +SPEC_END diff --git a/OTAnnotationTests/OTAnnotationScrollViewTests.m b/OTAnnotationTests/OTAnnotationScrollViewTests.m new file mode 100644 index 0000000..21d6391 --- /dev/null +++ b/OTAnnotationTests/OTAnnotationScrollViewTests.m @@ -0,0 +1,55 @@ +// +// OTAnnotationScrollViewTests.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import +#import +#import "OTAnnotationScrollView.h" + +SPEC_BEGIN(OTAnnotationScrollViewTests) + +context(@"OTAnnotationScrollView", ^(){ + + describe(@"An instance of OTAnnotationScrollView", ^(){ + OTAnnotationScrollView *annotationView = [[OTAnnotationScrollView alloc] init]; + + it(@"should not be nil", ^{ + [[annotationView shouldNot] beNil]; + }); + + it(@"should not be nil when init with frame", ^{ + [[[[OTAnnotationScrollView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)] shouldNot] beNil]; + }); + }); + + describe(@"An instance of OTAnnotationScrollView should set variables", ^{ + + OTAnnotationScrollView *annotationScrollView = [[OTAnnotationScrollView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; + + it(@"annotationView", ^{ + [[annotationScrollView.annotationView should] beNil]; + }); + + it(@"isAnnotatable should be NO", ^{ + [[theValue(annotationScrollView.isAnnotatable) should] equal:theValue(NO)]; + }); + + it(@"isAnnotatable should be YES", ^{ + [annotationScrollView setAnnotatable:YES]; + [[theValue(annotationScrollView.isAnnotatable) should] equal:theValue(YES)]; + }); + + it(@"toolbar needs to be initialize first", ^{ + [[annotationScrollView.toolbarView should] beNil]; + }); + + it(@"after initializeToolbarView, toolbarView should not be nil", ^{ + [annotationScrollView initializeToolbarView]; + [[annotationScrollView.toolbarView shouldNot] beNil]; + }); + }); +}); + +SPEC_END diff --git a/OTAnnotationTests/OTAnnotationTextView.m b/OTAnnotationTests/OTAnnotationTextView.m new file mode 100644 index 0000000..ebfe927 --- /dev/null +++ b/OTAnnotationTests/OTAnnotationTextView.m @@ -0,0 +1,48 @@ +// +// OTAnnotationTextView.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import +#import +#import "OTAnnotationTextView.h" + +SPEC_BEGIN(OTAnnotationTextViewTest) + +describe(@"Initialization of OTAnnotationTextView", ^(){ + + context(@"An instance of OTAnnotationTextView", ^(){ + + it(@"default initializer should be nil", ^{ + [[[[OTAnnotationTextView alloc] init] should] beNil]; + }); + + it(@"initializer with color should not be nil", ^{ + [[[[OTAnnotationTextView alloc] initWithTextColor:[UIColor yellowColor]] shouldNot] beNil]; + [[[[OTAnnotationTextView alloc] initWithTextColor:nil] should] beNil]; + }); + + it(@"initializer with default properties should not be nil", ^{ + [[[[OTAnnotationTextView alloc] initWithText:@"" textColor:[UIColor greenColor] fontSize:0.0f] shouldNot] beNil]; + }); + + it(@"initializer with default properties for remote should not be nil", ^{ + [[[[OTAnnotationTextView alloc] initRemoteWithText:@"" textColor:[UIColor greenColor] fontSize:0.0f] shouldNot] beNil]; + }); + }); + + context(@"A new instance of OTAnnotationTextView", ^{ + + OTAnnotationTextView *annotationsText = [[OTAnnotationTextView alloc] initWithText:@"" textColor:[UIColor greenColor] fontSize:0.0f]; + + it(@"should have NO for editable properties", ^{ + + [[theValue(annotationsText.isResizable) should] equal:theValue(NO)]; + [[theValue(annotationsText.isDraggable) should] equal:theValue(NO)]; + [[theValue(annotationsText.isRotatable) should] equal:theValue(NO)]; + }); + }); +}); + +SPEC_END diff --git a/OTAnnotationTests/OTAnnotationViewTests.m b/OTAnnotationTests/OTAnnotationViewTests.m new file mode 100644 index 0000000..e3f235a --- /dev/null +++ b/OTAnnotationTests/OTAnnotationViewTests.m @@ -0,0 +1,29 @@ +// +// OTAnnotationViewTests.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import +#import +#import "OTAnnotationView.h" + +SPEC_BEGIN(OTAnnotationViewTests) + +describe(@"Initialization of OTAnnotationView", ^(){ + + context(@"An instance of OTAnnotationView", ^(){ + + OTAnnotationView *annotationView = [[OTAnnotationView alloc] init]; + + it(@"should not be nil", ^{ + [[annotationView shouldNot] beNil]; + }); + + it(@"annotationDataManager should not be nil", ^{ + [[annotationView.annotationDataManager shouldNot] beNil]; + }); + }); +}); + +SPEC_END diff --git a/OTAnnotationTests/OTFullScreenAnnotationViewControllerTests.m b/OTAnnotationTests/OTFullScreenAnnotationViewControllerTests.m new file mode 100644 index 0000000..478a86c --- /dev/null +++ b/OTAnnotationTests/OTFullScreenAnnotationViewControllerTests.m @@ -0,0 +1,25 @@ +// +// OTFullScreenAnnotationViewControllerTests.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import +#import +#import "OTFullScreenAnnotationViewController.h" + +SPEC_BEGIN(OTFullScreenAnnotationViewControllerTests) + +describe(@"Initialization of OTFullScreenAnnotationViewController", ^(){ + + context(@"An instance of OTFullScreenAnnotationViewController", ^(){ + + OTFullScreenAnnotationViewController *fullSreenAnnotationVC = [[OTFullScreenAnnotationViewController alloc] init]; + + it(@"should not be nil", ^{ + [[fullSreenAnnotationVC shouldNot] beNil]; + }); + }); +}); + +SPEC_END diff --git a/OTTextChatSample/AppDelegate.h b/OTTextChatSample/AppDelegate.h new file mode 100644 index 0000000..e58460f --- /dev/null +++ b/OTTextChatSample/AppDelegate.h @@ -0,0 +1,17 @@ +// +// AppDelegate.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import +#import "OTAcceleratorSession.h" + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + +- (OTAcceleratorSession *)getSharedAcceleratorSession; + +@end + diff --git a/OTTextChatSample/AppDelegate.m b/OTTextChatSample/AppDelegate.m new file mode 100644 index 0000000..e0a486b --- /dev/null +++ b/OTTextChatSample/AppDelegate.m @@ -0,0 +1,49 @@ +// +// AppDelegate.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "AppDelegate.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + +static OTAcceleratorSession *sharedSession; + +- (OTAcceleratorSession *)getSharedAcceleratorSession { + return sharedSession; +} + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Override point for customization after application launch. +// sharedSession = [[OTAcceleratorSession alloc] initWithOpenTokApiKey:<#apikey#> sessionId:<#sessionid#> token:<#token#>]; + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application { + // 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. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. +} + +- (void)applicationDidEnterBackground:(UIApplication *)application { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. +} + +- (void)applicationWillEnterForeground:(UIApplication *)application { + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + // 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. +} + +- (void)applicationWillTerminate:(UIApplication *)application { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. +} + +@end diff --git a/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Contents.json b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..c6f1e22 --- /dev/null +++ b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,186 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "filename" : "Icon-40@2x-1.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "Icon-40@3x-1.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "filename" : "Icon-40@2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "Icon-40@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "filename" : "Icon-60@2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "filename" : "Icon-60@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "Icon-Small.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "Icon-40@2x-2.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "Icon-40.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "filename" : "Icon-40@2x-3.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "Icon-76.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "filename" : "Icon-76@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "filename" : "Icon-83.5@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + }, + { + "idiom" : "watch", + "role" : "notificationCenter", + "scale" : "2x", + "size" : "24x24", + "subtype" : "38mm" + }, + { + "idiom" : "watch", + "role" : "notificationCenter", + "scale" : "2x", + "size" : "27.5x27.5", + "subtype" : "42mm" + }, + { + "filename" : "Icon-Small@2x.png", + "idiom" : "watch", + "role" : "companionSettings", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "Icon-Small@3x.png", + "idiom" : "watch", + "role" : "companionSettings", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "40x40", + "subtype" : "38mm" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "44x44", + "subtype" : "40mm" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "50x50", + "subtype" : "44mm" + }, + { + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "86x86", + "subtype" : "38mm" + }, + { + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "98x98", + "subtype" : "42mm" + }, + { + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "108x108", + "subtype" : "44mm" + }, + { + "idiom" : "watch-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-40.png b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-40.png new file mode 100644 index 0000000..1b341c4 Binary files /dev/null and b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-40.png differ diff --git a/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-1.png b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-1.png new file mode 100644 index 0000000..baf7e36 Binary files /dev/null and b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-1.png differ diff --git a/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-2.png b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-2.png new file mode 100644 index 0000000..baf7e36 Binary files /dev/null and b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-2.png differ diff --git a/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-3.png b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-3.png new file mode 100644 index 0000000..4e7e051 Binary files /dev/null and b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-3.png differ diff --git a/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png new file mode 100644 index 0000000..4e7e051 Binary files /dev/null and b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png differ diff --git a/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-40@3x-1.png b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-40@3x-1.png new file mode 100644 index 0000000..b82133a Binary files /dev/null and b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-40@3x-1.png differ diff --git a/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png new file mode 100644 index 0000000..3451273 Binary files /dev/null and b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png differ diff --git a/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png new file mode 100644 index 0000000..3451273 Binary files /dev/null and b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png differ diff --git a/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png new file mode 100644 index 0000000..bd9b0c0 Binary files /dev/null and b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png differ diff --git a/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-76.png b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-76.png new file mode 100644 index 0000000..4375342 Binary files /dev/null and b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-76.png differ diff --git a/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png new file mode 100644 index 0000000..17e2924 Binary files /dev/null and b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png differ diff --git a/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png new file mode 100644 index 0000000..a611c6b Binary files /dev/null and b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png differ diff --git a/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-Small.png b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-Small.png new file mode 100644 index 0000000..b61a7ef Binary files /dev/null and b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-Small.png differ diff --git a/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png new file mode 100644 index 0000000..51a49a2 Binary files /dev/null and b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png differ diff --git a/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png new file mode 100644 index 0000000..f0edce6 Binary files /dev/null and b/OTTextChatSample/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png differ diff --git a/OTTextChatSample/Assets.xcassets/Contents.json b/OTTextChatSample/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/OTTextChatSample/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/OTTextChatSample/Assets.xcassets/maximize.imageset/Contents.json b/OTTextChatSample/Assets.xcassets/maximize.imageset/Contents.json new file mode 100644 index 0000000..1f5a224 --- /dev/null +++ b/OTTextChatSample/Assets.xcassets/maximize.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "maximize.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "maximize@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "maximize@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/OTTextChatSample/Assets.xcassets/maximize.imageset/maximize.png b/OTTextChatSample/Assets.xcassets/maximize.imageset/maximize.png new file mode 100644 index 0000000..94dbe82 Binary files /dev/null and b/OTTextChatSample/Assets.xcassets/maximize.imageset/maximize.png differ diff --git a/OTTextChatSample/Assets.xcassets/maximize.imageset/maximize@2x.png b/OTTextChatSample/Assets.xcassets/maximize.imageset/maximize@2x.png new file mode 100644 index 0000000..76b9d08 Binary files /dev/null and b/OTTextChatSample/Assets.xcassets/maximize.imageset/maximize@2x.png differ diff --git a/OTTextChatSample/Assets.xcassets/maximize.imageset/maximize@3x.png b/OTTextChatSample/Assets.xcassets/maximize.imageset/maximize@3x.png new file mode 100644 index 0000000..1b7eee0 Binary files /dev/null and b/OTTextChatSample/Assets.xcassets/maximize.imageset/maximize@3x.png differ diff --git a/OTTextChatSample/Assets.xcassets/minimize.imageset/Contents.json b/OTTextChatSample/Assets.xcassets/minimize.imageset/Contents.json new file mode 100644 index 0000000..a891ac2 --- /dev/null +++ b/OTTextChatSample/Assets.xcassets/minimize.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "minimize.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "minimize@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "minimize@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/OTTextChatSample/Assets.xcassets/minimize.imageset/minimize.png b/OTTextChatSample/Assets.xcassets/minimize.imageset/minimize.png new file mode 100644 index 0000000..07fe616 Binary files /dev/null and b/OTTextChatSample/Assets.xcassets/minimize.imageset/minimize.png differ diff --git a/OTTextChatSample/Assets.xcassets/minimize.imageset/minimize@2x.png b/OTTextChatSample/Assets.xcassets/minimize.imageset/minimize@2x.png new file mode 100644 index 0000000..87df3cf Binary files /dev/null and b/OTTextChatSample/Assets.xcassets/minimize.imageset/minimize@2x.png differ diff --git a/OTTextChatSample/Assets.xcassets/minimize.imageset/minimize@3x.png b/OTTextChatSample/Assets.xcassets/minimize.imageset/minimize@3x.png new file mode 100644 index 0000000..4fb8bf8 Binary files /dev/null and b/OTTextChatSample/Assets.xcassets/minimize.imageset/minimize@3x.png differ diff --git a/OTTextChatSample/CustomDateFormatter.h b/OTTextChatSample/CustomDateFormatter.h new file mode 100644 index 0000000..2c6abf9 --- /dev/null +++ b/OTTextChatSample/CustomDateFormatter.h @@ -0,0 +1,17 @@ +// +// CustomDateFormatter.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import + +@interface CustomDateFormatter : NSObject + ++ (instancetype)sharedInstance; + ++ (NSString *)convertToTimeFromDate:(NSDate *)date; + ++ (NSString *)convertToTimestampFromDate:(NSDate *)date; + +@end diff --git a/OTTextChatSample/CustomDateFormatter.m b/OTTextChatSample/CustomDateFormatter.m new file mode 100644 index 0000000..b5ffe1b --- /dev/null +++ b/OTTextChatSample/CustomDateFormatter.m @@ -0,0 +1,51 @@ +// +// CustomDateFormatter.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "CustomDateFormatter.h" + +@interface CustomDateFormatter() +@property (nonatomic) NSDateFormatter *timeFormatter; +@property (nonatomic) NSDateFormatter *timeStampFormatter; +@end + +@implementation CustomDateFormatter + +- (NSDateFormatter *)timeFormatter { + if (!_timeFormatter) { + _timeFormatter = [[NSDateFormatter alloc] init]; + _timeFormatter.dateFormat = @"hh:mm a"; + } + return _timeFormatter; +} + +- (NSDateFormatter *)timeStampFormatter { + if (!_timeStampFormatter) { + _timeStampFormatter = [[NSDateFormatter alloc] init]; + _timeStampFormatter.dateFormat = @"EE MMM dd, hh:mm a"; + } + return _timeStampFormatter; +} + ++ (instancetype)sharedInstance { + + static CustomDateFormatter *sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [[CustomDateFormatter alloc] init]; + }); + return sharedInstance; +} + ++ (NSString *)convertToTimeFromDate:(NSDate *)date { + return [[CustomDateFormatter sharedInstance].timeFormatter stringFromDate:date]; +} + ++ (NSString *)convertToTimestampFromDate:(NSDate *)date { + + return [[CustomDateFormatter sharedInstance].timeStampFormatter stringFromDate:date]; +} + +@end diff --git a/OTTextChatSample/CustomReceiveTextChatTableViewCell.h b/OTTextChatSample/CustomReceiveTextChatTableViewCell.h new file mode 100644 index 0000000..8bb7c63 --- /dev/null +++ b/OTTextChatSample/CustomReceiveTextChatTableViewCell.h @@ -0,0 +1,13 @@ +// +// CustomReceiveTextChatTableViewCell.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import + +@interface CustomReceiveTextChatTableViewCell : UITableViewCell +@property (weak, nonatomic) IBOutlet UILabel *receiverAliasLabel; +@property (weak, nonatomic) IBOutlet UILabel *textLabel; +@property (weak, nonatomic) IBOutlet UILabel *timeLabel; +@end diff --git a/OTTextChatSample/CustomReceiveTextChatTableViewCell.m b/OTTextChatSample/CustomReceiveTextChatTableViewCell.m new file mode 100644 index 0000000..700d350 --- /dev/null +++ b/OTTextChatSample/CustomReceiveTextChatTableViewCell.m @@ -0,0 +1,17 @@ +// +// CustomReceiveTextChatTableViewCell.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "CustomReceiveTextChatTableViewCell.h" + +@implementation CustomReceiveTextChatTableViewCell +@synthesize textLabel; + +- (void)awakeFromNib { + [super awakeFromNib]; + self.backgroundColor = [UIColor clearColor]; +} + +@end diff --git a/OTTextChatSample/CustomReceiveTextChatTableViewCell.xib b/OTTextChatSample/CustomReceiveTextChatTableViewCell.xib new file mode 100644 index 0000000..dd354e8 --- /dev/null +++ b/OTTextChatSample/CustomReceiveTextChatTableViewCell.xib @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTTextChatSample/CustomSendTextChatTableViewCell.h b/OTTextChatSample/CustomSendTextChatTableViewCell.h new file mode 100644 index 0000000..c9d8c9c --- /dev/null +++ b/OTTextChatSample/CustomSendTextChatTableViewCell.h @@ -0,0 +1,14 @@ +// +// CustomSendTextChatTableViewCell.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import + +@interface CustomSendTextChatTableViewCell : UITableViewCell + +@property (weak, nonatomic) IBOutlet UILabel *textLabel; +@property (weak, nonatomic) IBOutlet UILabel *timeLabel; + +@end diff --git a/OTTextChatSample/CustomSendTextChatTableViewCell.m b/OTTextChatSample/CustomSendTextChatTableViewCell.m new file mode 100644 index 0000000..d57f8ef --- /dev/null +++ b/OTTextChatSample/CustomSendTextChatTableViewCell.m @@ -0,0 +1,17 @@ +// +// CustomSendTextChatTableViewCell.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "CustomSendTextChatTableViewCell.h" + +@implementation CustomSendTextChatTableViewCell +@synthesize textLabel; + +- (void)awakeFromNib { + [super awakeFromNib]; + self.backgroundColor = [UIColor clearColor]; +} + +@end diff --git a/OTTextChatSample/CustomSendTextChatTableViewCell.xib b/OTTextChatSample/CustomSendTextChatTableViewCell.xib new file mode 100644 index 0000000..3b7eef6 --- /dev/null +++ b/OTTextChatSample/CustomSendTextChatTableViewCell.xib @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTTextChatSample/CustomTextChatTableViewController.h b/OTTextChatSample/CustomTextChatTableViewController.h new file mode 100644 index 0000000..a3909e6 --- /dev/null +++ b/OTTextChatSample/CustomTextChatTableViewController.h @@ -0,0 +1,11 @@ +// +// CustomTextChatTableViewController.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTTextChatViewController.h" + +@interface CustomTextChatTableViewController : OTTextChatViewController + +@end diff --git a/OTTextChatSample/CustomTextChatTableViewController.m b/OTTextChatSample/CustomTextChatTableViewController.m new file mode 100644 index 0000000..289f83c --- /dev/null +++ b/OTTextChatSample/CustomTextChatTableViewController.m @@ -0,0 +1,193 @@ +// +// CustomTextChatTableViewController.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "CustomTextChatTableViewController.h" + +#import "CustomSendTextChatTableViewCell.h" +#import "CustomReceiveTextChatTableViewCell.h" +#import "CustomTimestampTextChatTableViewCell.h" + +#import "CustomDateFormatter.h" + +#import "OTTextChat.h" + +#import "AppDelegate.h" + +@interface CustomTextChatTableViewController() { + + // this is for storing all possible identifiers + // under bad networking or phone's sleep mode, a new identifier will be returned + // checking one identifier to determine the sender or receiver is not enough from textChatTableView:cellForRowAtIndexPath: + NSMutableSet *senderIdentifiers; +} +@property (nonatomic) OTTextChat *textChat; +@property (nonatomic) NSMutableArray *textMessages; +@end + +@implementation CustomTextChatTableViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + senderIdentifiers = [[NSMutableSet alloc] init]; + + [self styleUI]; + [self configureBlurBackground]; + [self configureCustomCells]; + [self.textChatInputView.sendButton addTarget:self action:@selector(sendTextMessage) forControlEvents:UIControlEventTouchUpInside]; + + [self setupTextMessage]; +} + +- (void)styleUI { + self.textChat = [[OTTextChat alloc] init]; + self.textChat.dataSource = self; + self.textChat.alias = @"BD Demo"; + self.textMessages = [[NSMutableArray alloc] init]; + + self.textChatNavigationBar.topItem.title = self.textChat.alias; + self.tableView.textChatTableViewDelegate = self; + self.tableView.backgroundColor = [UIColor blackColor]; + + self.textChatInputView.sendButton.layer.cornerRadius = 16.0f; + self.textChatInputView.sendButton.backgroundColor = [UIColor colorWithRed:2/255.0f green:132/255.0f blue:196/255.0f alpha:1.0f]; +} + +- (void)configureCustomCells { + [self.tableView registerNib:[UINib nibWithNibName:@"CustomSendTextChatTableViewCell" + bundle:[NSBundle mainBundle]] + forCellReuseIdentifier:@"CustomSendTextChatTableViewCell"]; + + [self.tableView registerNib:[UINib nibWithNibName:@"CustomReceiveTextChatTableViewCell" + bundle:[NSBundle mainBundle]] + forCellReuseIdentifier:@"CustomReceiveTextChatTableViewCell"]; + + [self.tableView registerNib:[UINib nibWithNibName:@"CustomTimestampTextChatTableViewCell" + bundle:[NSBundle mainBundle]] + forCellReuseIdentifier:@"CustomTimestampTextChatTableViewCell"]; +} + +- (void)configureBlurBackground { + UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]; + UIVisualEffectView *blurView = [[UIVisualEffectView alloc] initWithEffect: blurEffect]; + blurView.frame = [UIScreen mainScreen].bounds; + [self.view insertSubview:blurView atIndex:0]; + + self.providesPresentationContextTransitionStyle = YES; + self.definesPresentationContext = YES; + self.modalPresentationStyle = UIModalPresentationOverCurrentContext; +} + +- (void)setupTextMessage { + __weak CustomTextChatTableViewController *weakSelf = self; + [self.textChat connectWithHandler:^(OTTextChatConnectionEventSignal signal, OTConnection *connection, NSError *error) { + + if (!error) { + if (signal == OTTextChatConnectionEventSignalDidConnect) { + [self->senderIdentifiers addObject:self.textChat.selfConnection.connectionId]; + } + else if (signal == OTTextChatConnectionEventSignalDidDisconnect) { + NSLog(@"Text Chat is stopped"); + } + } + } messageHandler:^(OTTextChatMessageEventSignal signal, OTTextMessage *textMessage, NSError *error) { + if (signal == OTTextChatMessageEventSignalDidSendMessage || signal == OTTextChatMessageEventSignalDidReceiveMessage) { + + [weakSelf addTextMessage:textMessage]; + [weakSelf.tableView reloadData]; + [weakSelf scrollTextChatTableViewToBottom]; + + if (signal == OTTextChatMessageEventSignalDidSendMessage) { + weakSelf.textChatInputView.textField.text = nil; + } + } + }]; +} + +- (void)sendTextMessage { + [self.textChat sendMessage:self.textChatInputView.textField.text]; +} + +- (void)addTextMessage:(OTTextMessage *)textMessage { + + if (self.textMessages.count == 0) { + [self.textMessages addObject:textMessage.dateTime]; + } + else { + + OTTextMessage *prevTextMessage = self.textMessages[self.textMessages.count - 1]; + if ([textMessage.dateTime timeIntervalSinceDate:prevTextMessage.dateTime] > 120) { + [self.textMessages addObject:textMessage.dateTime]; + } + } + + [self.textMessages addObject:textMessage]; +} + +#pragma mark - OTTextChatTableViewDataSource +- (OTTextChatViewType)typeOfTextChatTableView:(OTTextChatTableView *)tableView { + + return OTTextChatViewTypeCustom; +} + +- (NSInteger)textChatTableView:(OTTextChatTableView *)tableView + numberOfRowsInSection:(NSInteger)section { + + return self.textMessages.count; +} + +- (OTTextMessage *)textChatTableView:(OTTextChatTableView *)tableView + textMessageItemAtIndexPath:(NSIndexPath *)indexPath { + return self.textMessages[indexPath.row]; +} + +- (UITableViewCell *)textChatTableView:(OTTextChatTableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath { + + id data = self.textMessages[indexPath.row]; + + NSString *cellIdentifier; + UITableViewCell *cell; + if ([data isKindOfClass:[OTTextMessage class]]) { + + OTTextMessage *textMessage = (OTTextMessage *)data; + if ([senderIdentifiers containsObject:textMessage.senderId]) { + cellIdentifier = @"CustomSendTextChatTableViewCell"; + } + else { + cellIdentifier = @"CustomReceiveTextChatTableViewCell"; + } + + cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; + if ([senderIdentifiers containsObject:textMessage.senderId]) { + CustomSendTextChatTableViewCell *sendCell = (CustomSendTextChatTableViewCell *)cell; + sendCell.textLabel.text = textMessage.text; + sendCell.timeLabel.text = [CustomDateFormatter convertToTimeFromDate:textMessage.dateTime]; + } + else { + CustomReceiveTextChatTableViewCell *receiveCell = (CustomReceiveTextChatTableViewCell *)cell; + receiveCell.textLabel.text = textMessage.text; + receiveCell.timeLabel.text = [CustomDateFormatter convertToTimeFromDate:textMessage.dateTime]; + receiveCell.receiverAliasLabel.text = textMessage.alias; + } + } + else if ([data isKindOfClass:[NSDate class]]) { + + cell = [tableView dequeueReusableCellWithIdentifier:@"CustomTimestampTextChatTableViewCell"]; + CustomTimestampTextChatTableViewCell *timestampCell = (CustomTimestampTextChatTableViewCell *)cell; + NSDate *date = (NSDate *)data; + timestampCell.timeStampLabel.text = [CustomDateFormatter convertToTimestampFromDate:date]; + } + + return cell; +} + +#pragma markr - +- (OTAcceleratorSession *)sessionOfOTTextChat:(OTTextChat *)textChat { + return [(AppDelegate*)[[UIApplication sharedApplication] delegate] getSharedAcceleratorSession]; +} + +@end diff --git a/OTTextChatSample/CustomTimestampTextChatTableViewCell.h b/OTTextChatSample/CustomTimestampTextChatTableViewCell.h new file mode 100644 index 0000000..4a5df06 --- /dev/null +++ b/OTTextChatSample/CustomTimestampTextChatTableViewCell.h @@ -0,0 +1,12 @@ +// +// CustomTimestampTextChatTableViewCell.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import + +@interface CustomTimestampTextChatTableViewCell : UITableViewCell +@property (weak, nonatomic) IBOutlet UILabel *timeStampLabel; + +@end diff --git a/OTTextChatSample/CustomTimestampTextChatTableViewCell.m b/OTTextChatSample/CustomTimestampTextChatTableViewCell.m new file mode 100644 index 0000000..aa815c2 --- /dev/null +++ b/OTTextChatSample/CustomTimestampTextChatTableViewCell.m @@ -0,0 +1,16 @@ +// +// CustomTimestampTextChatTableViewCell.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "CustomTimestampTextChatTableViewCell.h" + +@implementation CustomTimestampTextChatTableViewCell + +- (void)awakeFromNib { + [super awakeFromNib]; + self.backgroundColor = [UIColor clearColor]; +} + +@end diff --git a/OTTextChatSample/CustomTimestampTextChatTableViewCell.xib b/OTTextChatSample/CustomTimestampTextChatTableViewCell.xib new file mode 100644 index 0000000..76116aa --- /dev/null +++ b/OTTextChatSample/CustomTimestampTextChatTableViewCell.xib @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTTextChatSample/DefaultTextChatTableViewController.h b/OTTextChatSample/DefaultTextChatTableViewController.h new file mode 100644 index 0000000..324b6a3 --- /dev/null +++ b/OTTextChatSample/DefaultTextChatTableViewController.h @@ -0,0 +1,12 @@ +// +// DefaultTextChatTableViewController.h +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "OTTextChatViewController.h" + +@interface DefaultTextChatTableViewController : OTTextChatViewController + +@end + diff --git a/OTTextChatSample/DefaultTextChatTableViewController.m b/OTTextChatSample/DefaultTextChatTableViewController.m new file mode 100644 index 0000000..640e818 --- /dev/null +++ b/OTTextChatSample/DefaultTextChatTableViewController.m @@ -0,0 +1,162 @@ +// +// DefaultTextChatTableViewController.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import "DefaultTextChatTableViewController.h" +#import "OTTextChat.h" + +#import "AppDelegate.h" + +@interface DefaultTextChatTableViewController () { + NSUInteger maximumTextMessageLength; + UILabel *countLabel; +} +@property (nonatomic) OTTextChat *textChat; +@property (nonatomic) NSMutableArray *textMessages; +@end + +@implementation DefaultTextChatTableViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + maximumTextMessageLength = 120; + + self.textChat = [[OTTextChat alloc] init]; + self.textChat.dataSource = self; + self.textChat.alias = @"Tokboxer"; + self.textMessages = [[NSMutableArray alloc] init]; + + self.textChatNavigationBar.topItem.title = self.textChat.alias; + self.tableView.textChatTableViewDelegate = self; + self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; + self.textChatInputView.textField.delegate = self; + + __weak DefaultTextChatTableViewController *weakSelf = self; + [self.textChat connectWithHandler:^(OTTextChatConnectionEventSignal signal, OTConnection *connection, NSError *error) { + if (signal == OTTextChatConnectionEventSignalDidConnect) { + NSLog(@"Text Chat starts"); + } + else if (signal == OTTextChatConnectionEventSignalDidDisconnect) { + NSLog(@"Text Chat stops"); + } + } messageHandler:^(OTTextChatMessageEventSignal signal, OTTextMessage *message, NSError *error) { + + if (signal == OTTextChatMessageEventSignalDidSendMessage || signal == OTTextChatMessageEventSignalDidReceiveMessage) { + + if (!error) { + [weakSelf.textMessages addObject:message]; + [weakSelf.tableView reloadData]; + weakSelf.textChatInputView.textField.text = nil; + [weakSelf scrollTextChatTableViewToBottom]; + } + } + }]; + + [self.textChatInputView.sendButton addTarget:self action:@selector(sendTextMessage) forControlEvents:UIControlEventTouchUpInside]; + [self configureCountLabel]; + + if (@available(iOS 13, *)) { + self.view.backgroundColor = [UIColor systemBackgroundColor]; + } else { + self.view.backgroundColor = [UIColor whiteColor]; + } +} + +- (void)sendTextMessage { + [self.textChat sendMessage:self.textChatInputView.textField.text]; + [self updateLabel:0]; +} + +- (void)configureCountLabel { + countLabel = [[UILabel alloc] initWithFrame:CGRectZero]; + countLabel.translatesAutoresizingMaskIntoConstraints = NO; + countLabel.text = [NSString stringWithFormat:@"%@", @(maximumTextMessageLength)]; + countLabel.textAlignment = NSTextAlignmentCenter; + countLabel.font = [UIFont systemFontOfSize:10.0f]; + countLabel.textColor = [UIColor darkGrayColor]; + [self.textChatInputView.textField addSubview:countLabel]; + + [NSLayoutConstraint constraintWithItem:countLabel + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self.textChatInputView.textField + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:-2.0].active = YES; + [NSLayoutConstraint constraintWithItem:countLabel + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:self.textChatInputView.textField + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:0.0].active = YES; +} + +#pragma mark - OTTextChatTableViewDataSource +- (OTTextChatViewType)typeOfTextChatTableView:(OTTextChatTableView *)tableView { + + return OTTextChatViewTypeDefault; +} + +- (NSInteger)textChatTableView:(OTTextChatTableView *)tableView + numberOfRowsInSection:(NSInteger)section { + + return self.textMessages.count; +} + +- (OTTextMessage *)textChatTableView:(OTTextChatTableView *)tableView + textMessageItemAtIndexPath:(NSIndexPath *)indexPath { + return self.textMessages[indexPath.row]; +} + +- (UITableViewCell *)textChatTableView:(OTTextChatTableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath { + + return nil; +} + +#pragma mark - UITextFieldDelegate + +- (BOOL)textFieldShouldReturn:(UITextField *)textField { + [self sendTextMessage]; + return YES; +} + +- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { + // Allow a backspace always, in case we went over inputMaxChars + const char *_char = [string cStringUsingEncoding:NSUTF8StringEncoding]; + if (strcmp(_char, "\b") == -8) { + [self updateLabel:[textField.text length] - 1]; + return YES; + } + + // If it's not a backspace, allow it if we're still under 150 chars. + NSUInteger newLength = [textField.text length] + [string length] - range.length; + [self updateLabel: newLength]; + return (newLength >= maximumTextMessageLength) ? NO : YES; +} + + +-(void)updateLabel:(NSUInteger)Charlength { + countLabel.textColor = [UIColor darkGrayColor]; + + NSUInteger charLeft = maximumTextMessageLength - Charlength; + NSUInteger closeEnd = round(maximumTextMessageLength * .1); + if (closeEnd >= 100) closeEnd = 30; + if (charLeft <= closeEnd) { + countLabel.textColor = [UIColor redColor]; + countLabel.textColor = [UIColor redColor]; + } + NSString* charCountStr = [NSString stringWithFormat:@"%lu", (unsigned long)charLeft]; + countLabel.text = charCountStr; +} + +#pragma mark - OTTextChatDataSource +- (OTAcceleratorSession *)sessionOfOTTextChat:(OTTextChat *)textChat { + return [(AppDelegate*)[[UIApplication sharedApplication] delegate] getSharedAcceleratorSession]; +} + +@end diff --git a/OTTextChatSample/Info.plist b/OTTextChatSample/Info.plist new file mode 100644 index 0000000..bd1ead3 --- /dev/null +++ b/OTTextChatSample/Info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/OTTextChatSample/LaunchScreen.storyboard b/OTTextChatSample/LaunchScreen.storyboard new file mode 100644 index 0000000..f4fc7f7 --- /dev/null +++ b/OTTextChatSample/LaunchScreen.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTTextChatSample/Main.storyboard b/OTTextChatSample/Main.storyboard new file mode 100644 index 0000000..3bd24f0 --- /dev/null +++ b/OTTextChatSample/Main.storyboard @@ -0,0 +1,172 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTTextChatSample/OTTextChatSampleBundle.bundle/Assets.car b/OTTextChatSample/OTTextChatSampleBundle.bundle/Assets.car new file mode 100644 index 0000000..5080993 Binary files /dev/null and b/OTTextChatSample/OTTextChatSampleBundle.bundle/Assets.car differ diff --git a/OTTextChatSample/OTTextChatSampleBundle.bundle/Info.plist b/OTTextChatSample/OTTextChatSampleBundle.bundle/Info.plist new file mode 100644 index 0000000..7404ade Binary files /dev/null and b/OTTextChatSample/OTTextChatSampleBundle.bundle/Info.plist differ diff --git a/OTTextChatSample/OTTextChatSampleBundle.bundle/OTTextChatViewController.nib b/OTTextChatSample/OTTextChatSampleBundle.bundle/OTTextChatViewController.nib new file mode 100644 index 0000000..3791a70 Binary files /dev/null and b/OTTextChatSample/OTTextChatSampleBundle.bundle/OTTextChatViewController.nib differ diff --git a/OTTextChatSample/OTTextChatSampleBundle.bundle/TextChatComponentDivTableViewCell.nib b/OTTextChatSample/OTTextChatSampleBundle.bundle/TextChatComponentDivTableViewCell.nib new file mode 100644 index 0000000..d939701 Binary files /dev/null and b/OTTextChatSample/OTTextChatSampleBundle.bundle/TextChatComponentDivTableViewCell.nib differ diff --git a/OTTextChatSample/OTTextChatSampleBundle.bundle/TextChatReceivedShortTableViewCell.nib b/OTTextChatSample/OTTextChatSampleBundle.bundle/TextChatReceivedShortTableViewCell.nib new file mode 100644 index 0000000..147d950 Binary files /dev/null and b/OTTextChatSample/OTTextChatSampleBundle.bundle/TextChatReceivedShortTableViewCell.nib differ diff --git a/OTTextChatSample/OTTextChatSampleBundle.bundle/TextChatReceivedTableViewCell.nib b/OTTextChatSample/OTTextChatSampleBundle.bundle/TextChatReceivedTableViewCell.nib new file mode 100644 index 0000000..1d90cdd Binary files /dev/null and b/OTTextChatSample/OTTextChatSampleBundle.bundle/TextChatReceivedTableViewCell.nib differ diff --git a/OTTextChatSample/OTTextChatSampleBundle.bundle/TextChatSentShortTableViewCell.nib b/OTTextChatSample/OTTextChatSampleBundle.bundle/TextChatSentShortTableViewCell.nib new file mode 100644 index 0000000..93c34a6 Binary files /dev/null and b/OTTextChatSample/OTTextChatSampleBundle.bundle/TextChatSentShortTableViewCell.nib differ diff --git a/OTTextChatSample/OTTextChatSampleBundle.bundle/TextChatSentTableViewCell.nib b/OTTextChatSample/OTTextChatSampleBundle.bundle/TextChatSentTableViewCell.nib new file mode 100644 index 0000000..41b2cb2 Binary files /dev/null and b/OTTextChatSample/OTTextChatSampleBundle.bundle/TextChatSentTableViewCell.nib differ diff --git a/OTTextChatSample/OTTextChatSampleBundle.bundle/_CodeSignature/CodeDirectory b/OTTextChatSample/OTTextChatSampleBundle.bundle/_CodeSignature/CodeDirectory new file mode 100644 index 0000000..3764c2b Binary files /dev/null and b/OTTextChatSample/OTTextChatSampleBundle.bundle/_CodeSignature/CodeDirectory differ diff --git a/OTTextChatSample/OTTextChatSampleBundle.bundle/_CodeSignature/CodeRequirements b/OTTextChatSample/OTTextChatSampleBundle.bundle/_CodeSignature/CodeRequirements new file mode 100644 index 0000000..dbf9d61 Binary files /dev/null and b/OTTextChatSample/OTTextChatSampleBundle.bundle/_CodeSignature/CodeRequirements differ diff --git a/OTTextChatSample/OTTextChatSampleBundle.bundle/_CodeSignature/CodeRequirements-1 b/OTTextChatSample/OTTextChatSampleBundle.bundle/_CodeSignature/CodeRequirements-1 new file mode 100644 index 0000000..ce41061 Binary files /dev/null and b/OTTextChatSample/OTTextChatSampleBundle.bundle/_CodeSignature/CodeRequirements-1 differ diff --git a/OTTextChatSample/OTTextChatSampleBundle.bundle/_CodeSignature/CodeResources b/OTTextChatSample/OTTextChatSampleBundle.bundle/_CodeSignature/CodeResources new file mode 100644 index 0000000..4ab75b9 --- /dev/null +++ b/OTTextChatSample/OTTextChatSampleBundle.bundle/_CodeSignature/CodeResources @@ -0,0 +1,203 @@ + + + + + files + + Assets.car + + D4ONTRcuugSwjcHID/jn1e4Dh4Y= + + OTTextChatViewController.nib + + 3Sao6557MmuOsz20gTYhuXvfY4Q= + + TextChatComponentDivTableViewCell.nib + + Qw2ArVBWOUxqKGXJJk2dXSw/0vI= + + TextChatReceivedShortTableViewCell.nib + + tedfnNKKC6AI+EGR/lD1Es3yqCg= + + TextChatReceivedTableViewCell.nib + + ng2FWdLctsBGMOmsAk9HP8Jv8+Q= + + TextChatSentShortTableViewCell.nib + + TdfipuIJydxMzUt6q5PEFozskn8= + + TextChatSentTableViewCell.nib + + bOr21vo+eAJbAZlb1zNUvNan6DQ= + + + files2 + + Assets.car + + hash + + D4ONTRcuugSwjcHID/jn1e4Dh4Y= + + hash2 + + l8m6JTNP/fbmBYYzEdljpT419YCHpXxwLRZPVbVhenA= + + + OTTextChatViewController.nib + + hash + + 3Sao6557MmuOsz20gTYhuXvfY4Q= + + hash2 + + yLynIsTr1J68W5q+7hl80ku2hlS2u5lzoaRGXq1CCjU= + + + TextChatComponentDivTableViewCell.nib + + hash + + Qw2ArVBWOUxqKGXJJk2dXSw/0vI= + + hash2 + + bd6TNi07Amde8A76bXR8WHoChxFaOL6V4NFqxzujgJQ= + + + TextChatReceivedShortTableViewCell.nib + + hash + + tedfnNKKC6AI+EGR/lD1Es3yqCg= + + hash2 + + 8/RE1y55ktnK6oBCx3zf9hBG7YSIaRYt4mlgdg8eMxo= + + + TextChatReceivedTableViewCell.nib + + hash + + ng2FWdLctsBGMOmsAk9HP8Jv8+Q= + + hash2 + + iHLGai6+p8tBZnCoYu/occCjSFF74Hp6j5XeDrUVrkc= + + + TextChatSentShortTableViewCell.nib + + hash + + TdfipuIJydxMzUt6q5PEFozskn8= + + hash2 + + CXnb62n2kprRhVSV4OG2Eo7E+rIB9RCQMHlRMFXKdik= + + + TextChatSentTableViewCell.nib + + hash + + bOr21vo+eAJbAZlb1zNUvNan6DQ= + + hash2 + + 20BFcbPhWQ7h9C7B2OQHY0kxWxF1+2pUIUfDTS+vgaA= + + + + rules + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/OTTextChatSample/OTTextChatSampleBundle.bundle/_CodeSignature/CodeSignature b/OTTextChatSample/OTTextChatSampleBundle.bundle/_CodeSignature/CodeSignature new file mode 100644 index 0000000..e69de29 diff --git a/OTTextChatSample/main.m b/OTTextChatSample/main.m new file mode 100644 index 0000000..903eaa3 --- /dev/null +++ b/OTTextChatSample/main.m @@ -0,0 +1,14 @@ +// +// main.m +// +// Copyright © 2016 Tokbox, Inc. All rights reserved. +// + +#import +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/OTTextChatSampleBundle/Assets.xcassets/Contents.json b/OTTextChatSampleBundle/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/OTTextChatSampleBundle/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/OTTextChatSampleBundle/Assets.xcassets/close_x_white.imageset/Contents.json b/OTTextChatSampleBundle/Assets.xcassets/close_x_white.imageset/Contents.json new file mode 100644 index 0000000..afb71aa --- /dev/null +++ b/OTTextChatSampleBundle/Assets.xcassets/close_x_white.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "close_x_white.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "close_x_white@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "close_x_white@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/OTTextChatSampleBundle/Assets.xcassets/close_x_white.imageset/close_x_white.png b/OTTextChatSampleBundle/Assets.xcassets/close_x_white.imageset/close_x_white.png new file mode 100644 index 0000000..9ff6bde Binary files /dev/null and b/OTTextChatSampleBundle/Assets.xcassets/close_x_white.imageset/close_x_white.png differ diff --git a/OTTextChatSampleBundle/Assets.xcassets/close_x_white.imageset/close_x_white@2x.png b/OTTextChatSampleBundle/Assets.xcassets/close_x_white.imageset/close_x_white@2x.png new file mode 100644 index 0000000..1570095 Binary files /dev/null and b/OTTextChatSampleBundle/Assets.xcassets/close_x_white.imageset/close_x_white@2x.png differ diff --git a/OTTextChatSampleBundle/Assets.xcassets/close_x_white.imageset/close_x_white@3x.png b/OTTextChatSampleBundle/Assets.xcassets/close_x_white.imageset/close_x_white@3x.png new file mode 100644 index 0000000..75ada05 Binary files /dev/null and b/OTTextChatSampleBundle/Assets.xcassets/close_x_white.imageset/close_x_white@3x.png differ diff --git a/OTTextChatSampleBundle/Info.plist b/OTTextChatSampleBundle/Info.plist new file mode 100644 index 0000000..3563e56 --- /dev/null +++ b/OTTextChatSampleBundle/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + NSHumanReadableCopyright + Copyright © 2020 Tokbox, Inc. All rights reserved. + NSPrincipalClass + + + diff --git a/OTTextChatSampleBundle/OTTextChatViewController.xib b/OTTextChatSampleBundle/OTTextChatViewController.xib new file mode 100644 index 0000000..31df9c7 --- /dev/null +++ b/OTTextChatSampleBundle/OTTextChatViewController.xib @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTTextChatSampleBundle/TextChatComponentDivTableViewCell.xib b/OTTextChatSampleBundle/TextChatComponentDivTableViewCell.xib new file mode 100644 index 0000000..ae30f25 --- /dev/null +++ b/OTTextChatSampleBundle/TextChatComponentDivTableViewCell.xib @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/OTTextChatSampleBundle/TextChatReceivedShortTableViewCell.xib b/OTTextChatSampleBundle/TextChatReceivedShortTableViewCell.xib new file mode 100644 index 0000000..9c8ddb6 --- /dev/null +++ b/OTTextChatSampleBundle/TextChatReceivedShortTableViewCell.xib @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. + + + + + + + + + + + + + + + + + + + diff --git a/OTTextChatSampleBundle/TextChatReceivedTableViewCell.xib b/OTTextChatSampleBundle/TextChatReceivedTableViewCell.xib new file mode 100644 index 0000000..ffb291e --- /dev/null +++ b/OTTextChatSampleBundle/TextChatReceivedTableViewCell.xib @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTTextChatSampleBundle/TextChatSentShortTableViewCell.xib b/OTTextChatSampleBundle/TextChatSentShortTableViewCell.xib new file mode 100644 index 0000000..dbd6856 --- /dev/null +++ b/OTTextChatSampleBundle/TextChatSentShortTableViewCell.xib @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. + + + + + + + + + + + + + + + + + + + diff --git a/OTTextChatSampleBundle/TextChatSentTableViewCell.xib b/OTTextChatSampleBundle/TextChatSentTableViewCell.xib new file mode 100644 index 0000000..900bc05 --- /dev/null +++ b/OTTextChatSampleBundle/TextChatSentTableViewCell.xib @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTTextChatTests/Info.plist b/OTTextChatTests/Info.plist new file mode 100644 index 0000000..ba72822 --- /dev/null +++ b/OTTextChatTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/OTTextChatTests/OTTextChatAcceleratorTests.m b/OTTextChatTests/OTTextChatAcceleratorTests.m new file mode 100644 index 0000000..eabd0dc --- /dev/null +++ b/OTTextChatTests/OTTextChatAcceleratorTests.m @@ -0,0 +1,41 @@ +// +// OTTextChatAcceleratorTests.m +// OTTextChatAccelerator +// +// Created by Xi Huang on 2/6/17. +// Copyright © 2017 Tokbox, Inc. All rights reserved. +// + +#import +#import "OTTextChatViewController.h" +#import "OTTextChatAcceleratorBundle.h" + +@interface OTTextChatAcceleratorTests : XCTestCase + +@end + +@implementation OTTextChatAcceleratorTests + +- (void)testAcceleratorCoreBundle { + XCTAssertNotNil([OTTextChatAcceleratorBundle textChatAcceleratorBundle]); +} + +- (void)testTextChatViewControllerNib { + NSBundle *textChatViewBundle = [OTTextChatAcceleratorBundle textChatAcceleratorBundle]; + XCTAssertTrue([textChatViewBundle loadNibNamed:@"TextChatSentTableViewCell" owner:self options:nil].count == 1); + + OTTextChatViewController *textChatViewController = [OTTextChatViewController textChatViewController]; + XCTAssertNotNil(textChatViewController); +} + +- (void)testTextChatTableViewCellNibs { + + NSBundle *textChatViewBundle = [OTTextChatAcceleratorBundle textChatAcceleratorBundle]; + XCTAssertTrue([textChatViewBundle loadNibNamed:@"TextChatSentShortTableViewCell" owner:self options:nil].count == 1); + XCTAssertTrue([textChatViewBundle loadNibNamed:@"TextChatReceivedTableViewCell" owner:self options:nil].count == 1); + XCTAssertTrue([textChatViewBundle loadNibNamed:@"TextChatReceivedShortTableViewCell" owner:self options:nil].count == 1); + XCTAssertTrue([textChatViewBundle loadNibNamed:@"TextChatComponentDivTableViewCell" owner:self options:nil].count == 1); + XCTAssertTrue([textChatViewBundle loadNibNamed:@"TextChatComponentDivTableViewCell" owner:self options:nil].count == 1); +} + +@end diff --git a/OTTextChatTests/OTTextChatTests.m b/OTTextChatTests/OTTextChatTests.m new file mode 100644 index 0000000..3df2ca5 --- /dev/null +++ b/OTTextChatTests/OTTextChatTests.m @@ -0,0 +1,33 @@ +// +// OTTextChatTests.m +// OTTextChatAccelerator +// +// Created by Xi Huang on 2/6/17. +// Copyright © 2017 Tokbox, Inc. All rights reserved. +// + +#import +#import +#import "OTTextChat.h" + +@interface OTTextChatTests : XCTestCase + +@end + +@implementation OTTextChatTests + +- (void)testTextChatInit { + OTTextChat *textchat = [[OTTextChat alloc] init]; + XCTAssertNotNil(textchat); + XCTAssertNil(textchat.alias); + XCTAssertNil(textchat.selfConnection); + XCTAssertNil(textchat.dataSource); +} + +- (void)testTextChatAlias { + OTTextChat *textchat = [[OTTextChat alloc] init]; + textchat.alias = @"textchatalias"; + XCTAssertTrue([textchat.alias isEqualToString:@"textchatalias"]); +} + +@end diff --git a/OTTextChatTests/OTTextChatUserInterface.m b/OTTextChatTests/OTTextChatUserInterface.m new file mode 100644 index 0000000..c32486f --- /dev/null +++ b/OTTextChatTests/OTTextChatUserInterface.m @@ -0,0 +1,31 @@ +// +// OTTextChatUserInterface.m +// OTTextChatAccelerator +// +// Created by Xi Huang on 4/5/17. +// Copyright © 2017 Tokbox, Inc. All rights reserved. +// + +#import +#import "OTTextChatNavigationBar.h" +#import "OTTextChatViewController.h" + +@interface OTTextChatUserInterface : XCTestCase + +@end + +@implementation OTTextChatUserInterface + +- (void)testNavigationBar { + OTTextChatNavigationBar *bar = [[OTTextChatNavigationBar alloc] init]; + XCTAssertTrue([bar.barTintColor isEqual:[UIColor colorWithRed:70/255.0f green:156/255.0f blue:178/255.0f alpha:1.0f]]); + XCTAssertTrue([bar.tintColor isEqual:[UIColor whiteColor]]); + XCTAssertTrue(bar.translatesAutoresizingMaskIntoConstraints == NO); +} + +- (void)testTextChatViewController { + OTTextChatViewController *vc = [OTTextChatViewController textChatViewController]; + XCTAssertNotNil(vc); +} + +@end diff --git a/OTTextChatTests/OTTextMessageTests.m b/OTTextChatTests/OTTextMessageTests.m new file mode 100644 index 0000000..aca8f12 --- /dev/null +++ b/OTTextChatTests/OTTextMessageTests.m @@ -0,0 +1,74 @@ +// +// OTTextMessageTests.m +// OTTextChatKit +// +// Created by Xi Huang on 1/30/17. +// Copyright © 2017 Tokbox, Inc. All rights reserved. +// + +#import +#import "OTTextMessage.h" +#import "OTTextMessage_Private.h" + +@interface OTTextMessageTests : XCTestCase + +@end + +@implementation OTTextMessageTests + +- (void)testTextMessageInitFactory { + OTTextMessage *tc = [OTTextMessage messageWithSenderId:@"1234" alias:@"Bob" text:@"text"]; + XCTAssertNotNil(tc); + XCTAssertNotNil(tc.dateTime); + XCTAssertTrue([tc.text isEqualToString:@"text"]); + XCTAssertTrue([tc.alias isEqualToString:@"Bob"]); + XCTAssertTrue([tc.senderId isEqualToString:@"1234"]); + XCTAssertTrue(tc.type == TCMessageTypesSent); +} + +- (void)testTextMessageInit { + + NSDate *date = [NSDate date]; + OTTextMessage *tc = [[OTTextMessage alloc] initWithSenderId:@"1234" + alias:@"Bob" + dateTime:date + text:@"text"]; + XCTAssertNotNil(tc); + XCTAssertTrue(tc.dateTime == date); + XCTAssertTrue([tc.text isEqualToString:@"text"]); + XCTAssertTrue([tc.alias isEqualToString:@"Bob"]); + XCTAssertTrue([tc.senderId isEqualToString:@"1234"]); + XCTAssertTrue(tc.type == TCMessageTypesSent); +} + +- (void)testTextMessagType { + OTTextMessage *tc = [OTTextMessage messageWithSenderId:@"1234" alias:@"Bob" text:@"text"]; + tc.type = TCMessageTypesReceived; + XCTAssertTrue(tc.type == TCMessageTypesReceived); + + tc.type = TCMessageTypesSentShort; + XCTAssertTrue(tc.type == TCMessageTypesSentShort); + + tc.type = TCMessageTypesReceivedShort; + XCTAssertTrue(tc.type == TCMessageTypesReceivedShort); +} + +- (void)testJSONParser { + NSDate *date = [NSDate date]; + OTTextMessage *tc = [[OTTextMessage alloc] initWithSenderId:@"1234" + alias:@"Bob" + dateTime:date + text:@"text"]; + tc.customData = @{@"config":@"VGA"}; + NSString *jsonString = [tc getTextChatSignalJSONString]; + XCTAssertNotNil(jsonString); + OTTextMessage *tc1 = [[OTTextMessage alloc] initWithJSONString:jsonString]; + + XCTAssertTrue([tc.senderId isEqualToString:tc1.senderId]); + XCTAssertTrue([tc.alias isEqualToString:tc1.alias]); + XCTAssertTrue([tc.text isEqualToString:tc1.text]); +// XCTAssertTrue([tc.dateTime isEqualToDate:tc1.dateTime]); + XCTAssertTrue([tc.customData isEqualToDictionary:tc1.customData]); +} + +@end diff --git a/Podfile b/Podfile index f93dcc3..ceca13a 100644 --- a/Podfile +++ b/Podfile @@ -2,12 +2,37 @@ project 'OTAcceleratorCore.xcodeproj' platform :ios, '11.0' -target 'OTAcceleratorCore' do +def opentok_pod pod 'OpenTok', '2.18.0' - pod 'OTKAnalytics', '= 2.1.2' - pod 'SVProgressHUD', '= 2.2.5' +end + +def shared_pods + opentok_pod + pod 'OTKAnalytics', '~> 2.1.2' +end + +target 'OTAcceleratorCore' do + shared_pods + pod 'SVProgressHUD', '~> 2.2.5' end target 'OTAcceleratorCoreTests' do - pod 'OpenTok', '2.18.0' + opentok_pod end + +target 'OTTextChatSample' do + shared_pods +end + +target 'OTTextChatTests' do + opentok_pod +end + +target 'OTAnnotationSample' do + shared_pods +end + +target 'OTAnnotationTests' do + opentok_pod + pod 'Kiwi' +end \ No newline at end of file diff --git a/Podfile.lock b/Podfile.lock index 0cc4375..2a40fee 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,24 +1,28 @@ PODS: + - Kiwi (3.0.0) - OpenTok (2.18.0) - OTKAnalytics (2.1.2) - SVProgressHUD (2.2.5) DEPENDENCIES: + - Kiwi - OpenTok (= 2.18.0) - - OTKAnalytics (= 2.1.2) - - SVProgressHUD (= 2.2.5) + - OTKAnalytics (~> 2.1.2) + - SVProgressHUD (~> 2.2.5) SPEC REPOS: trunk: + - Kiwi - OpenTok - OTKAnalytics - SVProgressHUD SPEC CHECKSUMS: + Kiwi: fbeafef0f00e4d8f7dcb3420a4930afe70af77f7 OpenTok: bf4c81ff741e7976542c61b8f317882f8076eede OTKAnalytics: 2de2f8971b5af7f313882ada0b1eae25104296f4 SVProgressHUD: 1428aafac632c1f86f62aa4243ec12008d7a51d6 -PODFILE CHECKSUM: d986e7a10a719418ba2d34cdd18b3504fc3add79 +PODFILE CHECKSUM: ae6e6613dcc2470123bfbe5ca9e5834f241c46f5 -COCOAPODS: 1.9.3 +COCOAPODS: 1.10.0 diff --git a/README.md b/README.md index 7f46680..f1c9b8f 100644 --- a/README.md +++ b/README.md @@ -8,15 +8,26 @@ Tokbox is now known as Vonage -The Accelerator Core is an easy solution to integrate audio/video communication to any iOS applications via OpenTok platform. Things you can easily do: +The Accelerator Core is a solution to integrate audio/video communication to any iOS applications via OpenTok platform. [Accelerator TextChat](https://github.com/opentok/accelerator-textchat-ios) and [Accelerator Annotation](https://github.com/opentok/accelerator-annotation-ios) have been deprecated and are now part of Accelerator Core. +Accelerator Core helps you with: + +Core: - one to one audio/video call - multiparty audio/video call - one to one screen sharing - multiparty screen sharing - UI components for handling audio/video enable&disable -# Configure, build and run the sample app +TextChat: +- one to one text chat + +Annotation: +- one to one audio/video call with annotations + +# Configure, build and run the sample apps + +The Accelerator Core workspace contains 3 sample apps, one for the Core, TextChat and Annotation components. 1. Get values for **API Key**, **Session ID**, and **Token**. See [Obtaining OpenTok Credentials](#obtaining-opentok-credentials) for important information. @@ -46,7 +57,7 @@ The Accelerator Core is an easy solution to integrate audio/video communication [Accelerator Core Android](https://github.com/opentok/accelerator-core-android) -## Sample Codes +## Core Sample Code Each communicator instance will take the OpenTok session from OTOneToOneCommunicatorDataSource, so this applies to each communicator instance: @@ -125,7 +136,7 @@ Each communicator instance will take the OpenTok session from OTOneToOneCommunic self.communicator.publisherView.handleAudioVideo = NO; ``` -## Ready-in-Use Components +### Core Ready-to-Use Components - One-to-One communication @@ -137,25 +148,159 @@ OTOneToOneCommunicationController *vc = [OTOneToOneCommunicationController oneTo [self.navigationController pushViewController:vc animated:YES]; ``` -## Sample Apps that uses the Core +## TextChat Sample Code -The following sample apps use `Accelerator Core`: +- Passing the session -- [OpenTok One-to-One Communication Sample App](https://github.com/opentok/one-to-one-sample-apps) -- [Accelerator Sample App](https://github.com/opentok/accelerator-sample-apps-ios) + ```objc + - (OTAcceleratorSession *)sessionOfOTOneToOneCommunicator:(OTOneToOneCommunicator *)oneToOneCommunicator { + return <#OTAcceleratorSession#>; + } + ``` -### Obtaining OpenTok Credentials +- Start signaling text chat data -To use OpenTok's framework you need a Session ID, a Token, and an API Key. You can get these values at the [OpenTok Developer Dashboard](https://dashboard.tokbox.com/) . For production deployment, you must generate the Session ID and Token values using one of the [OpenTok Server SDKs](https://tokbox.com/developer/sdks/server/). + ```objc + // we assume self owns a table tableView + [self.textChat connectWithHandler:^(OTTextChatConnectionEventSignal signal, OTConnection *connection, NSError *error) { + if (signal == OTTextChatConnectionEventSignalDidConnect) { + NSLog(@"Text Chat starts"); + } + else if (signal == OTTextChatConnectionEventSignalDidDisconnect) { + NSLog(@"Text Chat stops"); + } + } messageHandler:^(OTTextChatMessageEventSignal signal, OTTextMessage *message, NSError *error) { + if (signal == OTTextChatMessageEventSignalDidSendMessage || signal == OTTextChatMessageEventSignalDidReceiveMessage) { + if (!error) { + [weakSelf.textMessages addObject:message]; + [weakSelf.tableView reloadData]; + } + } + }]; + ``` + +- Stop signaling text chat data + + ```objc + [self.textchat disconnect]; + ``` + +### JSON Requirements for Text Chat Signaling + +The JSON used when using the OpenTok signaling API with the OpenTok Text Chat component describes the information used when submitting a chat message. This information includes the date, chat message text, sender alias, and sender ID. The JSON is formatted as shown in this example: + +``` javascript +// var type = "text-chat" +``` + +```json +{ + "sentOn" : 1462396461923.305, + "text" : "Hi", + "sender" : { + "alias" : "Tokboxer", + "id" : "16FEB40D-C09B-4491-A983-44677B7EBB3E" + } +} +``` -## Accelerator Core as a dependency +This formatted JSON is converted to a string, which is submitted to the OpenTok signaling API. For more information, see: -The Accelerator Core is required whenever you use any of the OpenTok accelerators. The Accelerator Core is a common layer that includes the audio-video communication logic contained in all [OpenTok One-to-One Communication Sample Apps](https://github.com/opentok/one-to-one-sample-apps), and permits all accelerators and samples to share the same OpenTok session. The accelerator packs and sample app access the OpenTok session through the Common Accelerator Session Pack layer, which allows them to share a single OpenTok session: +- [Signaling - JavaScript](https://tokbox.com/developer/guides/signaling/js/) +- [Signaling - iOS](https://tokbox.com/developer/guides/signaling/ios/) +- [Signaling - Android](https://tokbox.com/developer/guides/signaling/android/) + +For testing text chat, we include a simple web app to make it easier: [Browser-Demo-TextChat](https://github.com/opentok/accelerator-core-ios/blob/master/browser-demo-textchat.html). Simply open it and replace the corresponding API Key, Session ID, and Token values. Then save and load it to the browser. + +## Annotation sample code + +The `OTAnnotationScrollView` class is the backbone of the annotation features in this Sample. + + +```objc +self.annotationView = [[OTAnnotationScrollView alloc] init]; +self.annotationView.frame = <# desired frame #>; +[self.annotationView initializeToolbarView]; +self.annotationView.toolbarView.frame = <# desired frame #>; +``` + +If you would like to be annotated on either the entire screen or a specified portion of the screen: + +```objc +self.annotator = [[OTAnnotator alloc] init]; +[self.annotator connectForReceivingAnnotationWithSize:<# desired size #> + completionHandler:^(OTAnnotationSignal signal, NSError *error) { + if (signal == OTAnnotationSessionDidConnect){ + self.annotator.annotationScrollView.frame = self.view.bounds; + [self.view addSubview:self.annotator.annotationScrollView]; + } + }]; + +self.annotator.dataReceivingHandler = ^(NSArray *data) { + NSLog(@"%@", data); +}; +``` + +If you would like to annotate on a remote client's screen: + +```objc +self.annotator = [[OTAnnotator alloc] init]; +[self.annotator connectForSendingAnnotationWithSize:self.sharer.subscriberView.frame.size + completionHandler:^(OTAnnotationSignal signal, NSError *error) { + + if (signal == OTAnnotationSessionDidConnect){ + + // configure annotation view + self.annotator.annotationScrollView.frame = self.view.bounds; + [self.view addSubview:self.annotator.annotationScrollView]; + + // self.sharer.subscriberView is the screen shared from a remote client. + // It does not make sense to `connectForSendingAnnotationWithSize` if you don't receive a screen sharing. + [self.annotator.annotationScrollView addContentView:self.sharer.subscriberView]; + + // configure annotation feature + self.annotator.annotationScrollView.annotatable = YES; + self.annotator.annotationScrollView.annotationView.currentAnnotatable = [OTAnnotationPath pathWithStrokeColor:[UIColor yellowColor]]; + } + }]; + +self.annotator.dataSendingHandler = ^(NSArray *data, NSError *error) { + NSLog(@"%@", data); +}; +``` + +### Class design + +The following classes represent the software design for the OpenTok Annotations Accelerator Pack. + +| Class | Description | +| -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `OTAnnotator` | The core component for enabling remote annotation across devices and platforms. | +| `OTAnnotationScrollView` | Provides essentials components for annotating on either the entire screen or a specified portion of the screen. | +| `OTAnnotationToolbarView` | A convenient annotation toolbar that is optionally available for your development. As an alternative, you can create your own toolbar using `OTAnnotationScrollView`. | +| `OTFullScreenAnnotationViewController` | A convenient view controller enables you to annotate the whole screen immediately. | + +### 3rd Party Libraries + +- [LHToolbar](https://github.com/Lucashuang0802/LHToolbar) + +## Sample Apps that uses the Core + +The following sample apps use `Accelerator Core`: + +- [Accelerator Sample Apps](https://github.com/opentok/accelerator-sample-apps-ios) + +The accelerator and sample app access the OpenTok session through the Accelerator Session Pack layer, which allows them to share a single OpenTok session: ![architecture](./accpackarch.png) On the Android and iOS mobile platforms, when you try to set a listener (Android) or delegate (iOS), it is not normally possible to set multiple listeners or delegates for the same event. For example, on these mobile platforms you can only set one OpenTok signal listener. The Common Accelerator Session Pack, however, allows you to set up several listeners for the same event. +### Obtaining OpenTok Credentials + +To use OpenTok's framework you need a Session ID, a Token, and an API Key. You can get these values at the [OpenTok Developer Dashboard](https://dashboard.tokbox.com/) . For production deployment, you must generate the Session ID and Token values using one of the [OpenTok Server SDKs](https://tokbox.com/developer/sdks/server/). + + ## Development and Contributing Interested in contributing? We :heart: pull requests! See the [Contribution](CONTRIBUTING.md) guidelines. diff --git a/browser-demo-textchat.html b/browser-demo-textchat.html new file mode 100644 index 0000000..6964b3b --- /dev/null +++ b/browser-demo-textchat.html @@ -0,0 +1,98 @@ + + + + + OpenTok Test + + + +
+ + + + +