Initial commit

This commit is contained in:
Tha_14
2024-02-22 21:43:11 +02:00
commit 1b96a031d2
1108 changed files with 157706 additions and 0 deletions

View File

@ -0,0 +1,12 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end

View File

@ -0,0 +1,60 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#import "AppDelegate.h"
#import "OCTStartDemoViewController.h"
#import "DDLog.h"
#import "DDASLLogger.h"
#import "DDTTYLogger.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
[DDLog addLogger:[DDASLLogger sharedInstance]];
[DDLog addLogger:[DDTTYLogger sharedInstance]];
self.window.rootViewController = [OCTStartDemoViewController new];
[self.window makeKeyAndVisible];
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

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6214" systemVersion="14A314h" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6207"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Copyright (c) 2015 dvor. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
<rect key="frame" x="20" y="439" width="441" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="iOSDemo" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
<rect key="frame" x="20" y="140" width="441" height="43"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
<constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="548" y="455"/>
</view>
</objects>
</document>

View File

@ -0,0 +1,93 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>me.dvor.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@ -0,0 +1,9 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#import "OCTTableViewController.h"
@interface OCTCallsViewController : OCTTableViewController
@end

View File

@ -0,0 +1,244 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#import <BlocksKit/UIActionSheet+BlocksKit.h>
#import <BlocksKit/UIAlertView+BlocksKit.h>
#import <BlocksKit/UIBarButtonItem+BlocksKit.h>
#import "OCTCallsViewController.h"
#import "OCTSubmanagerCalls.h"
#import "OCTSubmanagerObjects.h"
#import "OCTCall.h"
#import "OCTVideoViewController.h"
@interface OCTCallsViewController ()
@property (strong, nonatomic) RLMResults<OCTCall *> *calls;
@property (strong, nonatomic) RLMNotificationToken *callsNotificationToken;
@property (strong, nonatomic) OCTCall *selectedCall;
@end
@implementation OCTCallsViewController
#pragma mark - Lifecycle
- (instancetype)initWithManager:(id<OCTManager>)manager
{
self = [super initWithManager:manager];
if (! self) {
return nil;
}
_calls = [self.manager.objects objectsForType:OCTFetchRequestTypeCall predicate:nil];
self.title = @"Calls";
return self;
}
- (void)dealloc
{
[self.callsNotificationToken invalidate];
}
- (void)viewDidLoad
{
[super viewDidLoad];
__weak typeof(self)weakSelf = self;
self.callsNotificationToken = [self.calls addNotificationBlock:^(RLMResults *results, RLMCollectionChange *changes, NSError *error) {
if (error) {
NSLog(@"Failed to open Realm on background worker: %@", error);
return;
}
UITableView *tableView = weakSelf.tableView;
// Initial run of the query will pass nil for the change information
if (! changes) {
[tableView reloadData];
return;
}
// Query results have changed, so apply them to the UITableView
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:[changes deletionsInSection:0] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView insertRowsAtIndexPaths:[changes insertionsInSection:0] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView reloadRowsAtIndexPaths:[changes modificationsInSection:0] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView endUpdates];
}];
}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.selectedCall = self.calls[indexPath.row];
[self showActionDialog];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
#pragma mark - UITableViewDataSource
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self cellForIndexPath:indexPath];
OCTCall *call = self.calls[indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:@"Call\n"
@"Chat identifier %@\n"
@"call status: %ld\n"
@"callDuration: %f\n"
@"friend sending audio: %d\n"
@"friend receiving audio: %d\n"
@"friend sending video: %d\n"
@"friend receiving Video: %d\n",
call.chat.uniqueIdentifier, (long)call.status, call.callDuration, call.friendSendingAudio, call.friendAcceptingAudio,
call.friendSendingVideo, call.friendAcceptingVideo];
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.calls.count;
}
#pragma mark - Private
- (void)showActionDialog
{
__weak OCTCallsViewController *weakSelf = self;
[self showActionSheet:^(UIActionSheet *sheet) {
[sheet bk_addButtonWithTitle:@"Send call controls" handler:^{
[weakSelf showSendControlDialog];
}];
[sheet bk_addButtonWithTitle:@"Mute/Unmute Mic" handler:^{
[weakSelf toggleMuteMic];
}];
[sheet bk_addButtonWithTitle:@"Use speaker phone" handler:^{
[weakSelf useSpeaker];
}];
[sheet bk_addButtonWithTitle:@"Use default speakers" handler:^{
[weakSelf useDefaultSpeaker];
}];
[sheet bk_addButtonWithTitle:@"Show video" handler:^{
[weakSelf showVideo];
}];
}];
}
- (void)showSendControlDialog
{
__weak OCTCallsViewController *weakSelf = self;
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Call control"
message:@"Pick call control to send to friend"
preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction *pauseAction = [UIAlertAction actionWithTitle:@"Pause"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {[weakSelf pause];
}];
UIAlertAction *resumeAction = [UIAlertAction actionWithTitle:@"Resume"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {[weakSelf resume];
}];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"End/Reject Call"
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction *action) {[weakSelf cancel];
}];
UIAlertAction *muteAction = [UIAlertAction actionWithTitle:@"Mute Audio"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {[weakSelf muteFriend];
}];
UIAlertAction *unmuteAction = [UIAlertAction actionWithTitle:@"Unmute Audio"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {[weakSelf unmuteFriend];
}];
[alertController addAction:pauseAction];
[alertController addAction:resumeAction];
[alertController addAction:cancelAction];
[alertController addAction:muteAction];
[alertController addAction:unmuteAction];
[self presentViewController:alertController animated:YES completion:nil];
}
#pragma mark - Call methods
- (void)cancel
{
NSError *error;
if (! [self.manager.calls sendCallControl:OCTToxAVCallControlCancel toCall:self.selectedCall error:&error]) {
NSLog(@"%@ Error %@", self, error.localizedDescription);
NSLog(@"%@ Reason: %@", self, error.localizedFailureReason);
}
}
- (void)useSpeaker
{
[self.manager.calls routeAudioToSpeaker:YES error:nil];
}
- (void)useDefaultSpeaker
{
[self.manager.calls routeAudioToSpeaker:NO error:nil];
}
- (void)toggleMuteMic
{
BOOL currentStatus = self.manager.calls.enableMicrophone;
self.manager.calls.enableMicrophone = ! currentStatus;
}
- (void)pause
{
[self.manager.calls sendCallControl:OCTToxAVCallControlPause toCall:self.selectedCall error:nil];
}
- (void)resume
{
[self.manager.calls sendCallControl:OCTToxAVCallControlResume toCall:self.selectedCall error:nil];
}
- (void)muteFriend
{
[self.manager.calls sendCallControl:OCTToxAVCallControlMuteAudio toCall:self.selectedCall error:nil];
}
- (void)unmuteFriend
{
[self.manager.calls sendCallControl:OCTToxAVCallControlUnmuteAudio toCall:self.selectedCall error:nil];
}
- (void)showVideo
{
[self.manager.calls enableVideoSending:YES forCall:self.selectedCall error:nil];
OCTVideoViewController *videoViewController = [[OCTVideoViewController alloc] initWithCallManager:self.manager.calls call:self.selectedCall];
videoViewController.modalInPopover = YES;
videoViewController.modalPresentationStyle = UIModalPresentationOverFullScreen;
[self presentViewController:videoViewController animated:YES completion:nil];
}
@end

