replace matrixConsole project with Vector Xcode project.

This commit is contained in:
giomfo
2015-08-10 16:58:31 +02:00
parent fcccca249b
commit 60fbaf3ffc
91 changed files with 739 additions and 1236 deletions
+33
View File
@@ -0,0 +1,33 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <UIKit/UIKit.h>
#import <MessageUI/MessageUI.h>
#import <MatrixKit/MXKResponderRageShaking.h>
@interface RageShakeManager : NSObject <MXKResponderRageShaking, MFMailComposeViewControllerDelegate>
+ (id)sharedManager;
/**
Prompt user to report a crash. The alert is presented by the provided view controller.
@param viewController the view controller which presents the alert
*/
- (void)promptCrashReportInViewController:(UIViewController*)viewController;
@end
+260
View File
@@ -0,0 +1,260 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#define RAGESHAKEMANAGER_MINIMUM_SHAKING_DURATION 2
#import "RageShakeManager.h"
#import "AppDelegate.h"
#import "GBDeviceInfo_iOS.h"
#import "NSBundle+MatrixKit.h"
static RageShakeManager* sharedInstance = nil;
@interface RageShakeManager() {
bool isShaking;
double startShakingTimeStamp;
MXKAlert *confirmationAlert;
MFMailComposeViewController* mailComposer;
}
@end
@implementation RageShakeManager
#pragma mark Singleton Method
+ (id)sharedManager {
@synchronized(self) {
if(sharedInstance == nil)
sharedInstance = [[self alloc] init];
}
return sharedInstance;
}
#pragma mark -
- (instancetype)init {
self = [super init];
if (self) {
isShaking = NO;
startShakingTimeStamp = 0;
mailComposer = nil;
confirmationAlert = nil;
}
return self;
}
- (void)promptCrashReportInViewController:(UIViewController*)viewController {
if ([MXLogger crashLog] && [MFMailComposeViewController canSendMail]) {
confirmationAlert = [[MXKAlert alloc] initWithTitle:NSLocalizedStringFromTable(@"bug_report_prompt", @"MatrixConsole", nil) message:nil style:MXKAlertStyleAlert];
__weak typeof(self) weakSelf = self;
[confirmationAlert addActionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] style:MXKAlertActionStyleDefault handler:^(MXKAlert *alert) {
typeof(self) self = weakSelf;
self->confirmationAlert = nil;
// Erase the crash log (there is only chance for the user to send it)
[MXLogger deleteCrashLog];
}];
[confirmationAlert addActionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] style:MXKAlertActionStyleDefault handler:^(MXKAlert *alert) {
typeof(self) self = weakSelf;
self->confirmationAlert = nil;
[self sendEmail:viewController withSnapshot:NO];
}];
[confirmationAlert showInViewController:viewController];
}
}
#pragma mark - MXKResponderRageShaking
- (void)startShaking:(UIResponder*)responder {
// Start only if the application is in foreground
if ([AppDelegate theDelegate].isAppForeground && !confirmationAlert) {
NSLog(@"[RageShakeManager] Start shaking with [%@]", [responder class]);
startShakingTimeStamp = [[NSDate date] timeIntervalSince1970];
isShaking = YES;
}
}
- (void)stopShaking:(UIResponder*)responder {
NSLog(@"[RageShakeManager] Stop shaking with [%@]", [responder class]);
if (isShaking && [AppDelegate theDelegate].isAppForeground && !confirmationAlert
&& (([[NSDate date] timeIntervalSince1970] - startShakingTimeStamp) > RAGESHAKEMANAGER_MINIMUM_SHAKING_DURATION)) {
if ([responder isKindOfClass:[UIViewController class]] && [MFMailComposeViewController canSendMail]) {
confirmationAlert = [[MXKAlert alloc] initWithTitle:NSLocalizedStringFromTable(@"rage_shake_prompt", @"MatrixConsole", nil) message:nil style:MXKAlertStyleAlert];
__weak typeof(self) weakSelf = self;
[confirmationAlert addActionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] style:MXKAlertActionStyleDefault handler:^(MXKAlert *alert) {
typeof(self) self = weakSelf;
self->confirmationAlert = nil;
}];
[confirmationAlert addActionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] style:MXKAlertActionStyleDefault handler:^(MXKAlert *alert) {
typeof(self) self = weakSelf;
self->confirmationAlert = nil;
[self sendEmail:(UIViewController*)responder withSnapshot:YES];
}];
[confirmationAlert showInViewController:(UIViewController*)responder];
}
}
isShaking = NO;
}
- (void)cancel:(UIResponder*)responder {
isShaking = NO;
}
/**
Prepare and send a report email. The mail composer is presented by the provided view controller.
@param controller the view controller which presents the alert.
@param snapshot if this boolean value is YES, a screenshot of `controller` is sent as email attachment
*/
- (void)sendEmail:(UIViewController*)controller withSnapshot:(BOOL)snapshot {
UIImage *image;
if (snapshot) {
AppDelegate* theDelegate = [AppDelegate theDelegate];
UIGraphicsBeginImageContextWithOptions(theDelegate.window.bounds.size, NO, [UIScreen mainScreen].scale);
// Iterate over every window from back to front
for (UIWindow *window in [[UIApplication sharedApplication] windows])
{
if (![window respondsToSelector:@selector(screen)] || [window screen] == [UIScreen mainScreen])
{
// -renderInContext: renders in the coordinate space of the layer,
// so we must first apply the layer's geometry to the graphics context
CGContextSaveGState(UIGraphicsGetCurrentContext());
// Center the context around the window's anchor point
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), [window center].x, [window center].y);
// Apply the window's transform about the anchor point
CGContextConcatCTM(UIGraphicsGetCurrentContext(), [window transform]);
// Offset by the portion of the bounds left of and above the anchor point
CGContextTranslateCTM(UIGraphicsGetCurrentContext(),
-[window bounds].size.width * [[window layer] anchorPoint].x,
-[window bounds].size.height * [[window layer] anchorPoint].y);
// Render the layer hierarchy to the current context
[[window layer] renderInContext:UIGraphicsGetCurrentContext()];
// Restore the context
CGContextRestoreGState(UIGraphicsGetCurrentContext());
}
}
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// the image is copied in the clipboard
[UIPasteboard generalPasteboard].image = image;
}
if (controller) {
mailComposer = [[MFMailComposeViewController alloc] init];
if ([MXLogger crashLog]) {
[mailComposer setSubject:@"Matrix crash report"];
}
else {
[mailComposer setSubject:@"Matrix bug report"];
}
[mailComposer setToRecipients:[NSArray arrayWithObject:@"rageshake@matrix.org"]];
NSString* appVersion = [AppDelegate theDelegate].appVersion;
NSString* build = [AppDelegate theDelegate].build;
NSMutableString* message = [[NSMutableString alloc] init];
[message appendFormat:@"Something went wrong on my Matrix client: \n\n\n"];
[message appendFormat:@"-----> my comments <-----\n\n\n"];
[message appendFormat:@"------------------------------\n"];
[message appendFormat:@"Account info\n"];
NSArray *mxAccounts = [MXKAccountManager sharedManager].accounts;
for (MXKAccount* account in mxAccounts) {
NSString *disabled = account.disabled ? @" (disabled)" : @"";
[message appendFormat:@"userId: %@%@\n", account.mxCredentials.userId, disabled];
[message appendFormat:@"displayname: %@\n", account.mxSession.myUser.displayname];
[message appendFormat:@"homeServerURL: %@\n", account.mxCredentials.homeServer];
}
[message appendFormat:@"------------------------------\n"];
[message appendFormat:@"Application info\n"];
[message appendFormat:@"Console version: %@\n", appVersion];
[message appendFormat:@"MatrixKit version: %@\n", MatrixKitVersion];
[message appendFormat:@"MatrixSDK version: %@\n", MatrixSDKVersion];
if (build.length) {
[message appendFormat:@"Build: %@\n", build];
}
[message appendFormat:@"------------------------------\n"];
[message appendFormat:@"Device info\n"];
[message appendFormat:@"model: %@\n", [GBDeviceInfo deviceDetails].modelString];
[message appendFormat:@"operatingSystem: %@ %@\n", [[UIDevice currentDevice] systemName], [[UIDevice currentDevice] systemVersion]];
[mailComposer setMessageBody:message isHTML:NO];
// Attach image only if required
if (image) {
[mailComposer addAttachmentData:UIImageJPEGRepresentation(image, 1.0) mimeType:@"image/jpg" fileName:@"screenshot.jpg"];
}
// Add logs files
NSMutableArray *logFiles = [NSMutableArray arrayWithArray:[MXLogger logFiles]];
if ([MXLogger crashLog]) {
[logFiles addObject:[MXLogger crashLog]];
}
for (NSString *logFile in logFiles) {
NSData *logContent = [NSData dataWithContentsOfFile:logFile];
[mailComposer addAttachmentData:logContent mimeType:@"text/plain" fileName:[logFile lastPathComponent]];
}
mailComposer.mailComposeDelegate = self;
[controller presentViewController:mailComposer animated:YES completion:nil];
}
}
#pragma mark - MFMailComposeViewControllerDelegate delegate
- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error {
// Do not send this crash anymore
[MXLogger deleteCrashLog];
[controller dismissViewControllerAnimated:NO completion:nil];
}
@end
+57
View File
@@ -0,0 +1,57 @@
/*
Copyright 2014 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <UIKit/UIKit.h>
#import <MatrixKit/MatrixKit.h>
#import "MasterTabBarController.h"
@interface AppDelegate : UIResponder <UIApplicationDelegate, UITabBarControllerDelegate, MXKCallViewControllerDelegate, MXKContactDetailsViewControllerDelegate, MXKRoomMemberDetailsViewControllerDelegate> {
BOOL isAPNSRegistered;
}
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) MasterTabBarController *masterTabBarController;
@property (strong, nonatomic) MXKAlert *errorNotification;
@property (strong, nonatomic) NSString *appVersion;
@property (strong, nonatomic) NSString *build;
@property (nonatomic) BOOL isAppForeground;
@property (nonatomic) BOOL isOffline;
+ (AppDelegate*)theDelegate;
- (void)selectMatrixAccount:(void (^)(MXKAccount *selectedAccount))onSelection;
- (void)registerUserNotificationSettings;
- (void)reloadMatrixSessions:(BOOL)clearCache;
- (void)logout;
- (MXKAlert*)showErrorAsAlert:(NSError*)error;
/**
Reopen an existing private OneToOne room with this userId or creates a new one (if it doesn't exist)
@param userId
*/
- (void)startPrivateOneToOneRoomWithUserId:(NSString*)userId;
@end
+1095
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
@@ -0,0 +1,99 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/** Single, end-to-end encrypted messages (ie. we don't know what they say) */
/* New message from a specific person, not referencing a room */
"MSG_FROM_USER" = "Message from %@";
/* New message from a specific person in a named room */
"MSG_FROM_USER_IN_ROOM" = "%@ posted in %@";
/** Single, unencrypted messages (where we can include the content */
/* New message from a specific person, not referencing a room. Content included. */
"MSG_FROM_USER_WITH_CONTENT" = "%@: %@";
/* New message from a specific person in a named room. Content included. */
"MSG_FROM_USER_IN_ROOM_WITH_CONTENT" = "%@ in %@: %@";
/* New action message from a specific person, not referencing a room. */
"ACTION_FROM_USER" = "* %@ %@";
/* New action message from a specific person in a named room. */
"ACTION_FROM_USER_IN_ROOM" = "%@: * %@ %@";
/** Image Messages **/
/* New action message from a specific person, not referencing a room. */
"IMAGE_FROM_USER" = "%@ sent you a picture";
/* New action message from a specific person in a named room. */
"IMAGE_FROM_USER_IN_ROOM" = "%@ posted a picture in %@";
/** Coalesced messages **/
/* Multiple unread messages in a room */
"UNREAD_IN_ROOM" = "%@ new messages in %@";
/* Multiple unread messages from a specific person, not referencing a room */
"MSGS_FROM_USER" = "%@ new messages in %@";
/* Multiple unread messages from two people */
"MSGS_FROM_TWO_USERS" = "%@ new messages from %@ and %@";
/* Multiple unread messages from three people */
"MSGS_FROM_THREE_USERS" = "%@ new messages from %@, %@ and %@";
/* Multiple unread messages from two plus people (ie. for 4+ people: 'others' replaces the third person) */
"MSGS_FROM_TWO_PLUS_USERS" = "%@ new messages from %@, %@ and others";
/* Multiple messages in two rooms */
"MSGS_IN_TWO_ROOMS" = "%@ new messages in %@ and %@";
/* Look, stuff's happened, alright? Just open the app. */
"MSGS_IN_TWO_PLUS_ROOMS" = "%@ new messages in %@, %@ and others";
/** Invites **/
/* A user has invited you to a chat */
"USER_INVITE_TO_CHAT" = "%@ has invited you to chat";
/* A user has invited you to an (unamed) group chat */
"USER_INVITE_TO_CHAT_GROUP_CHAT" = "%@ has invited you to a group chat";
/* A user has invited you to a named room */
"USER_INVITE_TO_NAMED_ROOM" = "%@ has invited you to %@";
/** Calls **/
/* Incoming one-to-one voice call */
"VOICE_CALL_FROM_USER" = "Call from %@";
/* Incoming one-to-one video call */
"VIDEO_CALL_FROM_USER" = "Video call from %@";
/* Incoming unnamed voice conference invite from a specific person */
"VOICE_CONF_FROM_USER" = "Group call from %@";
/* Incoming unnamed video conference invite from a specific person */
"VIDEO_CONF_FROM_USER" = "Video group call from %@";
/* Incoming named voice conference invite from a specific person */
"VOICE_CONF_NAMED_FROM_USER" = "Group call from %@: '%@'";
/* Incoming named video conference invite from a specific person */
"VIDEO_CONF_NAMED_FROM_USER" = "Video group call from %@: '%@'";
+23
View File
@@ -0,0 +1,23 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Titles
"recents" = "Recents";
"accounts" = "Accounts";
// Others
"bug_report_prompt" = "The application has crashed last time. Would you like to submit a crash report?";
"rage_shake_prompt" = "You seem to be shaking the phone in frustration. Would you like to submit a bug report?";
+41
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 matrix.org. 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="Vector" 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>
+346
View File
@@ -0,0 +1,346 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6751" systemVersion="14D131" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="GsA-m1-kGB">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6736"/>
</dependencies>
<scenes>
<!--RecentsNav-->
<scene sceneID="pY4-Hu-kfo">
<objects>
<navigationController title="Recents" id="RMx-3f-FxP" userLabel="RecentsNav" sceneMemberID="viewController">
<navigationBar key="navigationBar" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" id="Pmd-2v-anx">
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="Foe-o5-6Ud" kind="relationship" relationship="rootViewController" id="TOG-zb-B44"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="8fS-aE-onr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="918" y="-678"/>
</scene>
<!--Room-->
<scene sceneID="UrN-g2-oG1">
<objects>
<viewController title="Room" id="msb-ol-2LB" customClass="RoomViewController" sceneMemberID="viewController">
<navigationItem key="navigationItem" id="3Zt-Wl-J6o">
<nil key="title"/>
<view key="titleView" contentMode="scaleToFill" id="aas-th-FW1" userLabel="Room title view container">
<rect key="frame" x="16" y="2" width="60" height="40"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<barButtonItem key="rightBarButtonItem" image="icon_users.png" id="3d6-ln-ICU" userLabel="showRoomMembers">
<inset key="imageInsets" minX="0.0" minY="0.0" maxX="5" maxY="0.0"/>
<connections>
<action selector="showRoomMembers:" destination="msb-ol-2LB" id="Ouh-KX-Nj7"/>
<segue destination="y7N-Cs-bXh" kind="show" identifier="showMemberList" id="CyV-7r-Ykf"/>
</connections>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="roomTitleViewContainer" destination="aas-th-FW1" id="SFz-1s-ywg"/>
<outlet property="showRoomMembersButtonItem" destination="3d6-ln-ICU" id="Qh7-yj-G7e"/>
<segue destination="qlN-Mb-ZH7" kind="show" identifier="showMemberDetails" id="9Sj-Yf-p2I"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="94y-cU-qQD" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1596" y="81"/>
</scene>
<!--Contact Details View Controller-->
<scene sceneID="aUh-hv-QyM">
<objects>
<tableViewController id="ro1-6w-v07" customClass="MXKContactDetailsViewController" sceneMemberID="viewController"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="cKT-B6-clR" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1517" y="801"/>
</scene>
<!--Room Members-->
<scene sceneID="5AK-8u-TUO">
<objects>
<tableViewController id="y7N-Cs-bXh" userLabel="Room Members" customClass="RoomMembersViewController" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="Oe9-1Y-zhS">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<connections>
<outlet property="dataSource" destination="y7N-Cs-bXh" id="9Zb-6m-GNy"/>
<outlet property="delegate" destination="y7N-Cs-bXh" id="RVg-Nq-nNy"/>
</connections>
</tableView>
<connections>
<segue destination="qlN-Mb-ZH7" kind="show" identifier="showDetails" id="m9P-N3-WZN"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Exe-4N-jYR" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2895" y="-290"/>
</scene>
<!--Room Member Details View Controller-->
<scene sceneID="q1J-Wz-aLa">
<objects>
<tableViewController id="qlN-Mb-ZH7" customClass="MXKRoomMemberDetailsViewController" sceneMemberID="viewController"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="mrY-z4-HGF" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2895" y="488"/>
</scene>
<!--Home-->
<scene sceneID="ZM8-lp-XE5">
<objects>
<viewController id="U6w-lp-S4A" customClass="HomeViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="vDd-9N-DDP"/>
<viewControllerLayoutGuide type="bottom" id="dPJ-aO-2kU"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="RoP-22-pJ3">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<searchBar contentMode="redraw" showsCancelButton="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Tvw-ed-GsF">
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="eLI-EI-r3h"/>
</constraints>
<textInputTraits key="textInputTraits" returnKeyType="done"/>
<connections>
<outlet property="delegate" destination="U6w-lp-S4A" id="hCd-1N-xoi"/>
</connections>
</searchBar>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" keyboardDismissMode="interactive" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" translatesAutoresizingMaskIntoConstraints="NO" id="JaR-EZ-kVC">
<rect key="frame" x="0.0" y="44" width="600" height="556"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<connections>
<outlet property="dataSource" destination="U6w-lp-S4A" id="4wm-Hk-riw"/>
<outlet property="delegate" destination="U6w-lp-S4A" id="1Td-A7-K3v"/>
</connections>
</tableView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="JaR-EZ-kVC" secondAttribute="bottom" id="4Gb-U6-it8"/>
<constraint firstItem="Tvw-ed-GsF" firstAttribute="leading" secondItem="RoP-22-pJ3" secondAttribute="leading" id="729-XV-ZMk"/>
<constraint firstAttribute="trailing" secondItem="Tvw-ed-GsF" secondAttribute="trailing" id="Bus-cz-B6K"/>
<constraint firstItem="JaR-EZ-kVC" firstAttribute="leading" secondItem="RoP-22-pJ3" secondAttribute="leading" id="I6V-D3-pAd"/>
<constraint firstItem="Tvw-ed-GsF" firstAttribute="top" secondItem="RoP-22-pJ3" secondAttribute="top" id="Mow-Cu-hMJ"/>
<constraint firstAttribute="trailing" secondItem="JaR-EZ-kVC" secondAttribute="trailing" id="oEj-zz-ioV"/>
<constraint firstItem="JaR-EZ-kVC" firstAttribute="top" secondItem="Tvw-ed-GsF" secondAttribute="bottom" id="pmM-LQ-f57"/>
</constraints>
</view>
<navigationItem key="navigationItem" title="Home" id="soS-1K-ngS"/>
<connections>
<outlet property="publicRoomsSearchBar" destination="Tvw-ed-GsF" id="xfg-cb-f7H"/>
<outlet property="publicRoomsSearchBarHeightConstraint" destination="eLI-EI-r3h" id="NLK-zk-hqf"/>
<outlet property="publicRoomsSearchBarTopConstraint" destination="Mow-Cu-hMJ" id="FKY-yE-XsQ"/>
<outlet property="tableView" destination="JaR-EZ-kVC" id="iOr-IF-WMm"/>
<outlet property="tableViewBottomConstraint" destination="4Gb-U6-it8" id="WZq-Ll-iM9"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="jbe-3b-94N" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="911" y="-1492"/>
</scene>
<!--Authentication View Controller-->
<scene sceneID="FoA-N2-3aF">
<objects>
<viewController id="ZlD-EU-ncw" customClass="AuthenticationViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="crg-iM-twR"/>
<viewControllerLayoutGuide type="bottom" id="gbK-Nm-HUT"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="wIi-Yi-2pi">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="mvZ-se-pqQ" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="55" y="-2252"/>
</scene>
<!--HomeNav-->
<scene sceneID="whS-JX-66Y">
<objects>
<navigationController title="HomeNav" id="11T-cf-rgo" sceneMemberID="viewController">
<tabBarItem key="tabBarItem" title="Home" image="tab_home.ico" id="aQW-fV-f8t"/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="HZK-D9-mlB">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="U6w-lp-S4A" kind="relationship" relationship="rootViewController" id="vCz-RO-a89"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Fpd-U8-xAp" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="49" y="-1492"/>
</scene>
<!--TabBar-->
<scene sceneID="ONA-qQ-ve3">
<objects>
<tabBarController title="TabBar" id="GsA-m1-kGB" customClass="MasterTabBarController" sceneMemberID="viewController">
<nil key="simulatedBottomBarMetrics"/>
<tabBar key="tabBar" contentMode="scaleToFill" id="n7O-7P-Gti">
<rect key="frame" x="0.0" y="0.0" width="320" height="49"/>
<autoresizingMask key="autoresizingMask"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</tabBar>
<connections>
<segue destination="ZlD-EU-ncw" kind="presentation" identifier="showAuth" id="Yfo-em-Rs3"/>
<segue destination="11T-cf-rgo" kind="relationship" relationship="viewControllers" id="eov-7h-qzl"/>
<segue destination="H1p-Uh-vWS" kind="relationship" relationship="viewControllers" id="Iju-mu-cRD"/>
<segue destination="fsw-DP-sy3" kind="relationship" relationship="viewControllers" id="8ex-NL-bWb"/>
<segue destination="g3a-7T-Pjt" kind="relationship" relationship="viewControllers" id="Y6R-1Q-Uhv"/>
</connections>
</tabBarController>
<placeholder placeholderIdentifier="IBFirstResponder" id="EVG-PE-ktX" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-1041" y="-273"/>
</scene>
<!--Contacts-->
<scene sceneID="ViO-cb-0j7">
<objects>
<tableViewController id="v0E-AX-Hfa" userLabel="Contacts" customClass="ContactsViewController" sceneMemberID="viewController">
<navigationItem key="navigationItem" title="Contacts" id="Ppm-q1-NgB"/>
<connections>
<segue destination="ro1-6w-v07" kind="show" identifier="showContactDetails" id="8v9-F6-vUy"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="YV3-Df-XS8" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="795" y="801"/>
</scene>
<!--Settings-->
<scene sceneID="4CK-43-kSo">
<objects>
<tableViewController id="1TJ-Md-cjN" customClass="SettingsViewController" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" keyboardDismissMode="interactive" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="10" sectionFooterHeight="10" id="etG-ZU-b2r">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.93725490196078431" green="0.93725490196078431" blue="0.95686274509803926" alpha="1" colorSpace="calibratedRGB"/>
<connections>
<outlet property="dataSource" destination="1TJ-Md-cjN" id="Lqt-w3-NjQ"/>
<outlet property="delegate" destination="1TJ-Md-cjN" id="pgx-SX-xgc"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="Settings" id="7NM-zW-wJT"/>
<connections>
<segue destination="KeQ-Sp-kmH" kind="show" identifier="showAccountDetails" id="flf-61-vgV"/>
<segue destination="ZlD-EU-ncw" kind="show" identifier="addAccount" id="17E-G9-IZ6"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="ZKJ-22-Asy" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="880" y="2020"/>
</scene>
<!--Account Details View Controller-->
<scene sceneID="V8G-Oz-3KA">
<objects>
<tableViewController id="KeQ-Sp-kmH" customClass="AccountDetailsViewController" sceneMemberID="viewController">
<connections>
<segue destination="h5x-Kb-9pa" kind="show" identifier="showGlobalNotificationSettings" id="kgj-1g-opx"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="KBx-ZK-1zU" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1661" y="2022"/>
</scene>
<!--Global notification settings-->
<scene sceneID="QZt-ZR-Vkj">
<objects>
<tableViewController title="Global notification settings" id="h5x-Kb-9pa" customClass="GlobalNotificationSettingsViewController" sceneMemberID="viewController"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="iOf-Cf-2Ew" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1661" y="2776"/>
</scene>
<!--SettingsNav-->
<scene sceneID="b0q-mf-7ii">
<objects>
<navigationController title="SettingsNav" id="g3a-7T-Pjt" sceneMemberID="viewController">
<tabBarItem key="tabBarItem" title="Settings" image="tab_settings.png" id="3O7-Cp-aRc"/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="bra-h0-es4">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="1TJ-Md-cjN" kind="relationship" relationship="rootViewController" id="VCU-F9-Tew"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="itc-5f-CfR" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="60" y="2022"/>
</scene>
<!--RecentsSplitVC-->
<scene sceneID="Nki-YV-4Qg">
<objects>
<splitViewController title="RecentsSplitVC" id="H1p-Uh-vWS" sceneMemberID="viewController">
<tabBarItem key="tabBarItem" systemItem="recents" id="mXn-Bx-qzv"/>
<toolbarItems/>
<connections>
<segue destination="RMx-3f-FxP" kind="relationship" relationship="masterViewController" id="BlO-5A-QYV"/>
<segue destination="vC3-pB-5Vb" kind="relationship" relationship="detailViewController" id="0ws-cL-0tk"/>
</connections>
</splitViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="cZU-Oi-B1e" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="60" y="-266"/>
</scene>
<!--Recents View Controller-->
<scene sceneID="EAJ-Vu-76j">
<objects>
<viewController id="Foe-o5-6Ud" customClass="RecentsViewController" sceneMemberID="viewController">
<navigationItem key="navigationItem" id="6sO-OP-7Sv"/>
<connections>
<segue destination="vC3-pB-5Vb" kind="showDetail" identifier="showDetails" id="Vo4-8x-dtH"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Po3-cj-dhS" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1596" y="-678"/>
</scene>
<!--RoomNav-->
<scene sceneID="r7l-gg-dq7">
<objects>
<navigationController title="RoomNav" id="vC3-pB-5Vb" sceneMemberID="viewController">
<navigationBar key="navigationBar" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" id="DjV-YW-jjY">
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="msb-ol-2LB" kind="relationship" relationship="rootViewController" id="5tu-jh-ZP6"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="SLD-UC-DBI" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="911" y="81"/>
</scene>
<!--ContactsNav-->
<scene sceneID="HhC-eK-eSS">
<objects>
<navigationController title="ContactsNav" id="fsw-DP-sy3" sceneMemberID="viewController">
<tabBarItem key="tabBarItem" systemItem="contacts" id="nIx-2f-mmH"/>
<simulatedTabBarMetrics key="simulatedBottomBarMetrics"/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="gsI-4i-BX6">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="v0E-AX-Hfa" kind="relationship" relationship="rootViewController" id="Jwg-1B-PqQ"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="C6c-tb-ElJ" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="55" y="802"/>
</scene>
</scenes>
<resources>
<image name="icon_users.png" width="35" height="25"/>
<image name="tab_home.ico" width="16" height="16"/>
<image name="tab_settings.png" width="25" height="25"/>
</resources>
<inferredMetricsTieBreakers>
<segue reference="Vo4-8x-dtH"/>
<segue reference="9Sj-Yf-p2I"/>
<segue reference="17E-G9-IZ6"/>
</inferredMetricsTieBreakers>
</document>
@@ -0,0 +1,68 @@
{
"images" : [
{
"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" : "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"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
+69
View File
@@ -0,0 +1,69 @@
<?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>UIStatusBarHidden</key>
<false/>
<key>UserDefaults</key>
<string>${PRODUCT_NAME}-Defaults</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>org.matrix.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundleDisplayName</key>
<string>Vector</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.0.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
<key>UIStatusBarTintParameters</key>
<dict>
<key>UINavigationBar</key>
<dict>
<key>Style</key>
<string>UIBarStyleDefault</string>
<key>Translucent</key>
<false/>
</dict>
</dict>
<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>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>
+24
View File
@@ -0,0 +1,24 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <MatrixKit/MatrixKit.h>
/**
The data source for Console `RecentsViewController`.
*/
@interface RecentListDataSource : MXKInterleavedRecentsDataSource
@end
+53
View File
@@ -0,0 +1,53 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "RecentListDataSource.h"
#import "AppDelegate.h"
@implementation RecentListDataSource
#pragma mark - UITableViewDataSource
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the specified item to be editable.
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Leave the selected room
id<MXKRecentCellDataStoring> recentCellData = [self cellDataAtIndexPath:indexPath];
// cancel pending uploads/downloads
// they are useless by now
[MXKMediaManager cancelDownloadsInCacheFolder:recentCellData.roomDataSource.room.state.roomId];
// TODO GFO cancel pending uploads related to this room
[recentCellData.roomDataSource.room leave:^{
// Refresh table display
if (self.delegate) {
[self.delegate dataSource:self didCellChange:nil];
}
} failure:^(NSError *error) {
NSLog(@"[Console RecentListDataSource] Failed to leave room (%@) failed: %@", recentCellData.roomDataSource.room.state.roomId, error);
//Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}
}
@end
File diff suppressed because it is too large Load Diff
+34
View File
@@ -0,0 +1,34 @@
<?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>identityserverurl</key>
<string>https://matrix.org</string>
<key>homeserverurl</key>
<string>https://matrix.org</string>
<key>homeserver</key>
<string>matrix.org</string>
<key>apnsDeviceToken</key>
<string></string>
<key>showAllEventsInRoomHistory</key>
<false/>
<key>showRedactionsInRoomHistory</key>
<false/>
<key>showUnsupportedEventsInRoomHistory</key>
<false/>
<key>sortRoomMembersUsingLastSeenTime</key>
<true/>
<key>showLeftMembersInRoomMemberList</key>
<false/>
<key>syncLocalContacts</key>
<false/>
<key>maxAllowedMediaCacheSize</key>
<integer>1073741824</integer>
<key>presenceColorForOnlineUser</key>
<integer>3401011</integer>
<key>presenceColorForUnavailableUser</key>
<integer>15066368</integer>
<key>presenceColorForOfflineUser</key>
<integer>15020851</integer>
</dict>
</plist>
@@ -0,0 +1,22 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <MatrixKit/MatrixKit.h>
@interface AccountDetailsViewController : MXKAccountDetailsViewController
@end
@@ -0,0 +1,146 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "AccountDetailsViewController.h"
#import "RageShakeManager.h"
@interface AccountDetailsViewController()
{
NSInteger globalNotificationSettingsRowIndex;
// The "Global Notification Settings" button
UIButton *globalNotifSettingsButton;
}
@end
@implementation AccountDetailsViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Setup `MXKRoomMemberListViewController` properties
self.rageShakeManager = [RageShakeManager sharedManager];
}
- (void)destroy
{
[super destroy];
globalNotifSettingsButton = nil;
}
#pragma mark - TableView data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSInteger count = [super tableView:tableView numberOfRowsInSection:section];
// Add one button in notification section to edit global notification settings
if (section == notificationsSection)
{
globalNotificationSettingsRowIndex = count++;
}
return count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell;
if (indexPath.section == notificationsSection && indexPath.row == globalNotificationSettingsRowIndex)
{
MXKTableViewCellWithButton *globalNotifSettingsBtnCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier]];
if (!globalNotifSettingsBtnCell)
{
globalNotifSettingsBtnCell = [[MXKTableViewCellWithButton alloc] init];
}
[globalNotifSettingsBtnCell.mxkButton setTitle:NSLocalizedStringFromTable(@"notification_settings_global_notification_settings", @"MatrixConsole", nil) forState:UIControlStateNormal];
[globalNotifSettingsBtnCell.mxkButton setTitle:NSLocalizedStringFromTable(@"notification_settings_global_notification_settings", @"MatrixConsole", nil) forState:UIControlStateHighlighted];
[globalNotifSettingsBtnCell.mxkButton addTarget:self action:@selector(onButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
globalNotifSettingsButton = globalNotifSettingsBtnCell.mxkButton;
cell = globalNotifSettingsBtnCell;
// MXKTableViewCellWithTextView *userInfoCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithTextView defaultReuseIdentifier]];
// if (!userInfoCell)
// {
// userInfoCell = [[MXKTableViewCellWithTextView alloc] init];
// }
//
// userInfoCell.mxkTextView.text = NSLocalizedStringFromTable(@"settings_webclient_push", @"MatrixConsole", nil);
// cell = userInfoCell;
}
else
{
cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
}
return cell;
}
#pragma mark - TableView delegate
//- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
//{
// if (indexPath.section == notificationsSection)
// {
// if (indexPath.row == globalNotificationSettingsRowIndex)
// {
// UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, MAXFLOAT)];
// textView.font = [UIFont systemFontOfSize:14];
// textView.text = NSLocalizedStringFromTable(@"settings_webclient_push", @"MatrixConsole", nil);
// CGSize contentSize = [textView sizeThatFits:textView.frame.size];
// return contentSize.height + 1;
// }
// }
//
// return [super tableView:tableView heightForRowAtIndexPath:indexPath];
//}
- (void)onButtonPressed:(id)sender
{
if (sender == globalNotifSettingsButton)
{
[self performSegueWithIdentifier:@"showGlobalNotificationSettings" sender:self];
}
else
{
[super onButtonPressed:sender];
}
}
#pragma mark - Segues
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Keep ref on destinationViewController
[super prepareForSegue:segue sender:sender];
if ([[segue identifier] isEqualToString:@"showGlobalNotificationSettings"])
{
MXKNotificationSettingsViewController *notifSettingsViewController = segue.destinationViewController;
notifSettingsViewController.mxAccount = self.mxAccount;
}
}
@end
@@ -0,0 +1,22 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <MatrixKit/MatrixKit.h>
@interface AuthenticationViewController : MXKAuthenticationViewController <MXKAuthenticationViewControllerDelegate>
@end
@@ -0,0 +1,59 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "AuthenticationViewController.h"
#import "RageShakeManager.h"
@implementation AuthenticationViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Setup `MXKAuthenticationViewController` properties
self.rageShakeManager = [RageShakeManager sharedManager];
self.defaultHomeServerUrl = [[NSUserDefaults standardUserDefaults] objectForKey:@"homeserverurl"];
self.defaultIdentityServerUrl = [[NSUserDefaults standardUserDefaults] objectForKey:@"identityserverurl"];
// The view controller dismiss itself on successful login.
self.delegate = self;
}
#pragma mark - MXKAuthenticationViewControllerDelegate
- (void)authenticationViewController:(MXKAuthenticationViewController *)authenticationViewController didLogWithUserId:(NSString *)userId {
// Report server url typed by the user as default url.
if (self.homeServerTextField.text.length) {
[[NSUserDefaults standardUserDefaults] setObject:self.homeServerTextField.text forKey:@"homeserverurl"];
}
if (self.identityServerTextField.text.length) {
[[NSUserDefaults standardUserDefaults] setObject:self.identityServerTextField.text forKey:@"identityserverurl"];
}
[[NSUserDefaults standardUserDefaults] synchronize];
// Remove auth view controller on successful login
if (self.navigationController) {
// Pop the view controller
[self.navigationController popViewControllerAnimated:YES];
} else {
// Dismiss on successful login
[self dismissViewControllerAnimated:YES completion:nil];
}
}
@end
@@ -0,0 +1,28 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <MatrixKit/MatrixKit.h>
// SMS
#import <MessageUI/MessageUI.h>
#import <MessageUI/MFMessageComposeViewController.h>
/**
'ContactsViewController' inherits MXKContactListViewController to handle contact list.
*/
@interface ContactsViewController : MXKContactListViewController <MXKContactListViewControllerDelegate, MFMessageComposeViewControllerDelegate>
@end
@@ -0,0 +1,285 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "ContactsViewController.h"
#import "AppDelegate.h"
#import "RageShakeManager.h"
#import "NSBundle+MatrixKit.h"
@interface ContactsViewController ()
{
/**
Tap on thumbnail --> display matrix information.
*/
MXKContact* selectedContact;
}
@property (strong, nonatomic) MXKAlert *startChatMenu;
@property (strong, nonatomic) MXKAlert *allowContactSyncAlert;
@end
@implementation ContactsViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self.tableView setSectionIndexColor:[AppDelegate theDelegate].masterTabBarController.tabBar.tintColor];
[self.tableView setSectionIndexBackgroundColor:[UIColor clearColor]];
// Set rageShake handler
self.rageShakeManager = [RageShakeManager sharedManager];
// The view controller handles itself the selected contact
self.delegate = self;
}
- (void)destroy
{
if (self.startChatMenu)
{
[self.startChatMenu dismiss:NO];
}
if (self.allowContactSyncAlert)
{
[self.allowContactSyncAlert dismiss:NO];
}
selectedContact = nil;
[super destroy];
}
#pragma mark - Actions
- (IBAction)onSegmentValueChange:(id)sender
{
[super onSegmentValueChange:sender];
if (sender == self.contactsControls)
{
// Did the user select local contacts?
if (self.contactsControls.selectedSegmentIndex)
{
MXKAppSettings* appSettings = [MXKAppSettings standardAppSettings];
if (!appSettings.syncLocalContacts)
{
__weak typeof(self) weakSelf = self;
self.allowContactSyncAlert = [[MXKAlert alloc] initWithTitle:NSLocalizedStringFromTable(@"contact_local_sync_prompt", @"MatrixConsole", nil) message:nil style:MXKAlertStyleAlert];
[self.allowContactSyncAlert addActionWithTitle:[NSBundle mxk_localizedStringForKey:@"no"] style:MXKAlertActionStyleDefault handler:^(MXKAlert *alert)
{
weakSelf.allowContactSyncAlert = nil;
}];
[self.allowContactSyncAlert addActionWithTitle:[NSBundle mxk_localizedStringForKey:@"yes"] style:MXKAlertActionStyleDefault handler:^(MXKAlert *alert)
{
weakSelf.allowContactSyncAlert = nil;
dispatch_async(dispatch_get_main_queue(), ^{
appSettings.syncLocalContacts = YES;
[weakSelf.tableView reloadData];
});
}];
[self.allowContactSyncAlert showInViewController:self];
}
}
}
}
#pragma mark - MXKContactListViewControllerDelegate
- (void)contactListViewController:(MXKContactListViewController *)contactListViewController didSelectContact:(NSString*)contactId
{
MXKContact *contact = [[MXKContactManager sharedManager] contactWithContactID:contactId];
__weak typeof(self) weakSelf = self;
NSArray* matrixIDs = contact.matrixIdentifiers;
// matrix user ?
if (matrixIDs.count)
{
// Display action sheet only if at least one session is available for this user
BOOL isSessionAvailable = NO;
NSArray *mxSessions = self.mxSessions;
for (NSString* userID in matrixIDs)
{
for (MXSession *mxSession in mxSessions)
{
if ([mxSession userWithUserId:userID])
{
isSessionAvailable = YES;
break;
}
}
}
if (isSessionAvailable)
{
// only 1 matrix ID
if (matrixIDs.count == 1)
{
NSString* matrixID = [matrixIDs objectAtIndex:0];
self.startChatMenu = [[MXKAlert alloc] initWithTitle:[NSString stringWithFormat:NSLocalizedStringFromTable(@"chat_with_user", @"MatrixConsole", nil), matrixID] message:nil style:MXKAlertStyleAlert];
[self.startChatMenu addActionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] style:MXKAlertActionStyleDefault handler:^(MXKAlert *alert)
{
weakSelf.startChatMenu = nil;
}];
[self.startChatMenu addActionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] style:MXKAlertActionStyleDefault handler:^(MXKAlert *alert)
{
weakSelf.startChatMenu = nil;
[[AppDelegate theDelegate] startPrivateOneToOneRoomWithUserId:matrixID];
}];
}
else
{
self.startChatMenu = [[MXKAlert alloc] initWithTitle:NSLocalizedStringFromTable(@"chat_with", @"MatrixConsole", nil) message:nil style:MXKAlertStyleActionSheet];
for(NSString* matrixID in matrixIDs)
{
[self.startChatMenu addActionWithTitle:matrixID style:MXKAlertActionStyleDefault handler:^(MXKAlert *alert)
{
weakSelf.startChatMenu = nil;
[[AppDelegate theDelegate] startPrivateOneToOneRoomWithUserId:matrixID];
}];
}
[self.startChatMenu addActionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] style:MXKAlertActionStyleDefault handler:^(MXKAlert *alert)
{
weakSelf.startChatMenu = nil;
}];
self.startChatMenu.sourceView = self.tableView;
}
[self.startChatMenu showInViewController:self];
}
}
else
{
// invite to use matrix
if (([MFMessageComposeViewController canSendText] ? contact.emailAddresses.count : 0) + (contact.phoneNumbers.count > 0))
{
self.startChatMenu = [[MXKAlert alloc] initWithTitle:[NSString stringWithFormat:NSLocalizedStringFromTable(@"invite_this_user_to_use_matrix", @"MatrixConsole", nil)] message:nil style:MXKAlertStyleActionSheet];
// check if the target can send SMSes
if ([MFMessageComposeViewController canSendText])
{
// list phonenumbers
for(MXKPhoneNumber* phonenumber in contact.phoneNumbers)
{
[self.startChatMenu addActionWithTitle:phonenumber.textNumber style:MXKAlertActionStyleDefault handler:^(MXKAlert *alert)
{
weakSelf.startChatMenu = nil;
// launch SMS composer
MFMessageComposeViewController *messageComposer = [[MFMessageComposeViewController alloc] init];
if (messageComposer)
{
messageComposer.messageComposeDelegate = weakSelf;
messageComposer.body = NSLocalizedStringFromTable(@"invitation_message", @"MatrixConsole", nil);
messageComposer.recipients = [NSArray arrayWithObject:phonenumber.textNumber];
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf presentViewController:messageComposer animated:YES completion:nil];
});
}
}];
}
}
// list emails
for(MXKEmail* email in contact.emailAddresses)
{
[self.startChatMenu addActionWithTitle:email.emailAddress style:MXKAlertActionStyleDefault handler:^(MXKAlert *alert)
{
weakSelf.startChatMenu = nil;
dispatch_async(dispatch_get_main_queue(), ^{
NSString* subject = [NSLocalizedStringFromTable(@"invitation_subject", @"MatrixConsole", nil) stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString* body = [NSLocalizedStringFromTable(@"invitation_message", @"MatrixConsole", nil) stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@&body=%@", email.emailAddress, subject, body]]];
});
}];
}
[self.startChatMenu addActionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] style:MXKAlertActionStyleDefault handler:^(MXKAlert *alert)
{
weakSelf.startChatMenu = nil;
}];
self.startChatMenu.sourceView = self.tableView;
[self.startChatMenu showInViewController:self];
}
}
}
- (void)contactListViewController:(MXKContactListViewController *)contactListViewController didTapContactThumbnail:(NSString*)contactId
{
MXKContact *contact = [[MXKContactManager sharedManager] contactWithContactID:contactId];
// open detailled sheet if there
if (contact.matrixIdentifiers.count > 0)
{
selectedContact = contact;
[self performSegueWithIdentifier:@"showContactDetails" sender:self];
}
}
#pragma mark - Segues
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Keep ref on destinationViewController
[super prepareForSegue:segue sender:sender];
if ([segue.identifier isEqualToString:@"showContactDetails"])
{
MXKContactDetailsViewController *contactDetailsViewController = segue.destinationViewController;
// Set rageShake handler
contactDetailsViewController.rageShakeManager = [RageShakeManager sharedManager];
// Set delegate to handle start chat option
contactDetailsViewController.delegate = [AppDelegate theDelegate];
contactDetailsViewController.contact = selectedContact;
selectedContact = nil;
}
}
#pragma mark MFMessageComposeViewControllerDelegate
- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result
{
[self dismissViewControllerAnimated:YES completion:nil];
}
@end
@@ -0,0 +1,22 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <MatrixKit/MatrixKit.h>
@interface GlobalNotificationSettingsViewController : MXKNotificationSettingsViewController
@end
@@ -0,0 +1,31 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "GlobalNotificationSettingsViewController.h"
#import "RageShakeManager.h"
@implementation GlobalNotificationSettingsViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Setup `MXKRoomMemberListViewController` properties
self.rageShakeManager = [RageShakeManager sharedManager];
}
@end
@@ -0,0 +1,45 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <MatrixKit/MatrixKit.h>
@interface HomeViewController:MXKViewController <UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate, UITextFieldDelegate, MXKRoomCreationViewDelegate>
@property (weak, nonatomic) IBOutlet UISearchBar *publicRoomsSearchBar;
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *publicRoomsSearchBarHeightConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *publicRoomsSearchBarTopConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *tableViewBottomConstraint;
/**
Add a matrix REST Client. It is used to make Matrix API requests and retrieve public rooms.
*/
- (void)addRestClient:(MXRestClient*)restClient;
/**
Remove a matrix REST Client.
*/
- (void)removeRestClient:(MXRestClient*)restClient;
/**
Enable the search in recents list according to the room display name (YES by default).
Set NO this property to disable this option and hide the related bar button.
*/
@property (nonatomic) BOOL enableSearch;
@end
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,51 @@
/*
Copyright 2014 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <UIKit/UIKit.h>
#import <MatrixKit/MatrixKit.h>
#define TABBAR_HOME_INDEX 0
#define TABBAR_RECENTS_INDEX 1
#define TABBAR_CONTACTS_INDEX 2
#define TABBAR_SETTINGS_INDEX 3
#define TABBAR_COUNT 4
@interface MasterTabBarController : UITabBarController
// Associated matrix sessions (empty by default).
@property (nonatomic, readonly) NSArray *mxSessions;
// Current selected room id. nil if no room is presently visible.
@property (strong, nonatomic) NSString *visibleRoomId;
// Add a matrix session. This session is propagated to all view controllers handled by the tab bar controller.
- (void)addMatrixSession:(MXSession*)mxSession;
// Remove a matrix session.
- (void)removeMatrixSession:(MXSession*)mxSession;
- (void)showAuthenticationScreen;
- (void)showRoomCreationForm;
- (void)showRoom:(NSString*)roomId withMatrixSession:(MXSession*)mxSession;
- (void)popRoomViewControllerAnimated:(BOOL)animated;
- (BOOL)isPresentingMediaPicker;
- (void)presentMediaPicker:(UIImagePickerController*)mediaPicker;
- (void)dismissMediaPicker;
@end
@@ -0,0 +1,315 @@
/*
Copyright 2014 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "MasterTabBarController.h"
#import "AppDelegate.h"
#import "HomeViewController.h"
#import "RecentsViewController.h"
#import "RecentListDataSource.h"
#import "ContactsViewController.h"
#import "SettingsViewController.h"
@interface MasterTabBarController ()
{
//Array of `MXSession` instances.
NSMutableArray *mxSessionArray;
// Tab bar view controllers
HomeViewController *homeViewController;
UINavigationController *recentsNavigationController;
RecentsViewController *recentsViewController;
ContactsViewController *contactsViewController;
SettingsViewController *settingsViewController;
// mediaPicker
UIImagePickerController *mediaPicker;
}
@end
@implementation MasterTabBarController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
mxSessionArray = [NSMutableArray array];
// To simplify navigation into the app, we retrieve here the navigation controller and the view controller related
// to the recents list in Recents Tab.
// Note: UISplitViewController is not supported on iPhone for iOS < 8.0
UIViewController* recents = [self.viewControllers objectAtIndex:TABBAR_RECENTS_INDEX];
recentsNavigationController = nil;
if ([recents isKindOfClass:[UISplitViewController class]])
{
UISplitViewController *splitViewController = (UISplitViewController *)recents;
recentsNavigationController = [splitViewController.viewControllers objectAtIndex:0];
}
else if ([recents isKindOfClass:[UINavigationController class]])
{
recentsNavigationController = (UINavigationController*)recents;
}
if (recentsNavigationController)
{
for (UIViewController *viewController in recentsNavigationController.viewControllers)
{
if ([viewController isKindOfClass:[RecentsViewController class]])
{
recentsViewController = (RecentsViewController*)viewController;
}
}
}
// Retrieve the home view controller
UIViewController* home = [self.viewControllers objectAtIndex:TABBAR_HOME_INDEX];
if ([home isKindOfClass:[UINavigationController class]])
{
UINavigationController *homeNavigationController = (UINavigationController*)home;
for (UIViewController *viewController in homeNavigationController.viewControllers)
{
if ([viewController isKindOfClass:[HomeViewController class]])
{
homeViewController = (HomeViewController*)viewController;
}
}
}
// Retrieve the constacts view controller
UIViewController* contacts = [self.viewControllers objectAtIndex:TABBAR_CONTACTS_INDEX];
if ([contacts isKindOfClass:[UINavigationController class]])
{
UINavigationController *contactsNavigationController = (UINavigationController*)contacts;
for (UIViewController *viewController in contactsNavigationController.viewControllers)
{
if ([viewController isKindOfClass:[ContactsViewController class]])
{
contactsViewController = (ContactsViewController*)viewController;
}
}
}
// Retrieve the settings view controller
UIViewController* settings = [self.viewControllers objectAtIndex:TABBAR_SETTINGS_INDEX];
if ([settings isKindOfClass:[UINavigationController class]])
{
UINavigationController *settingsNavigationController = (UINavigationController*)settings;
for (UIViewController *viewController in settingsNavigationController.viewControllers)
{
if ([viewController isKindOfClass:[SettingsViewController class]])
{
settingsViewController = (SettingsViewController*)viewController;
}
}
}
// Sanity check
NSAssert(homeViewController &&recentsViewController && contactsViewController && settingsViewController, @"Something wrong in Main.storyboard");
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// Check whether we're not logged in
if (![MXKAccountManager sharedManager].accounts.count)
{
[self showAuthenticationScreen];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
[[AppDelegate theDelegate] reloadMatrixSessions:NO];
}
- (void)dealloc
{
mxSessionArray = nil;
homeViewController = nil;
recentsNavigationController = nil;
recentsViewController = nil;
contactsViewController = nil;
settingsViewController = nil;
[self dismissMediaPicker];
}
#pragma mark -
- (void)restoreInitialDisplay
{
// Dismiss potential media picker
if (mediaPicker)
{
if (mediaPicker.delegate && [mediaPicker.delegate respondsToSelector:@selector(imagePickerControllerDidCancel:)])
{
[mediaPicker.delegate imagePickerControllerDidCancel:mediaPicker];
}
else
{
[self dismissMediaPicker];
}
}
[self popRoomViewControllerAnimated:NO];
}
#pragma mark -
- (NSArray*)mxSessions
{
return [NSArray arrayWithArray:mxSessionArray];
}
- (void)addMatrixSession:(MXSession *)mxSession
{
if (mxSession)
{
// Update recents data source (The recents view controller will be updated by its data source)
if (!mxSessionArray.count)
{
// This is the first added session, list all the recents for the logged user
RecentListDataSource *recentlistDataSource = [[RecentListDataSource alloc] initWithMatrixSession:mxSession];
[recentsViewController displayList:recentlistDataSource];
}
else
{
[recentsViewController.dataSource addMatrixSession:mxSession];
}
// Update home tab
[homeViewController addMatrixSession:mxSession];
// Update contacts tab
[contactsViewController addMatrixSession:mxSession];
// Update settings tab
[settingsViewController addMatrixSession:mxSession];
[mxSessionArray addObject:mxSession];
}
}
- (void)removeMatrixSession:(MXSession*)mxSession
{
// Update recents data source
[recentsViewController.dataSource removeMatrixSession:mxSession];
// Update home tab
[homeViewController removeMatrixSession:mxSession];
// Update contacts tab
[contactsViewController removeMatrixSession:mxSession];
// Update settings tab
[settingsViewController removeMatrixSession:mxSession];
[mxSessionArray removeObject:mxSession];
// Check whether there are others sessions
if (!mxSessionArray.count)
{
// Keep reference on existing dataSource to release it properly
MXKRecentsDataSource *previousRecentlistDataSource = recentsViewController.dataSource;
[recentsViewController displayList:nil];
[previousRecentlistDataSource destroy];
}
}
- (void)showAuthenticationScreen
{
[self restoreInitialDisplay];
[self performSegueWithIdentifier:@"showAuth" sender:self];
}
- (void)showRoomCreationForm
{
// Switch in Home Tab
[self setSelectedIndex:TABBAR_HOME_INDEX];
}
- (void)showRoom:(NSString*)roomId withMatrixSession:(MXSession*)mxSession
{
[self restoreInitialDisplay];
// Switch on Recents Tab
[self setSelectedIndex:TABBAR_RECENTS_INDEX];
// Select room to display its details (dispatch this action in order to let TabBarController end its refresh)
dispatch_async(dispatch_get_main_queue(), ^{
[recentsViewController selectRoomWithId:roomId inMatrixSession:mxSession];
});
}
- (void)popRoomViewControllerAnimated:(BOOL)animated
{
// Force back to recents list if room details is displayed in Recents Tab
if (recentsViewController)
{
[recentsNavigationController popToViewController:recentsViewController animated:animated];
// Release the current selected room
[recentsViewController closeSelectedRoom];
}
}
- (BOOL)isPresentingMediaPicker
{
return nil != mediaPicker;
}
- (void)presentMediaPicker:(UIImagePickerController*)aMediaPicker
{
[self dismissMediaPicker];
[self presentViewController:aMediaPicker animated:YES completion:^{
mediaPicker = aMediaPicker;
}];
}
- (void)dismissMediaPicker
{
if (mediaPicker)
{
[self dismissViewControllerAnimated:NO completion:nil];
mediaPicker.delegate = nil;
mediaPicker = nil;
}
}
- (void)setVisibleRoomId:(NSString *)roomId
{
if (roomId)
{
// Enable inApp notification for this room in all existing accounts.
NSArray *mxAccounts = [MXKAccountManager sharedManager].accounts;
for (MXKAccount *account in mxAccounts)
{
[account updateNotificationListenerForRoomId:roomId ignore:NO];
}
}
_visibleRoomId = roomId;
}
@end
@@ -0,0 +1,35 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <MatrixKit/MatrixKit.h>
@interface RecentsViewController : MXKRecentListViewController <MXKRecentListViewControllerDelegate, UIGestureRecognizerDelegate>
/**
Open the room with the provided identifier in a specific matrix session.
@param roomId the room identifier.
@param mxSession the matrix session in which the room should be available.
*/
- (void)selectRoomWithId:(NSString*)roomId inMatrixSession:(MXSession*)mxSession;
/**
Close the current selected room (if any)
*/
- (void)closeSelectedRoom;
@end
@@ -0,0 +1,429 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "RecentsViewController.h"
#import "RoomViewController.h"
#import "AppDelegate.h"
#import "RageShakeManager.h"
#import "NSBundle+MatrixKit.h"
@interface RecentsViewController ()
{
// Recents refresh handling
BOOL shouldScrollToTopOnRefresh;
// Selected room description
NSString *selectedRoomId;
MXSession *selectedRoomSession;
// Keep reference on the current room view controller to release it correctly
RoomViewController *currentRoomViewController;
// Keep the selected cell index to handle correctly split view controller display in landscape mode
NSIndexPath *currentSelectedCellIndexPath;
// "Mark all as read" option
UITapGestureRecognizer *navigationBarTapGesture;
MXKAlert *markAllAsReadAlert;
}
@end
@implementation RecentsViewController
- (void)awakeFromNib
{
[super awakeFromNib];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
self.preferredContentSize = CGSizeMake(320.0, 600.0);
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.leftBarButtonItem = self.editButtonItem;
// Add navigation items
NSArray *rightBarButtonItems = self.navigationItem.rightBarButtonItems;
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(createNewRoom:)];
self.navigationItem.rightBarButtonItems = rightBarButtonItems ? [rightBarButtonItems arrayByAddingObject:addButton] : @[addButton];
// Prepare tap gesture on title bar
navigationBarTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onNavigationBarTap:)];
[navigationBarTapGesture setNumberOfTouchesRequired:1];
[navigationBarTapGesture setNumberOfTapsRequired:1];
[navigationBarTapGesture setDelegate:self];
// Initialisation
currentSelectedCellIndexPath = nil;
// Setup `MXKRecentListViewController` properties
self.rageShakeManager = [RageShakeManager sharedManager];
// The view controller handles itself the selected recent
self.delegate = self;
}
- (void)dealloc
{
[self closeSelectedRoom];
}
- (void)destroy
{
if (markAllAsReadAlert)
{
[markAllAsReadAlert dismiss:NO];
markAllAsReadAlert = nil;
}
if (navigationBarTapGesture)
{
[self.navigationController.navigationBar removeGestureRecognizer:navigationBarTapGesture];
navigationBarTapGesture = nil;
}
[super destroy];
}
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
[super setEditing:editing animated:animated];
self.recentsTableView.editing = editing;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self updateNavigationBarTitle];
// Deselect the current selected row, it will be restored on viewDidAppear (if any)
NSIndexPath *indexPath = [self.recentsTableView indexPathForSelectedRow];
if (indexPath)
{
[self.recentsTableView deselectRowAtIndexPath:indexPath animated:NO];
}
[self.navigationController.navigationBar addGestureRecognizer:navigationBarTapGesture];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// Leave potential editing mode
[self setEditing:NO];
selectedRoomId = nil;
selectedRoomSession = nil;
if (markAllAsReadAlert)
{
[markAllAsReadAlert dismiss:NO];
markAllAsReadAlert = nil;
}
[self.navigationController.navigationBar removeGestureRecognizer:navigationBarTapGesture];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// Release the current selected room (if any) except if the Room ViewController is still visible (see splitViewController.isCollapsed condition)
if (!self.splitViewController || self.splitViewController.isCollapsed)
{
// Release the current selected room (if any).
[self closeSelectedRoom];
}
else
{
// In case of split view controller where the primary and secondary view controllers are displayed side-by-side onscreen,
// the selected room (if any) is highlighted.
[self refreshCurrentSelectedCell:YES];
}
}
#pragma mark -
- (void)selectRoomWithId:(NSString*)roomId inMatrixSession:(MXSession*)matrixSession
{
if (selectedRoomId && [selectedRoomId isEqualToString:roomId]
&& selectedRoomSession && selectedRoomSession == matrixSession)
{
// Nothing to do
return;
}
selectedRoomId = roomId;
selectedRoomSession = matrixSession;
if (roomId && matrixSession)
{
[self performSegueWithIdentifier:@"showDetails" sender:self];
}
else
{
[self closeSelectedRoom];
}
}
- (void)closeSelectedRoom
{
selectedRoomId = nil;
selectedRoomSession = nil;
if (currentRoomViewController)
{
if (currentRoomViewController.roomDataSource)
{
// Let the manager release this room data source
MXSession *mxSession = currentRoomViewController.roomDataSource.mxSession;
MXKRoomDataSourceManager *roomDataSourceManager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:mxSession];
[roomDataSourceManager closeRoomDataSource:currentRoomViewController.roomDataSource forceClose:NO];
}
[currentRoomViewController destroy];
currentRoomViewController = nil;
}
}
#pragma mark - Internal methods
- (void)updateNavigationBarTitle
{
NSString *title = NSLocalizedStringFromTable(@"recents", @"MatrixConsole", nil);
if (self.dataSource.unreadCount)
{
title = [NSString stringWithFormat:@"%@ (%tu)", title, self.dataSource.unreadCount];
}
self.navigationItem.title = title;
}
- (void)createNewRoom:(id)sender
{
[[AppDelegate theDelegate].masterTabBarController showRoomCreationForm];
}
- (void)scrollToTop
{
// stop any scrolling effect
[UIView setAnimationsEnabled:NO];
// before scrolling to the tableview top
self.recentsTableView.contentOffset = CGPointMake(-self.recentsTableView.contentInset.left, -self.recentsTableView.contentInset.top);
[UIView setAnimationsEnabled:YES];
}
- (void)refreshCurrentSelectedCell:(BOOL)forceVisible
{
// Update here the index of the current selected cell (if any) - Useful in landscape mode with split view controller.
currentSelectedCellIndexPath = nil;
if (currentRoomViewController)
{
// Restore the current selected room id, it is erased when view controller disappeared (see viewWillDisappear).
if (!selectedRoomId)
{
selectedRoomId = currentRoomViewController.roomDataSource.roomId;
selectedRoomSession = currentRoomViewController.mainSession;
}
// Look for the rank of this selected room in displayed recents
currentSelectedCellIndexPath = [self.dataSource cellIndexPathWithRoomId:selectedRoomId andMatrixSession:selectedRoomSession];
}
if (currentSelectedCellIndexPath)
{
// Select the right row
[self.recentsTableView selectRowAtIndexPath:currentSelectedCellIndexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
if (forceVisible)
{
// Scroll table view to make the selected row appear at second position
NSInteger topCellIndexPathRow = currentSelectedCellIndexPath.row ? currentSelectedCellIndexPath.row - 1: currentSelectedCellIndexPath.row;
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:topCellIndexPathRow inSection:currentSelectedCellIndexPath.section];
[self.recentsTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
}
}
else
{
NSIndexPath *indexPath = [self.recentsTableView indexPathForSelectedRow];
if (indexPath)
{
[self.recentsTableView deselectRowAtIndexPath:indexPath animated:NO];
}
}
}
#pragma mark -
- (void)onNavigationBarTap:(id)sender
{
if (self.dataSource.unreadCount)
{
__weak typeof(self) weakSelf = self;
markAllAsReadAlert = [[MXKAlert alloc] initWithTitle:NSLocalizedStringFromTable(@"mark_all_as_read_prompt", @"MatrixConsole", nil) message:nil style:MXKAlertStyleAlert];
markAllAsReadAlert.cancelButtonIndex = [markAllAsReadAlert addActionWithTitle:[NSBundle mxk_localizedStringForKey:@"no"] style:MXKAlertActionStyleDefault handler:^(MXKAlert *alert)
{
typeof(self) strongSelf = weakSelf;
strongSelf->markAllAsReadAlert = nil;
}];
[markAllAsReadAlert addActionWithTitle:[NSBundle mxk_localizedStringForKey:@"yes"] style:MXKAlertActionStyleDefault handler:^(MXKAlert *alert)
{
typeof(self) strongSelf = weakSelf;
strongSelf->markAllAsReadAlert = nil;
[strongSelf.dataSource markAllAsRead];
[strongSelf updateNavigationBarTitle];
}];
[markAllAsReadAlert showInViewController:self];
}
}
#pragma mark - Segues
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:@"showDetails"])
{
UIViewController *controller;
if ([[segue destinationViewController] isKindOfClass:[UINavigationController class]])
{
controller = [[segue destinationViewController] topViewController];
}
else
{
controller = [segue destinationViewController];
}
if ([controller isKindOfClass:[RoomViewController class]])
{
// Release existing Room view controller (if any)
if (currentRoomViewController)
{
if (currentRoomViewController.roomDataSource)
{
// Let the manager release this room data source
MXSession *mxSession = currentRoomViewController.roomDataSource.mxSession;
MXKRoomDataSourceManager *roomDataSourceManager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:mxSession];
[roomDataSourceManager closeRoomDataSource:currentRoomViewController.roomDataSource forceClose:NO];
}
[currentRoomViewController destroy];
currentRoomViewController = nil;
}
currentRoomViewController = (RoomViewController *)controller;
MXKRoomDataSourceManager *roomDataSourceManager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:selectedRoomSession];
MXKRoomDataSource *roomDataSource = [roomDataSourceManager roomDataSourceForRoom:selectedRoomId create:YES];
[currentRoomViewController displayRoom:roomDataSource];
}
// Reset unread count for this room
//[roomDataSource resetUnreadCount]; // @TODO: This automatically done by roomDataSource. Is it a good thing?
[self updateNavigationBarTitle];
if (self.splitViewController)
{
// Refresh selected cell without scrolling the selected cell (We suppose it's visible here)
[self refreshCurrentSelectedCell:NO];
// IOS >= 8
if ([self.splitViewController respondsToSelector:@selector(displayModeButtonItem)])
{
controller.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
}
//
controller.navigationItem.leftItemsSupplementBackButton = YES;
}
// Hide back button title
self.navigationItem.backBarButtonItem =[[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
}
}
#pragma mark - MXKDataSourceDelegate
- (void)dataSource:(MXKDataSource *)dataSource didCellChange:(id)changes
{
// Update the unreadCount in the title
[self updateNavigationBarTitle];
[self.recentsTableView reloadData];
if (shouldScrollToTopOnRefresh)
{
[self scrollToTop];
shouldScrollToTopOnRefresh = NO;
}
// In case of split view controller where the primary and secondary view controllers are displayed side-by-side onscreen,
// the selected room (if any) is updated and kept visible.
if (self.splitViewController && !self.splitViewController.isCollapsed)
{
[self refreshCurrentSelectedCell:YES];
}
}
#pragma mark - MXKRecentListViewControllerDelegate
- (void)recentListViewController:(MXKRecentListViewController *)recentListViewController didSelectRoom:(NSString *)roomId inMatrixSession:(MXSession *)matrixSession
{
// Open the room
[self selectRoomWithId:roomId inMatrixSession:matrixSession];
}
#pragma mark - Override UISearchBarDelegate
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
// Prepare table refresh on new search session
shouldScrollToTopOnRefresh = YES;
[super searchBar:searchBar textDidChange:searchText];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
{
// Prepare table refresh on end of search
shouldScrollToTopOnRefresh = YES;
[super searchBarCancelButtonClicked: searchBar];
}
@end
@@ -0,0 +1,22 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <MatrixKit/MatrixKit.h>
@interface RoomMembersViewController : MXKRoomMemberListViewController <MXKRoomMemberListViewControllerDelegate>
@end
@@ -0,0 +1,96 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "RoomMembersViewController.h"
#import "AppDelegate.h"
#import "RageShakeManager.h"
@interface RoomMembersViewController ()
{
/**
The selected member
*/
MXRoomMember *selectedMember;
}
@end
@implementation RoomMembersViewController
- (void)awakeFromNib
{
[super awakeFromNib];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
self.preferredContentSize = CGSizeMake(320.0, 600.0);
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Setup `MXKRoomMemberListViewController` properties
self.rageShakeManager = [RageShakeManager sharedManager];
// The view controller handles itself the selected roomMember
self.delegate = self;
}
- (void)dealloc
{
selectedMember = nil;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Segues
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Keep ref on destinationViewController
[super prepareForSegue:segue sender:sender];
if ([[segue identifier] isEqualToString:@"showDetails"])
{
if (selectedMember)
{
MXKRoomMemberDetailsViewController *memberViewController = segue.destinationViewController;
// Set rageShake handler
memberViewController.rageShakeManager = [RageShakeManager sharedManager];
// Set delegate to handle start chat option
memberViewController.delegate = [AppDelegate theDelegate];
[memberViewController displayRoomMember:selectedMember withMatrixRoom:[self.mainSession roomWithRoomId:self.dataSource.roomId]];
}
}
}
#pragma mark - MXKRoomMemberListViewControllerDelegate
- (void)roomMemberListViewController:(MXKRoomMemberListViewController *)roomMemberListViewController didSelectMember:(MXRoomMember *)member
{
// Report the selected member and open details view
selectedMember = member;
[self performSegueWithIdentifier:@"showDetails" sender:self];
}
@end
@@ -0,0 +1,22 @@
/*
Copyright 2014 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <MatrixKit/MatrixKit.h>
@interface RoomViewController : MXKRoomViewController
@end
+346
View File
@@ -0,0 +1,346 @@
/*
Copyright 2014 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "RoomViewController.h"
#import "MXKRoomBubbleTableViewCell.h"
#import "AppDelegate.h"
#import "RageShakeManager.h"
@interface RoomViewController ()
{
// Members list
id membersListener;
// Voip call options
UIButton *voipVoiceCallButton;
UIButton *voipVideoCallButton;
UIBarButtonItem *voipVoiceCallBarButtonItem;
UIBarButtonItem *voipVideoCallBarButtonItem;
// the user taps on a member thumbnail
MXRoomMember *selectedRoomMember;
}
@property (strong, nonatomic) IBOutlet UIBarButtonItem *showRoomMembersButtonItem;
@property (strong, nonatomic) MXKAlert *actionMenu;
@end
@implementation RoomViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Set room title view
[self setRoomTitleViewClass:MXKRoomTitleViewWithTopic.class];
// Replace the default input toolbar view with the one based on `HPGrowingTextView`.
[self setRoomInputToolbarViewClass:MXKRoomInputToolbarViewWithHPGrowingText.class];
// Set rageShake handler
self.rageShakeManager = [RageShakeManager sharedManager];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// hide action
if (self.actionMenu)
{
[self.actionMenu dismiss:NO];
self.actionMenu = nil;
}
if (self.roomDataSource)
{
if (membersListener)
{
[self.roomDataSource.room removeListener:membersListener];
membersListener = nil;
}
}
}
- (void)viewDidAppear:(BOOL)animated
{
if (self.childViewControllers)
{
// Dispose data source defined for room member list view controller (if any)
for (id childViewController in self.childViewControllers)
{
if ([childViewController isKindOfClass:[MXKRoomMemberListViewController class]])
{
MXKRoomMemberListViewController *viewController = (MXKRoomMemberListViewController*)childViewController;
MXKDataSource *dataSource = [viewController dataSource];
[viewController destroy];
[dataSource destroy];
}
}
}
[super viewDidAppear:animated];
if (self.roomDataSource)
{
// Set visible room id
[AppDelegate theDelegate].masterTabBarController.visibleRoomId = self.roomDataSource.roomId;
}
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
// Reset visible room id
[AppDelegate theDelegate].masterTabBarController.visibleRoomId = nil;
}
#pragma mark - Override MXKRoomViewController
- (void)displayRoom:(MXKRoomDataSource *)dataSource
{
// Remove members listener (if any) before changing dataSource.
if (membersListener)
{
[self.roomDataSource.room removeListener:membersListener];
membersListener = nil;
}
[super displayRoom:dataSource];
}
- (void)updateViewControllerAppearanceOnRoomDataSourceState
{
[super updateViewControllerAppearanceOnRoomDataSourceState];
// Update UI by considering dataSource state
if (self.roomDataSource && self.roomDataSource.state == MXKDataSourceStateReady)
{
// Register a listener for events that concern room members
if (!membersListener)
{
membersListener = [self.roomDataSource.room listenToEventsOfTypes:@[kMXEventTypeStringRoomMember] onEvent:^(MXEvent *event, MXEventDirection direction, id customObject) {
// Consider only live event
if (direction == MXEventDirectionForwards)
{
// Update navigation bar items
[self updateNavigationBarButtonItems];
}
}];
}
}
else
{
// Remove members listener if any.
if (membersListener)
{
[self.roomDataSource.room removeListener:membersListener];
membersListener = nil;
}
}
// Update navigation bar items
[self updateNavigationBarButtonItems];
}
- (BOOL)isIRCStyleCommand:(NSString*)string
{
// Override the default behavior for `/join` command in order to open automatically the joined room
if ([string hasPrefix:kCmdJoinRoom])
{
// Join a room
NSString *roomAlias = [string substringFromIndex:kCmdJoinRoom.length + 1];
// Remove white space from both ends
roomAlias = [roomAlias stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
// Check
if (roomAlias.length)
{
[self.mainSession joinRoom:roomAlias success:^(MXRoom *room)
{
// Show the room
[[AppDelegate theDelegate].masterTabBarController showRoom:room.state.roomId withMatrixSession:self.mainSession];
} failure:^(NSError *error)
{
NSLog(@"[Console RoomVC] Join roomAlias (%@) failed: %@", roomAlias, error);
//Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}
else
{
// Display cmd usage in text input as placeholder
self.inputToolbarView.placeholder = @"Usage: /join <room_alias>";
}
return YES;
}
return [super isIRCStyleCommand:string];
}
- (void)destroy
{
if (membersListener)
{
[self.roomDataSource.room removeListener:membersListener];
membersListener = nil;
}
if (self.actionMenu)
{
[self.actionMenu dismiss:NO];
self.actionMenu = nil;
}
[super destroy];
}
#pragma mark -
- (void)updateNavigationBarButtonItems
{
// Update navigation bar buttons according to room members count
if (self.roomDataSource && self.roomDataSource.state == MXKDataSourceStateReady)
{
// Check conditions to display voip call buttons
if (self.roomDataSource.room.state.members.count == 2 && self.mainSession.callManager)
{
if (!voipVoiceCallBarButtonItem || !voipVideoCallBarButtonItem)
{
voipVoiceCallButton = [UIButton buttonWithType:UIButtonTypeCustom];
voipVoiceCallButton.frame = CGRectMake(0, 0, 36, 36);
UIImage *voiceImage = [UIImage imageNamed:@"voice"];
[voipVoiceCallButton setImage:voiceImage forState:UIControlStateNormal];
[voipVoiceCallButton setImage:voiceImage forState:UIControlStateHighlighted];
[voipVoiceCallButton addTarget:self action:@selector(onButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
voipVoiceCallBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:voipVoiceCallButton];
voipVideoCallButton = [UIButton buttonWithType:UIButtonTypeCustom];
voipVideoCallButton.frame = CGRectMake(0, 0, 36, 36);
UIImage *videoImage = [UIImage imageNamed:@"video"];
[voipVideoCallButton setImage:videoImage forState:UIControlStateNormal];
[voipVideoCallButton setImage:videoImage forState:UIControlStateHighlighted];
[voipVideoCallButton addTarget:self action:@selector(onButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
voipVideoCallBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:voipVideoCallButton];
}
_showRoomMembersButtonItem.enabled = YES;
self.navigationItem.rightBarButtonItems = @[_showRoomMembersButtonItem, voipVideoCallBarButtonItem, voipVoiceCallBarButtonItem];
}
else
{
_showRoomMembersButtonItem.enabled = ([self.roomDataSource.room.state members].count != 0);
self.navigationItem.rightBarButtonItems = @[_showRoomMembersButtonItem];
}
}
else
{
_showRoomMembersButtonItem.enabled = NO;
self.navigationItem.rightBarButtonItems = @[_showRoomMembersButtonItem];
}
}
#pragma mark - MXKDataSource delegate
- (void)dataSource:(MXKDataSource *)dataSource didRecognizeAction:(NSString *)actionIdentifier inCell:(id<MXKCellRendering>)cell userInfo:(NSDictionary *)userInfo
{
// Override default implementation in case of tap on avatar
if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellTapOnAvatarView])
{
selectedRoomMember = [self.roomDataSource.room.state memberWithUserId:userInfo[kMXKRoomBubbleCellUserIdKey]];
if (selectedRoomMember)
{
[self performSegueWithIdentifier:@"showMemberDetails" sender:self];
}
}
else
{
// Keep default implementation for other actions
[super dataSource:dataSource didRecognizeAction:actionIdentifier inCell:cell userInfo:userInfo];
}
}
#pragma mark - Segues
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Keep ref on destinationViewController
[super prepareForSegue:segue sender:sender];
id pushedViewController = [segue destinationViewController];
if ([[segue identifier] isEqualToString:@"showMemberList"])
{
if ([pushedViewController isKindOfClass:[MXKRoomMemberListViewController class]])
{
MXKRoomMemberListViewController* membersController = (MXKRoomMemberListViewController*)pushedViewController;
// Dismiss keyboard
[self dismissKeyboard];
MXKRoomMemberListDataSource *membersDataSource = [[MXKRoomMemberListDataSource alloc] initWithRoomId:self.roomDataSource.roomId andMatrixSession:self.mainSession];
[membersController displayList:membersDataSource];
}
}
else if ([[segue identifier] isEqualToString:@"showMemberDetails"])
{
if (selectedRoomMember)
{
MXKRoomMemberDetailsViewController *memberViewController = pushedViewController;
// Set rageShake handler
memberViewController.rageShakeManager = [RageShakeManager sharedManager];
// Set delegate to handle start chat option
memberViewController.delegate = [AppDelegate theDelegate];
[memberViewController displayRoomMember:selectedRoomMember withMatrixRoom:self.roomDataSource.room];
selectedRoomMember = nil;
}
}
}
#pragma mark - Action
- (IBAction)onButtonPressed:(id)sender
{
if (sender == voipVoiceCallButton || sender == voipVideoCallButton)
{
[self.mainSession.callManager placeCallInRoom:self.roomDataSource.roomId withVideo:(sender == voipVideoCallButton)];
}
}
@end
@@ -0,0 +1,28 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <MatrixKit/MatrixKit.h>
@interface SettingsViewController : MXKTableViewController <UIPickerViewDataSource, UIPickerViewDelegate>
/**
The application settings displayed in the view controller.
By default the shared application settings are considered.
*/
@property (nonatomic) MXKAppSettings *settings;
@end
@@ -0,0 +1,811 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "SettingsViewController.h"
#import "RageShakeManager.h"
#import "AppDelegate.h"
#define SETTINGS_SECTION_ACCOUNTS_INDEX 0
#define SETTINGS_SECTION_CONTACTS_INDEX 1
#define SETTINGS_SECTION_ROOMS_INDEX 2
#define SETTINGS_SECTION_CONFIGURATION_INDEX 3
#define SETTINGS_SECTION_COMMANDS_INDEX 4
#define SETTINGS_SECTION_COUNT 5
#define SETTINGS_SECTION_ROOMS_DISPLAY_ALL_EVENTS_INDEX 0
#define SETTINGS_SECTION_ROOMS_SHOW_REDACTIONS_INDEX 1
#define SETTINGS_SECTION_ROOMS_SHOW_UNSUPPORTED_EVENTS_INDEX 2
#define SETTINGS_SECTION_ROOMS_SORT_MEMBERS_INDEX 3
#define SETTINGS_SECTION_ROOMS_DISPLAY_LEFT_MEMBERS_INDEX 4
#define SETTINGS_SECTION_ROOMS_SET_CACHE_SIZE_INDEX 5
#define SETTINGS_SECTION_ROOMS_CLEAR_CACHE_INDEX 6
#define SETTINGS_SECTION_ROOMS_INDEX_COUNT 7
@interface SettingsViewController ()
{
MXKAccount *selectedAccount;
id removedAccountObserver;
id accountUserInfoObserver;
// Contacts
UISwitch *contactsSyncSwitch;
// Country codes management
NSArray* countryCodes;
NSString* countryCode;
NSString* selectedCountryCode;
BOOL isSelectingCountryCode;
// Dynamic rows in Contacts section
NSInteger syncLocalContactsRowIndex;
NSInteger countryCodeRowIndex;
// Rooms settings
UISwitch *allEventsSwitch;
UISwitch *redactionsSwitch;
UISwitch *unsupportedEventsSwitch;
UISwitch *sortMembersSwitch;
UISwitch *displayLeftMembersSwitch;
MXKTableViewCellWithLabelAndSlider* maxCacheSizeCell;
NSUInteger minimumCacheSize;
UIButton *clearCacheButton;
}
@end
@implementation SettingsViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Consider the standard settings by default
_settings = [MXKAppSettings standardAppSettings];
// Initialize the minimum cache size with the current value
minimumCacheSize = self.minCachesSize;
// country selection
NSString *path = [[NSBundle mainBundle] pathForResource:@"countryCodes" ofType:@"plist"];
countryCodes = [NSArray arrayWithContentsOfFile:path];
isSelectingCountryCode = NO;
// Setup `MXKRoomMemberListViewController` properties
self.rageShakeManager = [RageShakeManager sharedManager];
// Add observer to handle removed accounts
removedAccountObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKAccountManagerDidRemoveAccountNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXKAccount *account = notif.object;
if (account)
{
if (self.childViewControllers.count)
{
for (id viewController in self.childViewControllers)
{
// Check whether details of this account was displayed
if ([viewController isKindOfClass:[MXKAccountDetailsViewController class]])
{
MXKAccountDetailsViewController *accountDetailsViewController = viewController;
if ([accountDetailsViewController.mxAccount.mxCredentials.userId isEqualToString:account.mxCredentials.userId])
{
// pop the account details view controller
[self.navigationController popToRootViewControllerAnimated:YES];
break;
}
}
}
}
}
// Refresh table to remove this account
[self.tableView reloadData];
}];
// Add observer to handle accounts update
accountUserInfoObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKAccountUserInfoDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
// Refresh table to remove this account
[self.tableView reloadData];
}];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)destroy
{
[self reset];
[super destroy];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (!_settings)
{
// Consider the standard settings by default
_settings = [MXKAppSettings standardAppSettings];
}
selectedCountryCode = countryCode = [_settings phonebookCountryCode];
// Update the minimum cache size with the current value
// Dispatch this operation to not freeze the app
dispatch_async(dispatch_get_main_queue(), ^{
minimumCacheSize = self.minCachesSize;
});
// Refresh display
[self.tableView reloadData];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// if country has been updated
// update the contact phonenumbers
// and check if they match now to Matrix Users
if (![countryCode isEqualToString:selectedCountryCode])
{
[_settings setPhonebookCountryCode:selectedCountryCode];
countryCode = selectedCountryCode;
}
countryCode = [_settings phonebookCountryCode];
}
#pragma mark - Internal methods
- (void)reset
{
// Remove observers
if (removedAccountObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:removedAccountObserver];
removedAccountObserver = nil;
}
if (accountUserInfoObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:accountUserInfoObserver];
accountUserInfoObserver = nil;
}
[[NSNotificationCenter defaultCenter] removeObserver:self];
selectedAccount = nil;
contactsSyncSwitch = nil;
allEventsSwitch = nil;
redactionsSwitch = nil;
unsupportedEventsSwitch = nil;
sortMembersSwitch = nil;
displayLeftMembersSwitch = nil;
maxCacheSizeCell = nil;
clearCacheButton = nil;
}
- (IBAction)onAccountToggleChange:(id)sender
{
UISwitch *accountSwitchToggle = sender;
NSArray *accounts = [[MXKAccountManager sharedManager] accounts];
if (accountSwitchToggle.tag < accounts.count)
{
MXKAccount *account = [accounts objectAtIndex:accountSwitchToggle.tag];
account.disabled = !accountSwitchToggle.on;
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
#pragma mark - Actions
- (IBAction)addAccount:(id)sender
{
[self performSegueWithIdentifier:@"addAccount" sender:self];
}
- (IBAction)logout:(id)sender
{
[[AppDelegate theDelegate] logout];
}
- (IBAction)onButtonPressed:(id)sender
{
if (sender == allEventsSwitch)
{
_settings.showAllEventsInRoomHistory = allEventsSwitch.on;
}
else if (sender == redactionsSwitch)
{
_settings.showRedactionsInRoomHistory = redactionsSwitch.on;
}
else if (sender == unsupportedEventsSwitch)
{
_settings.showUnsupportedEventsInRoomHistory = unsupportedEventsSwitch.on;
}
else if (sender == sortMembersSwitch)
{
_settings.sortRoomMembersUsingLastSeenTime = sortMembersSwitch.on;
}
else if (sender == displayLeftMembersSwitch)
{
_settings.showLeftMembersInRoomMemberList = displayLeftMembersSwitch.on;
}
else if (sender == contactsSyncSwitch)
{
_settings.syncLocalContacts = contactsSyncSwitch.on;
isSelectingCountryCode = NO;
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
else if (sender == clearCacheButton)
{
[[AppDelegate theDelegate] reloadMatrixSessions:YES];
}
}
- (IBAction)onSliderValueChange:(id)sender
{
if (sender == maxCacheSizeCell.mxkSlider)
{
UISlider* slider = maxCacheSizeCell.mxkSlider;
// check if the upper bounds have been updated
if (slider.maximumValue != self.maxAllowedCachesSize)
{
slider.maximumValue = self.maxAllowedCachesSize;
}
// check if the value does not exceed the bounds
if (slider.value < minimumCacheSize)
{
slider.value = minimumCacheSize;
}
[self setCurrentMaxCachesSize:slider.value];
maxCacheSizeCell.mxkLabel.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"settings_max_cache_size", @"MatrixConsole", nil), [NSByteCountFormatter stringFromByteCount:self.currentMaxCachesSize countStyle:NSByteCountFormatterCountStyleFile]];
}
}
#pragma mark - Segues
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Keep ref on destinationViewController
[super prepareForSegue:segue sender:sender];
if ([[segue identifier] isEqualToString:@"showAccountDetails"])
{
MXKAccountDetailsViewController *accountViewController = segue.destinationViewController;
accountViewController.mxAccount = selectedAccount;
selectedAccount = nil;
}
}
#pragma mark - UITableView data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return SETTINGS_SECTION_COUNT;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSInteger count = 0;
if (section == SETTINGS_SECTION_ACCOUNTS_INDEX)
{
count = [[MXKAccountManager sharedManager] accounts].count + 1; // Add one cell in this section to display "logout all" option.
}
else if (section == SETTINGS_SECTION_CONTACTS_INDEX)
{
countryCodeRowIndex = syncLocalContactsRowIndex = -1;
// init row index
syncLocalContactsRowIndex = count++;
if ([_settings syncLocalContacts])
{
countryCodeRowIndex = count++;
}
}
else if (section == SETTINGS_SECTION_ROOMS_INDEX)
{
count = SETTINGS_SECTION_ROOMS_INDEX_COUNT;
}
else if (section == SETTINGS_SECTION_CONFIGURATION_INDEX)
{
count = 1;
}
else if (section == SETTINGS_SECTION_COMMANDS_INDEX)
{
count = 1;
}
return count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = nil;
if (indexPath.section == SETTINGS_SECTION_ACCOUNTS_INDEX)
{
NSArray *accounts = [[MXKAccountManager sharedManager] accounts];
if (indexPath.row < accounts.count)
{
MXKAccountTableViewCell *accountCell = [tableView dequeueReusableCellWithIdentifier:[MXKAccountTableViewCell defaultReuseIdentifier]];
if (!accountCell)
{
accountCell = [[MXKAccountTableViewCell alloc] init];
}
accountCell.mxAccount = [accounts objectAtIndex:indexPath.row];
// Display switch toggle in case of multiple accounts
if (accounts.count > 1 || accountCell.mxAccount.disabled)
{
accountCell.accountSwitchToggle.tag = indexPath.row;
accountCell.accountSwitchToggle.hidden = NO;
[accountCell.accountSwitchToggle addTarget:self action:@selector(onAccountToggleChange:) forControlEvents:UIControlEventValueChanged];
}
cell = accountCell;
}
else
{
MXKTableViewCellWithButton *logoutBtnCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier]];
if (!logoutBtnCell)
{
logoutBtnCell = [[MXKTableViewCellWithButton alloc] init];
}
[logoutBtnCell.mxkButton setTitle:NSLocalizedStringFromTable(@"account_logout_all", @"MatrixConsole", nil) forState:UIControlStateNormal];
[logoutBtnCell.mxkButton setTitle:NSLocalizedStringFromTable(@"account_logout_all", @"MatrixConsole", nil) forState:UIControlStateHighlighted];
[logoutBtnCell.mxkButton removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside];
[logoutBtnCell.mxkButton addTarget:self action:@selector(logout:) forControlEvents:UIControlEventTouchUpInside];
cell = logoutBtnCell;
}
}
else if (indexPath.section == SETTINGS_SECTION_CONTACTS_INDEX)
{
if (indexPath.row == syncLocalContactsRowIndex)
{
MXKTableViewCellWithLabelAndSwitch *contactsCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithLabelAndSwitch defaultReuseIdentifier]];
if (!contactsCell)
{
contactsCell = [[MXKTableViewCellWithLabelAndSwitch alloc] init];
}
[contactsCell.mxkSwitch addTarget:self action:@selector(onButtonPressed:) forControlEvents:UIControlEventValueChanged];
contactsCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_contact_sync", @"MatrixConsole", nil);
contactsCell.mxkSwitch.on = [_settings syncLocalContacts];
contactsSyncSwitch = contactsCell.mxkSwitch;
cell = contactsCell;
}
else if (indexPath.row == countryCodeRowIndex)
{
int index = 0;
NSString* countryName = @"";
for(NSDictionary* dict in countryCodes)
{
if ([[dict valueForKey:@"id"] isEqualToString:selectedCountryCode])
{
countryName = [dict valueForKey:@"country"];
break;
}
index++;
}
// there is no country code selection
if (!isSelectingCountryCode)
{
MXKTableViewCellWithLabelAndSubLabel *countryCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithLabelAndSubLabel defaultReuseIdentifier]];
if (!countryCell)
{
countryCell = [[MXKTableViewCellWithLabelAndSubLabel alloc] init];
}
countryCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_country_select", @"MatrixConsole", nil);
countryCell.mxkSublabel.text = countryName;
countryCell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell = countryCell;
}
else
{
// there is a selection in progress
MXKTableViewCellWithPicker *pickerCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithPicker defaultReuseIdentifier]];
if (!pickerCell)
{
pickerCell = [[MXKTableViewCellWithPicker alloc] init];
}
// display a picker
pickerCell.mxkPickerView.delegate = self;
pickerCell.mxkPickerView.dataSource = self;
if (countryName.length > 0)
{
dispatch_async(dispatch_get_main_queue(), ^{
[pickerCell.mxkPickerView selectRow:index inComponent:0 animated:NO];
});
}
cell = pickerCell;
}
}
}
else if (indexPath.section == SETTINGS_SECTION_ROOMS_INDEX)
{
if (indexPath.row == SETTINGS_SECTION_ROOMS_CLEAR_CACHE_INDEX)
{
MXKTableViewCellWithButton *clearCacheBtnCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier]];
if (!clearCacheBtnCell)
{
clearCacheBtnCell = [[MXKTableViewCellWithButton alloc] init];
}
NSString *btnTitle = [NSString stringWithFormat:@"%@ (%@)", NSLocalizedStringFromTable(@"settings_clear_cache", @"MatrixConsole", nil), [NSByteCountFormatter stringFromByteCount:self.cachesSize countStyle:NSByteCountFormatterCountStyleFile]];
[clearCacheBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateNormal];
[clearCacheBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateHighlighted];
clearCacheButton = clearCacheBtnCell.mxkButton;
[clearCacheButton removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside];
[clearCacheButton addTarget:self action:@selector(onButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
cell = clearCacheBtnCell;
}
else if (indexPath.row == SETTINGS_SECTION_ROOMS_SET_CACHE_SIZE_INDEX)
{
maxCacheSizeCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithLabelAndSlider defaultReuseIdentifier]];
if (!maxCacheSizeCell)
{
maxCacheSizeCell = [[MXKTableViewCellWithLabelAndSlider alloc] init];
}
maxCacheSizeCell.mxkSlider.minimumValue = 0;
maxCacheSizeCell.mxkSlider.maximumValue = self.maxAllowedCachesSize;
maxCacheSizeCell.mxkSlider.value = self.currentMaxCachesSize;
[self onSliderValueChange:maxCacheSizeCell.mxkSlider];
[maxCacheSizeCell.mxkSlider addTarget:self action:@selector(onSliderValueChange:) forControlEvents:UIControlEventValueChanged];
cell = maxCacheSizeCell;
}
else
{
MXKTableViewCellWithLabelAndSwitch *roomsSettingCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithLabelAndSwitch defaultReuseIdentifier]];
if (!roomsSettingCell)
{
roomsSettingCell = [[MXKTableViewCellWithLabelAndSwitch alloc] init];
}
[roomsSettingCell.mxkSwitch addTarget:self action:@selector(onButtonPressed:) forControlEvents:UIControlEventValueChanged];
if (indexPath.row == SETTINGS_SECTION_ROOMS_DISPLAY_ALL_EVENTS_INDEX)
{
roomsSettingCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_display_all_events", @"MatrixConsole", nil);
roomsSettingCell.mxkSwitch.on = [_settings showAllEventsInRoomHistory];
allEventsSwitch = roomsSettingCell.mxkSwitch;
}
else if (indexPath.row == SETTINGS_SECTION_ROOMS_SHOW_REDACTIONS_INDEX)
{
roomsSettingCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_show_redactions", @"MatrixConsole", nil);
roomsSettingCell.mxkSwitch.on = [_settings showRedactionsInRoomHistory];
redactionsSwitch = roomsSettingCell.mxkSwitch;
}
else if (indexPath.row == SETTINGS_SECTION_ROOMS_SHOW_UNSUPPORTED_EVENTS_INDEX)
{
roomsSettingCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_show_unsupported_events", @"MatrixConsole", nil);
roomsSettingCell.mxkSwitch.on = [_settings showUnsupportedEventsInRoomHistory];
unsupportedEventsSwitch = roomsSettingCell.mxkSwitch;
}
else if (indexPath.row == SETTINGS_SECTION_ROOMS_SORT_MEMBERS_INDEX)
{
roomsSettingCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_sort_by_last_seen", @"MatrixConsole", nil);
roomsSettingCell.mxkSwitch.on = [_settings sortRoomMembersUsingLastSeenTime];
sortMembersSwitch = roomsSettingCell.mxkSwitch;
}
else if (indexPath.row == SETTINGS_SECTION_ROOMS_DISPLAY_LEFT_MEMBERS_INDEX)
{
roomsSettingCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_display_left_members", @"MatrixConsole", nil);
roomsSettingCell.mxkSwitch.on = [_settings showLeftMembersInRoomMemberList];
displayLeftMembersSwitch = roomsSettingCell.mxkSwitch;
}
cell = roomsSettingCell;
}
}
else if (indexPath.section == SETTINGS_SECTION_CONFIGURATION_INDEX)
{
MXKTableViewCellWithTextView *configurationCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithTextView defaultReuseIdentifier]];
if (!configurationCell)
{
configurationCell = [[MXKTableViewCellWithTextView alloc] init];
}
NSString* appVersion = [AppDelegate theDelegate].appVersion;
NSString* build = [AppDelegate theDelegate].build;
if (build.length)
{
build = [NSString stringWithFormat:NSLocalizedStringFromTable(@"settings_config_build_number", @"MatrixConsole", nil), build];
}
NSString *configurationFormatText = [NSString stringWithFormat:@"%@\n%@\n%@\n%@", NSLocalizedStringFromTable(@"settings_config_ios_console_version", @"MatrixConsole", nil), NSLocalizedStringFromTable(@"settings_config_ios_kit_version", @"MatrixConsole", nil), NSLocalizedStringFromTable(@"settings_config_ios_sdk_version", @"MatrixConsole", nil), @"%@"];
configurationCell.mxkTextView.text = [NSString stringWithFormat:configurationFormatText, appVersion, MatrixKitVersion, MatrixSDKVersion, build];
cell = configurationCell;
}
else if (indexPath.section == SETTINGS_SECTION_COMMANDS_INDEX)
{
MXKTableViewCellWithTextView *commandsCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithTextView defaultReuseIdentifier]];
if (!commandsCell)
{
commandsCell = [[MXKTableViewCellWithTextView alloc] init];
}
commandsCell.mxkTextView.text = NSLocalizedStringFromTable(@"settings_command_commands", @"MatrixConsole", nil);
cell = commandsCell;
}
return cell;
}
#pragma mark - UITableView delegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == SETTINGS_SECTION_ACCOUNTS_INDEX)
{
return 50;
}
else if (indexPath.section == SETTINGS_SECTION_CONTACTS_INDEX)
{
if ((indexPath.row == countryCodeRowIndex) && isSelectingCountryCode)
{
return 164;
}
}
else if (indexPath.section == SETTINGS_SECTION_ROOMS_INDEX)
{
if (indexPath.row == SETTINGS_SECTION_ROOMS_SET_CACHE_SIZE_INDEX)
{
return 88;
}
}
else if (indexPath.section == SETTINGS_SECTION_CONFIGURATION_INDEX)
{
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, MAXFLOAT)];
textView.font = [UIFont systemFontOfSize:14];
NSString* appVersion = [AppDelegate theDelegate].appVersion;
NSString* build = [AppDelegate theDelegate].build;
if (build.length)
{
build = [NSString stringWithFormat:NSLocalizedStringFromTable(@"settings_config_build_number", @"MatrixConsole", nil), build];
}
NSString *configurationFormatText = [NSString stringWithFormat:@"%@\n%@\n%@\n%@", NSLocalizedStringFromTable(@"settings_config_ios_console_version", @"MatrixConsole", nil), NSLocalizedStringFromTable(@"settings_config_ios_kit_version", @"MatrixConsole", nil), NSLocalizedStringFromTable(@"settings_config_ios_sdk_version", @"MatrixConsole", nil), @"%@"];
textView.text = [NSString stringWithFormat:configurationFormatText, appVersion, MatrixKitVersion, MatrixSDKVersion, build];
CGSize contentSize = [textView sizeThatFits:textView.frame.size];
return contentSize.height + 1;
}
else if (indexPath.section == SETTINGS_SECTION_COMMANDS_INDEX)
{
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, MAXFLOAT)];
textView.font = [UIFont systemFontOfSize:14];
textView.text = NSLocalizedStringFromTable(@"settings_command_commands", @"MatrixConsole", nil);
CGSize contentSize = [textView sizeThatFits:textView.frame.size];
return contentSize.height + 1;
}
return 44;
}
- (CGFloat) tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 30;
}
- (CGFloat) tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
return 1;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *sectionHeader = [[UIView alloc] initWithFrame:[tableView rectForHeaderInSection:section]];
sectionHeader.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:1.0];
UILabel *sectionLabel = [[UILabel alloc] initWithFrame:CGRectMake(5, 5, sectionHeader.frame.size.width - 10, sectionHeader.frame.size.height - 10)];
sectionLabel.font = [UIFont boldSystemFontOfSize:16];
sectionLabel.backgroundColor = [UIColor clearColor];
[sectionHeader addSubview:sectionLabel];
if (section == SETTINGS_SECTION_ACCOUNTS_INDEX)
{
sectionLabel.text = NSLocalizedStringFromTable(@"accounts", @"MatrixConsole", nil);
UIButton *addAccount = [UIButton buttonWithType:UIButtonTypeContactAdd];
[addAccount addTarget:self action:@selector(addAccount:) forControlEvents:UIControlEventTouchUpInside];
CGRect frame = addAccount.frame;
frame.origin.x = sectionHeader.frame.size.width - frame.size.width - 8;
frame.origin.y = (sectionHeader.frame.size.height - frame.size.height) / 2;
addAccount.frame = frame;
[sectionHeader addSubview:addAccount];
addAccount.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin);
sectionHeader.userInteractionEnabled = YES;
}
else if (section == SETTINGS_SECTION_CONTACTS_INDEX)
{
sectionLabel.text = NSLocalizedStringFromTable(@"contacts", @"MatrixConsole", nil);
}
else if (section == SETTINGS_SECTION_ROOMS_INDEX)
{
sectionLabel.text = NSLocalizedStringFromTable(@"settings_title_rooms", @"MatrixConsole", nil);
}
else if (section == SETTINGS_SECTION_CONFIGURATION_INDEX)
{
sectionLabel.text = NSLocalizedStringFromTable(@"settings_title_config", @"MatrixConsole", nil);
}
else if (section == SETTINGS_SECTION_COMMANDS_INDEX)
{
sectionLabel.text = NSLocalizedStringFromTable(@"settings_title_commands", @"MatrixConsole", nil);
}
else
{
sectionHeader = nil;
}
return sectionHeader;
}
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (self.tableView == aTableView)
{
if (indexPath.section == SETTINGS_SECTION_ACCOUNTS_INDEX)
{
NSArray *accounts = [[MXKAccountManager sharedManager] accounts];
if (indexPath.row < accounts.count)
{
selectedAccount = [accounts objectAtIndex:indexPath.row];
[self performSegueWithIdentifier:@"showAccountDetails" sender:self];
}
}
else if (indexPath.section == SETTINGS_SECTION_CONTACTS_INDEX)
{
if (indexPath.row == countryCodeRowIndex)
{
isSelectingCountryCode = YES;
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
}
[aTableView deselectRowAtIndexPath:indexPath animated:YES];
}
}
#pragma mark - UIPickerViewDataSource
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return [countryCodes count];
}
#pragma mark - UIPickerViewDelegate
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
return [[countryCodes objectAtIndex:row] valueForKey:@"country"];
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
// sanity check
if ((row >= 0) && (row < countryCodes.count))
{
NSDictionary* dict = [countryCodes objectAtIndex:row];
selectedCountryCode = [dict valueForKey:@"id"];
}
isSelectingCountryCode = NO;
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
#pragma mark - Cache handling
// return the MX cache size in bytes
- (NSUInteger)MXCacheSize
{
NSUInteger cacheSize = 0;
NSArray *mxSessions = self.mxSessions;
for (MXSession *mxSession in mxSessions)
{
if (mxSession.store && [mxSession.store isKindOfClass:[MXFileStore class]])
{
MXFileStore *fileStore = (MXFileStore*)mxSession.store;
cacheSize += fileStore.diskUsage;
}
}
return cacheSize;
}
// return the sum of the caches (MX cache + media cache ...) in bytes
- (NSUInteger)cachesSize
{
return self.MXCacheSize + [MXKMediaManager cacheSize];
}
// defines the min allow cache size in bytes
- (NSUInteger)minCachesSize
{
// add a 50MB margin to avoid cache file deletion
return self.MXCacheSize + [MXKMediaManager minCacheSize] + 50 * 1024 * 1024;
}
// defines the current max caches size in bytes
- (NSUInteger)currentMaxCachesSize
{
return self.MXCacheSize + [MXKMediaManager currentMaxCacheSize];
}
- (void)setCurrentMaxCachesSize:(NSUInteger)maxCachesSize
{
[MXKMediaManager setCurrentMaxCacheSize:maxCachesSize - self.MXCacheSize];
}
// defines the max allowed caches size in bytes
- (NSUInteger) maxAllowedCachesSize
{
return self.MXCacheSize + [MXKMediaManager maxAllowedCacheSize];
}
@end
+26
View File
@@ -0,0 +1,26 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <Foundation/Foundation.h>
/*
This file exists only to force Xcode to use the GNU c++ standard library (libstdc++).
Even if the project settings indicate they want to use libstdc++, if there is no .mm file, Xcode
continues to use its c++ lib. That prevents the app from linking if it includes some .hh files.
@see: http://stackoverflow.com/a/19250215/3936576
*/
+34
View File
@@ -0,0 +1,34 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import <MXLogger.h>
int main(int argc, char * argv[]) {
@autoreleasepool {
// Redirect NSLogs to files only if we are not debugging
if (!isatty(STDERR_FILENO)) {
[MXLogger redirectNSLogToFiles:YES];
}
// Catch and log crashes
[MXLogger logCrashes:YES];
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}