View File

@ -0,0 +1,9 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#import "OCTTableViewController.h"
@interface OCTChatsViewController : OCTTableViewController
@end

View File

@ -0,0 +1,106 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#import "OCTChatsViewController.h"
#import "OCTConversationViewController.h"
#import "OCTChat.h"
#import "OCTSubmanagerObjects.h"
@interface OCTChatsViewController ()
@property (strong, nonatomic) RLMResults<OCTChat *> *chats;
@property (strong, nonatomic) RLMNotificationToken *chatsNotificationToken;
@end
@implementation OCTChatsViewController
#pragma mark - Lifecycle
- (instancetype)initWithManager:(id<OCTManager>)manager
{
self = [super initWithManager:manager];
if (! self) {
return nil;
}
_chats = [self.manager.objects objectsForType:OCTFetchRequestTypeChat predicate:nil];
self.title = @"Chats";
return self;
}
- (void)dealloc
{
[self.chatsNotificationToken invalidate];
}
- (void)viewDidLoad
{
[super viewDidLoad];
__weak typeof(self)weakSelf = self;
self.chatsNotificationToken = [self.chats addNotificationBlock:^(RLMResults *results, RLMCollectionChange *changes, NSError *error) {
if (error) {
NSLog(@"Failed to open Realm on background worker: %@", error);
return;
}
UITableView *tableView = weakSelf.tableView;
// Initial run of the query will pass nil for the change information
if (! changes) {
[tableView reloadData];
return;
}
// Query results have changed, so apply them to the UITableView
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:[changes deletionsInSection:0] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView insertRowsAtIndexPaths:[changes insertionsInSection:0] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView reloadRowsAtIndexPaths:[changes modificationsInSection:0] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView endUpdates];
}];
}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
OCTChat *chat = self.chats[indexPath.row];
OCTConversationViewController *cv = [[OCTConversationViewController alloc] initWithManager:self.manager chat:chat];
[self.navigationController pushViewController:cv animated:YES];
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.chats.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self cellForIndexPath:indexPath];
OCTChat *chat = self.chats[indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:@"Chat\n"
@"uniqueIdentifier %@\n"
@"friends %@\n"
@"enteredText %@\n"
@"lastReadDate %@\n"
@"hasUnreadMessages %d\n"
@"lastMessage: %@",
chat.uniqueIdentifier, chat.friends, chat.enteredText, chat.lastReadDate, [chat hasUnreadMessages], chat.lastMessage];
return cell;
}
@end

View File

@ -0,0 +1,13 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#import "OCTTableViewController.h"
@class OCTChat;
@interface OCTConversationViewController : OCTTableViewController
- (instancetype)initWithManager:(id<OCTManager>)manager chat:(OCTChat *)chat;
@end

View File

@ -0,0 +1,159 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#import <BlocksKit/UIActionSheet+BlocksKit.h>
#import <BlocksKit/UIAlertView+BlocksKit.h>
#import <BlocksKit/UIBarButtonItem+BlocksKit.h>
#import "OCTConversationViewController.h"
#import "OCTChat.h"
#import "OCTMessageAbstract.h"
#import "OCTSubmanagerObjects.h"
#import "OCTSubmanagerChats.h"
#import "OCTSubmanagerCalls.h"
@interface OCTConversationViewController ()
@property (strong, nonatomic) OCTChat *chat;
@property (strong, nonatomic) RLMResults<OCTMessageAbstract *> *messages;
@property (strong, nonatomic) RLMNotificationToken *messagesNotificationToken;
@end
@implementation OCTConversationViewController
- (instancetype)initWithManager:(id<OCTManager>)manager chat:(OCTChat *)chat
{
self = [super initWithManager:manager];
if (! self) {
return nil;
}
_chat = chat;
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"chatUniqueIdentifier == %@", chat.uniqueIdentifier];
_messages = [self.manager.objects objectsForType:OCTFetchRequestTypeMessageAbstract predicate:predicate];
self.title = [NSString stringWithFormat:@"%@", chat.uniqueIdentifier];
return self;
}
- (void)dealloc
{
[self.messagesNotificationToken invalidate];
}
- (void)viewDidLoad
{
[super viewDidLoad];
__weak typeof(self)weakSelf = self;
self.messagesNotificationToken = [self.messages addNotificationBlock:^(RLMResults *results, RLMCollectionChange *changes, NSError *error) {
if (error) {
NSLog(@"Failed to open Realm on background worker: %@", error);
return;
}
UITableView *tableView = weakSelf.tableView;
// Initial run of the query will pass nil for the change information
if (! changes) {
[tableView reloadData];
return;
}
// Query results have changed, so apply them to the UITableView
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:[changes deletionsInSection:0] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView insertRowsAtIndexPaths:[changes insertionsInSection:0] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView reloadRowsAtIndexPaths:[changes modificationsInSection:0] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView endUpdates];
}];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
bk_initWithBarButtonSystemItem:UIBarButtonSystemItemAdd handler:^(id handler) {
[weakSelf showSendDialog];
}];
}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.messages.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self cellForIndexPath:indexPath];
OCTMessageAbstract *message = self.messages[indexPath.row];
cell.textLabel.text = [message description];
return cell;
}
#pragma mark - Private
- (void)showSendDialog
{
__weak OCTConversationViewController *weakSelf = self;
[self showActionSheet:^(UIActionSheet *sheet) {
[sheet bk_addButtonWithTitle:@"Send message" handler:^{
[weakSelf sendMessage];
}];
[sheet bk_addButtonWithTitle:@"Call friend" handler:^{
[weakSelf callFriend];
}];
}];
}
- (void)sendMessage
{
UIAlertView *alert = [UIAlertView bk_alertViewWithTitle:@"Send message" message:nil];
alert.alertViewStyle = UIAlertViewStylePlainTextInput;
UITextField *messageField = [alert textFieldAtIndex:0];
messageField.placeholder = @"Message";
__weak OCTConversationViewController *weakSelf = self;
[alert bk_addButtonWithTitle:@"OK" handler:^{
[weakSelf.manager.chats sendMessageToChat:weakSelf.chat text:messageField.text type:OCTToxMessageTypeNormal successBlock:^(OCTMessageAbstract *_) {
[weakSelf.tableView reloadData];
} failureBlock:nil];
}];
[alert bk_setCancelButtonWithTitle:@"Cancel" handler:nil];
[alert show];
}
#pragma mark - Call methods
- (void)callFriend
{
NSError *error;
OCTCall *call = [self.manager.calls callToChat:self.chat enableAudio:YES enableVideo:NO error:&error];
if (! call) {
NSLog(@"Unable to create call, %@", error.localizedFailureReason);
}
}
@end

View File

@ -0,0 +1,9 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#import "OCTTableViewController.h"
@interface OCTFriendsViewController : OCTTableViewController
@end

View File

@ -0,0 +1,264 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#import <BlocksKit/UIActionSheet+BlocksKit.h>
#import <BlocksKit/UIAlertView+BlocksKit.h>
#import <BlocksKit/UIBarButtonItem+BlocksKit.h>
#import "OCTFriendsViewController.h"
#import "OCTFriend.h"
#import "OCTFriendRequest.h"
#import "OCTSubmanagerObjects.h"
#import "OCTSubmanagerChats.h"
#import "OCTSubmanagerFriends.h"
typedef NS_ENUM(NSUInteger, SectionType) {
SectionTypeFriends = 0,
SectionTypeFriendRequests,
SectionTypeCount,
};
@interface OCTFriendsViewController ()
@property (strong, nonatomic) RLMResults<OCTFriend *> *friends;
@property (strong, nonatomic) RLMNotificationToken *friendsNotificationToken;
@property (strong, nonatomic) RLMResults<OCTFriendRequest *> *friendRequests;
@property (strong, nonatomic) RLMNotificationToken *friendRequestsNotificationToken;
@end
@implementation OCTFriendsViewController
#pragma mark - Lifecycle
- (instancetype)initWithManager:(id<OCTManager>)manager
{
self = [super initWithManager:manager];
if (! self) {
return nil;
}
_friends = [self.manager.objects objectsForType:OCTFetchRequestTypeFriend predicate:nil];
_friendRequests = [self.manager.objects objectsForType:OCTFetchRequestTypeFriendRequest predicate:nil];
self.title = @"Friends";
return self;
}
- (void)dealloc
{
[self.friendsNotificationToken invalidate];
[self.friendRequestsNotificationToken invalidate];
}
- (void)viewDidLoad
{
[super viewDidLoad];
__weak typeof(self)weakSelf = self;
self.friendsNotificationToken = [self.friends addNotificationBlock:^(RLMResults *results, RLMCollectionChange *changes, NSError *error) {
[weakSelf realmWasUpdated:changes sectionType:SectionTypeFriends error:error];
}];
self.friendRequestsNotificationToken = [self.friendRequests addNotificationBlock:^(RLMResults *results, RLMCollectionChange *changes, NSError *error) {
[weakSelf realmWasUpdated:changes sectionType:SectionTypeFriendRequests error:error];
}];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
bk_initWithBarButtonSystemItem:UIBarButtonSystemItemAdd handler:^(id handler) {
[weakSelf sendFriendRequest];
}];
}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
SectionType type = indexPath.section;
switch (type) {
case SectionTypeFriends:
[self didSelectFriend:self.friends[indexPath.row]];
break;
case SectionTypeFriendRequests:
[self didSelectFriendRequest:self.friendRequests[indexPath.row]];
break;
case SectionTypeCount:
// nop
break;
}
}
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return SectionTypeCount;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
SectionType type = section;
switch (type) {
case SectionTypeFriends:
return self.friends.count;
case SectionTypeFriendRequests:
return self.friendRequests.count;
case SectionTypeCount:
return 0;
}
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
SectionType type = section;
switch (type) {
case SectionTypeFriends:
return @"Friends";
case SectionTypeFriendRequests:
return @"FriendRequests";
case SectionTypeCount:
return nil;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
SectionType type = indexPath.section;
switch (type) {
case SectionTypeFriends:
return [self friendCellAtIndexPath:indexPath];
case SectionTypeFriendRequests:
return [self friendRequestCellAtIndexPath:indexPath];
case SectionTypeCount:
return nil;
}
}
#pragma mark - Private
- (UITableViewCell *)friendCellAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self cellForIndexPath:indexPath];
OCTFriend *friend = self.friends[indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:@"Friend\n"
@"friendNumber %u\n"
@"publicKey %@\n"
@"name %@\n"
@"nickname %@\n"
@"statusMessage %@\n"
@"status %@\n"
@"isConnected %d\n"
@"connectionStatus %@\n"
@"lastSeenOnline %@\n"
@"isTyping %d",
friend.friendNumber,
friend.publicKey,
friend.name,
friend.nickname,
friend.statusMessage,
[self stringFromUserStatus:friend.status],
friend.isConnected,
[self stringFromConnectionStatus:friend.connectionStatus],
friend.lastSeenOnline,
friend.isTyping];
return cell;
}
- (UITableViewCell *)friendRequestCellAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self cellForIndexPath:indexPath];
OCTFriendRequest *request = self.friendRequests[indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:@"Friend request\n"
@"publicKey %@\n"
@"message %@\n",
request.publicKey, request.message];
return cell;
}
- (void)didSelectFriend:(OCTFriend *)friend
{
__weak OCTFriendsViewController *weakSelf = self;
[self showActionSheet:^(UIActionSheet *sheet) {
[sheet bk_addButtonWithTitle:@"Create chat" handler:^{
[weakSelf.manager.chats getOrCreateChatWithFriend:friend];
}];
[sheet bk_addButtonWithTitle:@"Remove" handler:^{
[weakSelf.manager.friends removeFriend:friend error:nil];
}];
}];
}
- (void)didSelectFriendRequest:(OCTFriendRequest *)request
{
__weak OCTFriendsViewController *weakSelf = self;
[self showActionSheet:^(UIActionSheet *sheet) {
[sheet bk_addButtonWithTitle:@"Add" handler:^{
[weakSelf.manager.friends approveFriendRequest:request error:nil];
}];
[sheet bk_addButtonWithTitle:@"Remove" handler:^{
[weakSelf.manager.friends removeFriendRequest:request];
}];
}];
}
- (void)sendFriendRequest
{
UIAlertView *alert = [UIAlertView bk_alertViewWithTitle:@"Send friend request" message:nil];
alert.alertViewStyle = UIAlertViewStyleLoginAndPasswordInput;
UITextField *addressField = [alert textFieldAtIndex:0];
addressField.placeholder = @"Address";
UITextField *messageField = [alert textFieldAtIndex:1];
messageField.placeholder = @"Message";
messageField.secureTextEntry = NO;
__weak OCTFriendsViewController *weakSelf = self;
[alert bk_addButtonWithTitle:@"OK" handler:^{
[weakSelf.manager.friends sendFriendRequestToAddress:addressField.text message:messageField.text error:nil];
[weakSelf.tableView reloadData];
}];
[alert bk_setCancelButtonWithTitle:@"Cancel" handler:nil];
[alert show];
}
- (void)realmWasUpdated:(RLMCollectionChange *)changes sectionType:(SectionType)sectionType error:(NSError *)error
{
if (error) {
NSLog(@"Failed to open Realm on background worker: %@", error);
return;
}
// Initial run of the query will pass nil for the change information
if (! changes) {
[self.tableView reloadData];
return;
}
[self.tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:[changes deletionsInSection:sectionType] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView insertRowsAtIndexPaths:[changes insertionsInSection:sectionType] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView reloadRowsAtIndexPaths:[changes modificationsInSection:sectionType] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];
}
@end

View File

@ -0,0 +1,9 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#import "OCTTableViewController.h"
@interface OCTStartDemoViewController : OCTTableViewController
@end

View File

@ -0,0 +1,158 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#import <Masonry/Masonry.h>
#import "OCTStartDemoViewController.h"
#import "OCTUserViewController.h"
#import "OCTFriendsViewController.h"
#import "OCTChatsViewController.h"
#import "OCTCallsViewController.h"
#import "OCTTabBarControllerViewController.h"
#import "AppDelegate.h"
#import "OCTManagerConfiguration.h"
#import "OCTManagerFactory.h"
#import "OCTSubmanagerBootstrap.h"
#define NAVIGATION_WITH_CONTROLLER(class) \
[[UINavigationController alloc] initWithRootViewController:[[class alloc] initWithManager:manager]]
static NSString *const kLoginIdentifier = @"kLoginIdentifier";
typedef NS_ENUM(NSUInteger, Row) {
RowBootstrap,
RowIPv6Enabled,
RowUDPEnabled,
__RowCount,
};
@interface OCTStartDemoViewController ()
@property (strong, nonatomic) OCTManagerConfiguration *configuration;
@end
@implementation OCTStartDemoViewController
#pragma mark - Lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = @"Start";
self.configuration = [OCTManagerConfiguration defaultConfiguration];
}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
Row row = indexPath.row;
switch (row) {
case RowBootstrap:
[self bootstrap];
break;
case RowIPv6Enabled:
case RowUDPEnabled:
[self showActionSheetForRow:row];
break;
case __RowCount:
// nop
break;
}
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return __RowCount;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self cellForIndexPath:indexPath];
Row row = indexPath.row;
cell.textLabel.textColor = [UIColor blackColor];
cell.textLabel.textAlignment = NSTextAlignmentLeft;
cell.textLabel.font = [UIFont boldSystemFontOfSize:16.0];
switch (row) {
case RowBootstrap:
cell.textLabel.text = @"Bootstrap";
cell.textLabel.textColor = [UIColor blueColor];
cell.textLabel.textAlignment = NSTextAlignmentCenter;
cell.textLabel.font = [UIFont boldSystemFontOfSize:20.0];
break;
case RowIPv6Enabled:
cell.textLabel.text = [NSString stringWithFormat:@"IPv6Enabled %u", self.configuration.options.ipv6Enabled];
break;
case RowUDPEnabled:
cell.textLabel.text = [NSString stringWithFormat:@"UDPEnabled %u", self.configuration.options.udpEnabled];
break;
case __RowCount:
// nop
break;
}
return cell;
}
#pragma mark - Private
- (void)bootstrap
{
[OCTManagerFactory managerWithConfiguration:self.configuration encryptPassword:@"123" successBlock:^(id < OCTManager > manager) {
OCTTabBarControllerViewController *tabBar = [OCTTabBarControllerViewController new];
tabBar.viewControllers = @[
NAVIGATION_WITH_CONTROLLER(OCTUserViewController),
NAVIGATION_WITH_CONTROLLER(OCTFriendsViewController),
NAVIGATION_WITH_CONTROLLER(OCTChatsViewController),
NAVIGATION_WITH_CONTROLLER(OCTCallsViewController),
];
AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
delegate.window.rootViewController = tabBar;
manager.calls.delegate = tabBar;
[manager.bootstrap addPredefinedNodes];
[manager.bootstrap bootstrap];
} failureBlock:nil];
}
- (void)showActionSheetForRow:(Row)row
{
__weak OCTStartDemoViewController *weakSelf = self;
[self showActionSheet:^(UIActionSheet *sheet) {
switch (row) {
case RowIPv6Enabled:
{
[weakSelf addToSheet:sheet multiEditButtonWithOptions:@[ @"NO", @"YES" ] block:^(NSUInteger index) {
weakSelf.configuration.options.ipv6Enabled = (BOOL)index;
}];
break;
}
case RowUDPEnabled:
{
[weakSelf addToSheet:sheet multiEditButtonWithOptions:@[ @"NO", @"YES" ] block:^(NSUInteger index) {
weakSelf.configuration.options.udpEnabled = (BOOL)index;
}];
break;
}
case RowBootstrap:
case __RowCount:
// nop
break;
}
}];
}
@end

View File

@ -0,0 +1,10 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#import <UIKit/UIKit.h>
#import "OCTSubmanagerCalls.h"
@interface OCTTabBarControllerViewController : UITabBarController <OCTSubmanagerCallDelegate>
@end

View File

@ -0,0 +1,53 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#import "OCTTabBarControllerViewController.h"
@interface OCTTabBarControllerViewController ()
@end
@implementation OCTTabBarControllerViewController
#pragma mark - OCTSubmanagerCalls Delegate
- (void)callSubmanager:(id<OCTSubmanagerCalls>)callSubmanager receiveCall:(OCTCall *)call audioEnabled:(BOOL)audioEnabled videoEnabled:(BOOL)videoEnabled
{
NSString *title = [NSString stringWithFormat:@"audio: %d video: %d", audioEnabled, videoEnabled];
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Receiving call" message:title preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *rejectAction = [UIAlertAction actionWithTitle:@"Reject Call"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action) {
NSError *error;
BOOL status = [callSubmanager sendCallControl:OCTToxAVCallControlCancel toCall:call error:&error];
if (! status) {
NSLog(@"End call error: %@, %@", error.localizedDescription
, error.localizedFailureReason);
}
}];
UIAlertAction *acceptAction = [UIAlertAction actionWithTitle:@"Accept"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
NSError *error;
BOOL status = [callSubmanager answerCall:call
enableAudio:YES
enableVideo:YES
error:&error];
if (! status) {
NSLog(@"Accept call error: %@, %@", error.localizedDescription
, error.localizedFailureReason);
}
}];
[alertController addAction:rejectAction];
[alertController addAction:acceptAction];
[self presentViewController:alertController animated:YES completion:nil];
}
- (void)callSubmanager:(id<OCTSubmanagerCalls>)callSubmanager audioBitRateChanged:(OCTToxAVAudioBitRate)bitRate stable:(BOOL)stable forCall:(OCTCall *)call
{}
@end

View File

@ -0,0 +1,32 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#import <UIKit/UIKit.h>
#import "OCTManager.h"
@interface OCTTableViewController : UIViewController
@property (strong, nonatomic, readonly) UITableView *tableView;
@property (strong, nonatomic, readonly) id<OCTManager> manager;
- (instancetype)initWithManager:(id<OCTManager>)manager;
- (UITableViewCell *)cellForIndexPath:(NSIndexPath *)indexPath;
- (void)showActionSheet:(void (^)(UIActionSheet *sheet))block;
- (void)addToSheet:(UIActionSheet *)sheet copyButtonWithValue:(id)value;
- (void)addToSheet:(UIActionSheet *)sheet textEditButtonWithValue:(id)value block:(void (^)(NSString *string))block;
- (void) addToSheet:(UIActionSheet *)sheet
multiEditButtonWithOptions:(NSArray *)options
block:(void (^)(NSUInteger index))block;
// Value can be NSString or NSNumber
- (NSString *)stringFromValue:(id)value;
- (NSString *)stringFromUserStatus:(OCTToxUserStatus)status;
- (NSString *)stringFromConnectionStatus:(OCTToxConnectionStatus)status;
// Shows error if it isn't nil.
- (void)maybeShowError:(NSError *)error;
@end

View File

@ -0,0 +1,203 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#import <BlocksKit/UIActionSheet+BlocksKit.h>
#import <BlocksKit/UIAlertView+BlocksKit.h>
#import <Masonry/Masonry.h>
#import "OCTTableViewController.h"
static NSString *const kUITableViewCellIdentifier = @"kUITableViewCellIdentifier";
@interface OCTTableViewController () <UITableViewDelegate, UITableViewDataSource>
@property (strong, nonatomic) UITableView *tableView;
@property (strong, nonatomic) id<OCTManager> manager;
@end
@implementation OCTTableViewController
#pragma mark - Lifecycle
- (instancetype)initWithManager:(id<OCTManager>)manager
{
self = [super init];
if (! self) {
return nil;
}
_manager = manager;
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 44.0;
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:kUITableViewCellIdentifier];
[self.view addSubview:self.tableView];
[self installConstraints];
}
#pragma mark - Public
- (UITableViewCell *)cellForIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:kUITableViewCellIdentifier
forIndexPath:indexPath];
cell.textLabel.numberOfLines = 0;
return cell;
}
- (void)showActionSheet:(void (^)(UIActionSheet *sheet))block
{
if (! block) {
return;
}
UIActionSheet *sheet = [UIActionSheet bk_actionSheetWithTitle:@"Select action"];
[sheet bk_setCancelButtonWithTitle:@"Cancel" handler:nil];
block(sheet);
[sheet showInView:self.view];
}
- (void)addToSheet:(UIActionSheet *)sheet copyButtonWithValue:(id)value
{
[sheet bk_addButtonWithTitle:@"Copy" handler:^{
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
pasteboard.string = [self stringFromValue:value];
}];
}
- (void)addToSheet:(UIActionSheet *)sheet textEditButtonWithValue:(id)value block:(void (^)(NSString *string))block
{
NSParameterAssert(block);
__weak OCTTableViewController *weakSelf = self;
[sheet bk_addButtonWithTitle:@"Edit" handler:^{
UIAlertView *alert = [UIAlertView bk_alertViewWithTitle:@"" message:nil];
alert.alertViewStyle = UIAlertViewStylePlainTextInput;
[[alert textFieldAtIndex:0] setText:[self stringFromValue:value]];
[alert bk_addButtonWithTitle:@"OK" handler:^{
block([alert textFieldAtIndex:0].text);
[weakSelf.tableView reloadData];
}];
[alert bk_setCancelButtonWithTitle:@"Cancel" handler:nil];
[alert show];
}];
}
- (void) addToSheet:(UIActionSheet *)sheet
multiEditButtonWithOptions:(NSArray *)options
block:(void (^)(NSUInteger index))block
{
NSParameterAssert(block);
__weak OCTTableViewController *weakSelf = self;
[sheet bk_addButtonWithTitle:@"Edit" handler:^{
UIActionSheet *editSheet = [UIActionSheet bk_actionSheetWithTitle:@"Select action"];
for (NSUInteger index = 0; index < options.count; index++) {
[editSheet bk_addButtonWithTitle:options[index] handler:^{
block(index);
[weakSelf.tableView reloadData];
}];
}
[editSheet bk_setCancelButtonWithTitle:@"Cancel" handler:nil];
[editSheet showInView:weakSelf.view];
}];
}
- (NSString *)stringFromValue:(id)value
{
if ([value isKindOfClass:[NSString class]]) {
return value;
}
if ([value isKindOfClass:[NSNumber class]]) {
return [NSString stringWithFormat:@"%@", value];
}
return nil;
}
- (NSString *)stringFromUserStatus:(OCTToxUserStatus)status
{
switch (status) {
case OCTToxUserStatusNone:
return @"None";
case OCTToxUserStatusAway:
return @"Away";
case OCTToxUserStatusBusy:
return @"Busy";
}
}
- (NSString *)stringFromConnectionStatus:(OCTToxConnectionStatus)status
{
switch (status) {
case OCTToxConnectionStatusNone:
return @"None";
case OCTToxConnectionStatusTCP:
return @"TCP";
case OCTToxConnectionStatusUDP:
return @"UDP";
}
}
- (void)maybeShowError:(NSError *)error
{
if (! error) {
return;
}
UIAlertView *alert = [UIAlertView bk_alertViewWithTitle:error.localizedDescription
message:error.localizedFailureReason];
[alert bk_setCancelButtonWithTitle:@"OK" handler:nil];
[alert show];
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSAssert(NO, @"Implement in subclass");
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSAssert(NO, @"Implement in subclass");
return nil;
}
#pragma mark - Private
- (void)installConstraints
{
[self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
}
@end

View File

@ -0,0 +1,9 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#import "OCTTableViewController.h"
@interface OCTUserViewController : OCTTableViewController
@end

View File

@ -0,0 +1,189 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#import <BlocksKit/UIActionSheet+BlocksKit.h>
#import "OCTUserViewController.h"
#import "OCTStartDemoViewController.h"
#import "AppDelegate.h"
#import "OCTSubmanagerUser.h"
typedef NS_ENUM(NSUInteger, Row) {
RowConnectionStatus,
RowAddress,
RowPublicKey,
RowNospam,
RowStatus,
RowName,
RowStatusMessage,
RowLogOut,
};
@interface OCTUserViewController () <OCTSubmanagerUserDelegate>
@property (strong, nonatomic) NSArray *userData;
@end
@implementation OCTUserViewController
#pragma mark - Lifecycle
- (instancetype)initWithManager:(id<OCTManager>)manager
{
self = [super initWithManager:manager];
if (! self) {
return nil;
}
manager.user.delegate = self;
_userData = @[
@(RowConnectionStatus),
@(RowAddress),
@(RowPublicKey),
@(RowNospam),
@(RowStatus),
@(RowName),
@(RowStatusMessage),
@(RowLogOut),
];
self.title = @"User";
return self;
}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
__weak OCTUserViewController *weakSelf = self;
[self showActionSheet:^(UIActionSheet *sheet) {
switch (indexPath.row) {
case RowConnectionStatus:
// nop
break;
case RowAddress:
[weakSelf addToSheet:sheet copyButtonWithValue:weakSelf.manager.user.userAddress];
break;
case RowPublicKey:
[weakSelf addToSheet:sheet copyButtonWithValue:weakSelf.manager.user.publicKey];
break;
case RowNospam:
{
[weakSelf addToSheet:sheet copyButtonWithValue:@(weakSelf.manager.user.nospam)];
[weakSelf addToSheet:sheet textEditButtonWithValue:@(weakSelf.manager.user.nospam) block:^(NSString *string) {
weakSelf.manager.user.nospam = (OCTToxNoSpam)[string integerValue];
}];
break;
}
case RowStatus:
{
[weakSelf addToSheet:sheet multiEditButtonWithOptions:@[
[weakSelf stringFromUserStatus:0],
[weakSelf stringFromUserStatus:1],
[weakSelf stringFromUserStatus:2],
] block:^(NSUInteger index) {
weakSelf.manager.user.userStatus = index;
}];
break;
}
case RowName:
{
[weakSelf addToSheet:sheet copyButtonWithValue:weakSelf.manager.user.userName];
[weakSelf addToSheet:sheet textEditButtonWithValue:weakSelf.manager.user.userName block:^(NSString *string) {
NSError *error;
[weakSelf.manager.user setUserName:string error:&error];
[self maybeShowError:error];
}];
break;
}
case RowStatusMessage:
{
[weakSelf addToSheet:sheet copyButtonWithValue:weakSelf.manager.user.userStatusMessage];
[weakSelf addToSheet:sheet textEditButtonWithValue:weakSelf.manager.user.userStatusMessage block:^(NSString *string) {
NSError *error;
[weakSelf.manager.user setUserStatusMessage:string error:&error];
[self maybeShowError:error];
}];
break;
}
case RowLogOut:
{
[sheet bk_addButtonWithTitle:@"Log out" handler:^{
[weakSelf logOut];
}];
break;
}
}
}];
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.userData.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self cellForIndexPath:indexPath];
Row row = indexPath.row;
switch (row) {
case RowConnectionStatus:
cell.textLabel.text = [NSString stringWithFormat:@"connectionStatus: %@",
[self stringFromConnectionStatus:self.manager.user.connectionStatus]];
break;
case RowAddress:
cell.textLabel.text = [NSString stringWithFormat:@"userAddress: %@", self.manager.user.userAddress];
break;
case RowPublicKey:
cell.textLabel.text = [NSString stringWithFormat:@"publicKey: %@", self.manager.user.publicKey];
break;
case RowNospam:
cell.textLabel.text = [NSString stringWithFormat:@"nospam: %u", self.manager.user.nospam];
break;
case RowStatus:
cell.textLabel.text = [NSString stringWithFormat:@"userStatus: %@",
[self stringFromUserStatus:self.manager.user.userStatus]];
break;
case RowName:
cell.textLabel.text = [NSString stringWithFormat:@"userName: %@", self.manager.user.userName];
break;
case RowStatusMessage:
cell.textLabel.text = [NSString stringWithFormat:@"userStatusMessage: %@", self.manager.user.userStatusMessage];
break;
case RowLogOut:
cell.textLabel.text = @"Log Out";
break;
}
return cell;
}
#pragma mark - OCTSubmanagerUserDelegate
- (void)submanagerUser:(id<OCTSubmanagerUser>)submanager connectionStatusUpdate:(OCTToxConnectionStatus)connectionStatus
{
[self.tableView reloadData];
}
#pragma mark - Private
- (void)logOut
{
AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
delegate.window.rootViewController = [OCTStartDemoViewController new];
}
@end

View File

@ -0,0 +1,14 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#import <UIKit/UIKit.h>
@protocol OCTSubmanagerCalls;
@class OCTCall;
@interface OCTVideoViewController : UIViewController
- (instancetype)initWithCallManager:(id<OCTSubmanagerCalls>)manager call:(OCTCall *)call;
@end

View File

@ -0,0 +1,248 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#import "OCTVideoViewController.h"
#import "OCTSubmanagerCalls.h"
#import "OCTTableViewController.h"
static const CGFloat kEdgeInsets = 25.0;
@interface OCTVideoViewController ()
@property (nonatomic, strong) id<OCTSubmanagerCalls> manager;
@property (nonatomic, strong) UIButton *menuActionButton;
@property (nonatomic, strong) UIView *previewView;
@property (nonatomic, weak) CALayer *previewLayer;
@property (nonatomic, strong) UIView *videoFeed;
@property (nonatomic, strong) OCTCall *call;
@end
@implementation OCTVideoViewController
- (instancetype)initWithCallManager:(id<OCTSubmanagerCalls>)manager call:(OCTCall *)call
{
self = [super init];
if (! self) {
return nil;
}
_manager = manager;
_call = call;
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self createDismissVCButton];
[self createPreviewView];
[self createVideoFeedView];
[self createPreviewViewConstraints];
}
- (void)viewDidLayoutSubviews
{
[self adjustPreviewLayer];
}
- (void)createVideoViewConstraints
{
NSLayoutConstraint *videoViewTop = [NSLayoutConstraint constraintWithItem:self.videoFeed
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:kEdgeInsets];
NSLayoutConstraint *videoViewRight = [NSLayoutConstraint constraintWithItem:self.videoFeed
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:-kEdgeInsets];
NSLayoutConstraint *videoViewLeft = [NSLayoutConstraint constraintWithItem:self.videoFeed
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:kEdgeInsets];
NSLayoutConstraint *videoViewBottom = [NSLayoutConstraint constraintWithItem:self.videoFeed
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.previewView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0];
[self.view addConstraints:@[videoViewBottom, videoViewLeft, videoViewRight, videoViewTop]];
}
- (void)createPreviewViewConstraints
{
NSLayoutConstraint *previewViewBottom = [NSLayoutConstraint constraintWithItem:self.previewView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:-kEdgeInsets];
NSLayoutConstraint *previewViewLeft = [NSLayoutConstraint constraintWithItem:self.previewView
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:kEdgeInsets];
NSLayoutConstraint *previewViewRight = [NSLayoutConstraint constraintWithItem:self.previewView
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:-kEdgeInsets];
NSLayoutConstraint *previewViewHeight = [NSLayoutConstraint constraintWithItem:self.previewView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:-kEdgeInsets];
[self.view addConstraints:@[previewViewBottom, previewViewHeight, previewViewLeft, previewViewRight]];
}
- (void)createPreviewView
{
self.previewView = [UIView new];
self.previewView.userInteractionEnabled = NO;
self.previewView.backgroundColor = [UIColor blackColor];
self.previewView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.previewView];
}
- (void)adjustPreviewLayer
{
if (! self.previewLayer) {
__weak OCTVideoViewController *weakSelf = self;
[self.previewView.layer addSublayer:self.previewLayer];
[self.manager getVideoCallPreview:^(CALayer *layer) {
OCTVideoViewController *strongSelf = weakSelf;
[strongSelf.previewView.layer addSublayer:layer];
strongSelf.previewLayer = layer;
strongSelf.previewLayer.frame = strongSelf.previewView.bounds;
}];
}
else {
self.previewLayer.frame = self.previewView.bounds;
}
}
- (void)createVideoFeedView
{
self.videoFeed = [self.manager videoFeed];
self.videoFeed.translatesAutoresizingMaskIntoConstraints = NO;
self.videoFeed.userInteractionEnabled = NO;
[self.view addSubview:self.videoFeed];
[self createVideoViewConstraints];
}
- (void)createDismissVCButton
{
self.menuActionButton = [[UIButton alloc] initWithFrame:self.view.bounds];
self.menuActionButton.backgroundColor = [UIColor clearColor];
[self.menuActionButton addTarget:self
action:@selector(showActionDialog)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.menuActionButton];
}
- (void)showActionDialog
{
__weak OCTVideoViewController *weakSelf = self;
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Video"
message:@"actions"
preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction *stopSendingVideoAction = [UIAlertAction actionWithTitle:@"Stop sending video"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[weakSelf stopSendingVideo];
}];
UIAlertAction *startSendingVideoAction = [UIAlertAction actionWithTitle:@"Start sending video"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[weakSelf startSendingVideo];
}];
UIAlertAction *switchToRearAction = [UIAlertAction actionWithTitle:@"Use rear camera"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[weakSelf switchToBack];
}];
UIAlertAction *switchToFrontAction = [UIAlertAction actionWithTitle:@"Use front camera"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[weakSelf switchToFront];
}];
UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:@"Dismiss view"
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction *action) {
[weakSelf dismissViewButtonPressed];
}];
[alertController addAction:stopSendingVideoAction];
[alertController addAction:startSendingVideoAction];
[alertController addAction:switchToRearAction];
[alertController addAction:switchToFrontAction];
[alertController addAction:dismissAction];
[self presentViewController:alertController animated:YES completion:nil];
}
#pragma mark - Touch actions
- (void)dismissViewButtonPressed
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)stopSendingVideo
{
[self.manager enableVideoSending:NO forCall:self.call error:nil];
}
- (void)startSendingVideo
{
[self.manager enableVideoSending:YES forCall:self.call error:nil];
}
- (void)switchToFront
{
[self.manager switchToCameraFront:YES error:nil];
}
- (void)switchToBack
{
[self.manager switchToCameraFront:NO error:nil];
}
@end

View File

@ -0,0 +1,13 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char *argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}