Merge MatrixKit develop with commit hash: b85b736313bec0592bd1cabc68035d97f5331137

This commit is contained in:
SBiOSoftWhare
2021-12-03 11:47:24 +01:00
parent 475fe5552f
commit 1d9f01b5e3
475 changed files with 87437 additions and 0 deletions
@@ -0,0 +1,43 @@
/*
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 "MXKTableViewCell.h"
#import "MXKImageView.h"
#import "MXKAccount.h"
/**
MXKAccountTableViewCell instance is a table view cell used to display a matrix user.
*/
@interface MXKAccountTableViewCell : MXKTableViewCell
/**
The displayed account
*/
@property (nonatomic) MXKAccount* mxAccount;
/**
The default account picture displayed when no picture is defined.
*/
@property (nonatomic) UIImage *picturePlaceholder;
@property (strong, nonatomic) IBOutlet MXKImageView* accountPicture;
@property (strong, nonatomic) IBOutlet UILabel* accountDisplayName;
@property (strong, nonatomic) IBOutlet UISwitch* accountSwitchToggle;
@end
@@ -0,0 +1,96 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector 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 "MXKAccountTableViewCell.h"
@import MatrixSDK.MXMediaManager;
#import "NSBundle+MatrixKit.h"
@implementation MXKAccountTableViewCell
- (void)customizeTableViewCellRendering
{
[super customizeTableViewCellRendering];
self.accountPicture.defaultBackgroundColor = [UIColor clearColor];
}
- (void)setMxAccount:(MXKAccount *)mxAccount
{
UIColor *presenceColor = nil;
_accountDisplayName.text = mxAccount.fullDisplayName;
if (mxAccount.mxSession)
{
_accountPicture.mediaFolder = kMXMediaManagerAvatarThumbnailFolder;
_accountPicture.enableInMemoryCache = YES;
[_accountPicture setImageURI:mxAccount.userAvatarUrl
withType:nil
andImageOrientation:UIImageOrientationUp
toFitViewSize:_accountPicture.frame.size
withMethod:MXThumbnailingMethodCrop
previewImage:self.picturePlaceholder
mediaManager:mxAccount.mxSession.mediaManager];
presenceColor = [MXKAccount presenceColor:mxAccount.userPresence];
}
else
{
_accountPicture.image = self.picturePlaceholder;
}
if (presenceColor)
{
_accountPicture.layer.borderWidth = 2;
_accountPicture.layer.borderColor = presenceColor.CGColor;
}
else
{
_accountPicture.layer.borderWidth = 0;
}
_accountSwitchToggle.on = !mxAccount.disabled;
if (mxAccount.disabled)
{
_accountDisplayName.textColor = [UIColor lightGrayColor];
}
else
{
_accountDisplayName.textColor = [UIColor blackColor];
}
_mxAccount = mxAccount;
}
- (UIImage*)picturePlaceholder
{
return [NSBundle mxk_imageFromMXKAssetsBundleWithName:@"default-profile"];
}
- (void)layoutSubviews
{
[super layoutSubviews];
// Round image view
[_accountPicture.layer setCornerRadius:_accountPicture.frame.size.width / 2];
_accountPicture.clipsToBounds = YES;
}
@end
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="odA-la-SwE" customClass="MXKAccountTableViewCell">
<rect key="frame" x="0.0" y="0.0" width="600" height="50"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="odA-la-SwE" id="Ice-YN-Ffy">
<rect key="frame" x="0.0" y="0.0" width="600" height="49"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="v7q-rw-kph" customClass="MXKImageView">
<rect key="frame" x="5" y="5" width="39" height="39"/>
<color key="backgroundColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" secondItem="v7q-rw-kph" secondAttribute="height" multiplier="1:1" id="FNp-g0-6gK"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rfi-Fa-7Uq">
<rect key="frame" x="54" y="14" width="536" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<switch hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="k0G-KZ-5ft">
<rect key="frame" x="543" y="9" width="51" height="31"/>
</switch>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="rfi-Fa-7Uq" secondAttribute="trailing" constant="10" id="TxZ-NW-Qjo"/>
<constraint firstAttribute="centerY" secondItem="rfi-Fa-7Uq" secondAttribute="centerY" id="Z4K-cO-leY"/>
<constraint firstAttribute="bottom" secondItem="v7q-rw-kph" secondAttribute="bottom" constant="5" id="ddM-yA-zDv"/>
<constraint firstItem="v7q-rw-kph" firstAttribute="leading" secondItem="Ice-YN-Ffy" secondAttribute="leading" constant="5" id="gWc-fO-98S"/>
<constraint firstAttribute="centerY" secondItem="k0G-KZ-5ft" secondAttribute="centerY" id="iEh-VQ-sVC"/>
<constraint firstAttribute="trailing" secondItem="k0G-KZ-5ft" secondAttribute="trailing" constant="8" id="msS-z1-wdL"/>
<constraint firstItem="rfi-Fa-7Uq" firstAttribute="leading" secondItem="v7q-rw-kph" secondAttribute="trailing" constant="10" id="p61-62-Veh"/>
<constraint firstItem="v7q-rw-kph" firstAttribute="top" secondItem="Ice-YN-Ffy" secondAttribute="top" constant="5" id="yf2-Mb-AeI"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="accountDisplayName" destination="rfi-Fa-7Uq" id="Tfu-Fo-eWp"/>
<outlet property="accountPicture" destination="v7q-rw-kph" id="Ss6-iD-9vq"/>
<outlet property="accountSwitchToggle" destination="k0G-KZ-5ft" id="k4E-FL-XyR"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -0,0 +1,41 @@
/*
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 "MXKAuthInputsView.h"
@interface MXKAuthInputsEmailCodeBasedView : MXKAuthInputsView
/**
The input text field related to user id or user login.
*/
@property (weak, nonatomic) IBOutlet UITextField *userLoginTextField;
/**
The input text field used to fill an email or the related token.
*/
@property (weak, nonatomic) IBOutlet UITextField *emailAndTokenTextField;
/**
Label used to prompt user to fill the email token.
*/
@property (weak, nonatomic) IBOutlet UILabel *promptEmailTokenLabel;
/**
The text field related to the display name. This item is displayed in case of registration.
*/
@property (weak, nonatomic) IBOutlet UITextField *displayNameTextField;
@end
@@ -0,0 +1,202 @@
/*
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 "MXKAuthInputsEmailCodeBasedView.h"
#import "NSBundle+MatrixKit.h"
#import "MXKSwiftHeader.h"
@implementation MXKAuthInputsEmailCodeBasedView
+ (UINib *)nib
{
return [UINib nibWithNibName:NSStringFromClass([MXKAuthInputsEmailCodeBasedView class])
bundle:[NSBundle bundleForClass:[MXKAuthInputsEmailCodeBasedView class]]];
}
- (void)awakeFromNib
{
[super awakeFromNib];
_userLoginTextField.placeholder = [MatrixKitL10n loginUserIdPlaceholder];
_emailAndTokenTextField.placeholder = [MatrixKitL10n loginEmailPlaceholder];
_promptEmailTokenLabel.text = [MatrixKitL10n loginPromptEmailToken];
_displayNameTextField.placeholder = [MatrixKitL10n loginDisplayNamePlaceholder];
}
#pragma mark -
- (BOOL)setAuthSession:(MXAuthenticationSession *)authSession withAuthType:(MXKAuthenticationType)authType;
{
if (type == MXKAuthenticationTypeLogin || type == MXKAuthenticationTypeRegister)
{
// Validate first the provided session
MXAuthenticationSession *validSession = [self validateAuthenticationSession:authSession];
if ([super setAuthSession:validSession withAuthType:authType])
{
// Set initial layout
self.userLoginTextField.hidden = NO;
self.promptEmailTokenLabel.hidden = YES;
if (type == MXKAuthenticationTypeLogin)
{
self.emailAndTokenTextField.returnKeyType = UIReturnKeyDone;
self.displayNameTextField.hidden = YES;
self.viewHeightConstraint.constant = self.displayNameTextField.frame.origin.y;
}
else
{
self.emailAndTokenTextField.returnKeyType = UIReturnKeyNext;
self.displayNameTextField.hidden = NO;
self.viewHeightConstraint.constant = 122;
}
return YES;
}
}
return NO;
}
- (NSString*)validateParameters
{
NSString *errorMsg = [super validateParameters];
if (!errorMsg)
{
if (!self.areAllRequiredFieldsSet)
{
errorMsg = [MatrixKitL10n loginInvalidParam];
}
}
return errorMsg;
}
- (BOOL)areAllRequiredFieldsSet
{
BOOL ret = [super areAllRequiredFieldsSet];
// Check required fields //FIXME what are required fields in this authentication flow?
ret = (ret && self.userLoginTextField.text.length && self.emailAndTokenTextField.text.length);
return ret;
}
- (void)dismissKeyboard
{
[self.userLoginTextField resignFirstResponder];
[self.emailAndTokenTextField resignFirstResponder];
[self.displayNameTextField resignFirstResponder];
[super dismissKeyboard];
}
- (void)nextStep
{
// Consider here the email token has been requested with success
[super nextStep];
self.userLoginTextField.hidden = YES;
self.promptEmailTokenLabel.hidden = NO;
self.emailAndTokenTextField.placeholder = nil;
self.emailAndTokenTextField.returnKeyType = UIReturnKeyDone;
self.displayNameTextField.hidden = YES;
}
- (NSString*)userId
{
return self.userLoginTextField.text;
}
#pragma mark UITextField delegate
- (BOOL)textFieldShouldReturn:(UITextField*)textField
{
if (textField.returnKeyType == UIReturnKeyDone)
{
// "Done" key has been pressed
[textField resignFirstResponder];
// Launch authentication now
[self.delegate authInputsViewDidPressDoneKey:self];
}
else
{
//"Next" key has been pressed
if (textField == self.userLoginTextField)
{
[self.emailAndTokenTextField becomeFirstResponder];
}
else if (textField == self.emailAndTokenTextField)
{
[self.displayNameTextField becomeFirstResponder];
}
}
return YES;
}
#pragma mark -
- (MXAuthenticationSession*)validateAuthenticationSession:(MXAuthenticationSession*)authSession
{
// Check whether at least one of the listed flow is supported.
BOOL isSupported = NO;
for (MXLoginFlow *loginFlow in authSession.flows)
{
// Check whether flow type is defined
if ([loginFlow.type isEqualToString:kMXLoginFlowTypeEmailCode])
{
isSupported = YES;
break;
}
else if (loginFlow.stages.count == 1 && [loginFlow.stages.firstObject isEqualToString:kMXLoginFlowTypeEmailCode])
{
isSupported = YES;
break;
}
}
if (isSupported)
{
if (authSession.flows.count == 1)
{
// Return the original session.
return authSession;
}
else
{
// Keep only the supported flow.
MXAuthenticationSession *updatedAuthSession = [[MXAuthenticationSession alloc] init];
updatedAuthSession.session = authSession.session;
updatedAuthSession.params = authSession.params;
updatedAuthSession.flows = @[[MXLoginFlow modelFromJSON:@{@"stages":@[kMXLoginFlowTypeEmailCode]}]];
return updatedAuthSession;
}
}
return nil;
}
@end
@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Nmn-Ry-t32" customClass="MXKAuthInputsEmailCodeBasedView">
<rect key="frame" x="0.0" y="0.0" width="300" height="122"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Matrix ID (e.g. @bob:matrix.org or bob)" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="oen-Yq-xJw">
<rect key="frame" x="14" y="8" width="272" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="gfu-Ef-79U"/>
<constraint firstAttribute="width" constant="272" id="y4N-L3-qlh"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="next"/>
<connections>
<outlet property="delegate" destination="Nmn-Ry-t32" id="Ari-Ab-eGE"/>
</connections>
</textField>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Email address" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="mbS-mV-L86" userLabel="Email-Token">
<rect key="frame" x="14" y="46" width="272" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="ecT-NT-FqH"/>
<constraint firstAttribute="width" constant="272" id="xkx-sr-F8M"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="done" secureTextEntry="YES"/>
<connections>
<outlet property="delegate" destination="Nmn-Ry-t32" id="yrA-lO-OCi"/>
</connections>
</textField>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Please enter your email validation token:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vDa-DE-6Bn">
<rect key="frame" x="20" y="8" width="260" height="30"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<textField hidden="YES" opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Display name (e.g. Bob Obson)" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="FVM-ec-tja" userLabel="DisplayName">
<rect key="frame" x="14" y="84" width="272" height="30"/>
<constraints>
<constraint firstAttribute="width" constant="272" id="1b5-Ej-eUB"/>
<constraint firstAttribute="height" constant="30" id="bEU-4F-cUv"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="done" secureTextEntry="YES"/>
<connections>
<outlet property="delegate" destination="Nmn-Ry-t32" id="Q3D-XL-8Ke"/>
</connections>
</textField>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="FVM-ec-tja" firstAttribute="top" secondItem="mbS-mV-L86" secondAttribute="bottom" constant="8" id="0BV-8c-ZVg"/>
<constraint firstAttribute="height" constant="122" id="1nP-RR-5GK"/>
<constraint firstAttribute="centerX" secondItem="mbS-mV-L86" secondAttribute="centerX" id="2R2-Du-o97"/>
<constraint firstItem="oen-Yq-xJw" firstAttribute="top" secondItem="Nmn-Ry-t32" secondAttribute="top" constant="8" id="BkW-Np-qB3"/>
<constraint firstItem="mbS-mV-L86" firstAttribute="top" secondItem="oen-Yq-xJw" secondAttribute="bottom" constant="8" id="TRx-gf-Afp"/>
<constraint firstAttribute="centerX" secondItem="FVM-ec-tja" secondAttribute="centerX" id="c9K-6H-ufd"/>
<constraint firstItem="mbS-mV-L86" firstAttribute="top" secondItem="vDa-DE-6Bn" secondAttribute="bottom" constant="8" id="cq8-qs-59M"/>
<constraint firstItem="vDa-DE-6Bn" firstAttribute="top" secondItem="Nmn-Ry-t32" secondAttribute="top" constant="8" id="eAp-Ob-hFr"/>
<constraint firstAttribute="centerX" secondItem="vDa-DE-6Bn" secondAttribute="centerX" id="otm-qx-iNL"/>
<constraint firstAttribute="centerX" secondItem="oen-Yq-xJw" secondAttribute="centerX" id="s7Y-qf-yrY"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<nil key="simulatedTopBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
<outlet property="displayNameTextField" destination="FVM-ec-tja" id="pMF-oP-7Jj"/>
<outlet property="emailAndTokenTextField" destination="mbS-mV-L86" id="nY5-gT-wGe"/>
<outlet property="promptEmailTokenLabel" destination="vDa-DE-6Bn" id="X3w-5E-0Kg"/>
<outlet property="userLoginTextField" destination="oen-Yq-xJw" id="OM2-gy-O9P"/>
<outlet property="viewHeightConstraint" destination="1nP-RR-5GK" id="s3W-1L-bWN"/>
</connections>
</view>
</objects>
</document>
@@ -0,0 +1,46 @@
/*
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 "MXKAuthInputsView.h"
@interface MXKAuthInputsPasswordBasedView : MXKAuthInputsView
/**
The input text field related to user id or user login.
*/
@property (weak, nonatomic) IBOutlet UITextField *userLoginTextField;
/**
The input text field used to fill the password.
*/
@property (weak, nonatomic) IBOutlet UITextField *passWordTextField;
/**
The input text field used to fill an email. This item is optional, it is added in case of registration.
*/
@property (weak, nonatomic) IBOutlet UITextField *emailTextField;
/**
Label used to display email field information.
*/
@property (weak, nonatomic) IBOutlet UILabel *emailInfoLabel;
/**
The text field related to the display name. This item is displayed in case of registration.
*/
@property (weak, nonatomic) IBOutlet UITextField *displayNameTextField;
@end
@@ -0,0 +1,253 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations 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 "MXKAuthInputsPasswordBasedView.h"
#import "MXKTools.h"
#import "MXKAppSettings.h"
#import "NSBundle+MatrixKit.h"
#import "MXKSwiftHeader.h"
@implementation MXKAuthInputsPasswordBasedView
+ (UINib *)nib
{
return [UINib nibWithNibName:NSStringFromClass([MXKAuthInputsPasswordBasedView class])
bundle:[NSBundle bundleForClass:[MXKAuthInputsPasswordBasedView class]]];
}
- (void)awakeFromNib
{
[super awakeFromNib];
_userLoginTextField.placeholder = [MatrixKitL10n loginUserIdPlaceholder];
_passWordTextField.placeholder = [MatrixKitL10n loginPasswordPlaceholder];
_emailTextField.placeholder = [NSString stringWithFormat:@"%@ (%@)", [MatrixKitL10n loginEmailPlaceholder], [MatrixKitL10n loginOptionalField]];
_emailInfoLabel.text = [MatrixKitL10n loginEmailInfo];
_displayNameTextField.placeholder = [MatrixKitL10n loginDisplayNamePlaceholder];
}
#pragma mark -
- (BOOL)setAuthSession:(MXAuthenticationSession *)authSession withAuthType:(MXKAuthenticationType)authType;
{
if (type == MXKAuthenticationTypeLogin || type == MXKAuthenticationTypeRegister)
{
// Validate first the provided session
MXAuthenticationSession *validSession = [self validateAuthenticationSession:authSession];
if ([super setAuthSession:validSession withAuthType:authType])
{
if (type == MXKAuthenticationTypeLogin)
{
self.passWordTextField.returnKeyType = UIReturnKeyDone;
self.emailTextField.hidden = YES;
self.emailInfoLabel.hidden = YES;
self.displayNameTextField.hidden = YES;
self.viewHeightConstraint.constant = self.displayNameTextField.frame.origin.y;
}
else
{
self.passWordTextField.returnKeyType = UIReturnKeyNext;
self.emailTextField.hidden = NO;
self.emailInfoLabel.hidden = NO;
self.displayNameTextField.hidden = NO;
self.viewHeightConstraint.constant = 179;
}
return YES;
}
}
return NO;
}
- (NSString*)validateParameters
{
NSString *errorMsg = [super validateParameters];
if (!errorMsg)
{
// Check user login and pass fields
if (!self.areAllRequiredFieldsSet)
{
errorMsg = [MatrixKitL10n loginInvalidParam];
}
}
return errorMsg;
}
- (void)prepareParameters:(void (^)(NSDictionary *parameters, NSError *error))callback
{
if (callback)
{
// Sanity check on required fields
if (!self.areAllRequiredFieldsSet)
{
callback(nil, [NSError errorWithDomain:MXKAuthErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey:[MatrixKitL10n loginInvalidParam]}]);
return;
}
// Retrieve the user login and check whether it is an email or a username.
// TODO: Update the UI view to support the login based on a mobile phone number.
NSString *user = self.userLoginTextField.text;
user = [user stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
BOOL isEmailAddress = [MXTools isEmailAddress:user];
NSDictionary *parameters;
if (isEmailAddress)
{
parameters = @{
@"type": kMXLoginFlowTypePassword,
@"identifier": @{
@"type": kMXLoginIdentifierTypeThirdParty,
@"medium": kMX3PIDMediumEmail,
@"address": user
},
@"password": self.passWordTextField.text
};
}
else
{
parameters = @{
@"type": kMXLoginFlowTypePassword,
@"identifier": @{
@"type": kMXLoginIdentifierTypeUser,
@"user": user
},
@"password": self.passWordTextField.text
};
}
callback(parameters, nil);
}
}
- (BOOL)areAllRequiredFieldsSet
{
BOOL ret = [super areAllRequiredFieldsSet];
// Check user login and pass fields
ret = (ret && self.userLoginTextField.text.length && self.passWordTextField.text.length);
return ret;
}
- (void)dismissKeyboard
{
[self.userLoginTextField resignFirstResponder];
[self.passWordTextField resignFirstResponder];
[self.emailTextField resignFirstResponder];
[self.displayNameTextField resignFirstResponder];
[super dismissKeyboard];
}
- (NSString*)userId
{
return self.userLoginTextField.text;
}
- (NSString*)password
{
return self.passWordTextField.text;
}
#pragma mark UITextField delegate
- (BOOL)textFieldShouldReturn:(UITextField*)textField
{
if (textField.returnKeyType == UIReturnKeyDone)
{
// "Done" key has been pressed
[textField resignFirstResponder];
// Launch authentication now
[self.delegate authInputsViewDidPressDoneKey:self];
}
else
{
//"Next" key has been pressed
if (textField == self.userLoginTextField)
{
[self.passWordTextField becomeFirstResponder];
}
else if (textField == self.passWordTextField)
{
[self.displayNameTextField becomeFirstResponder];
}
else if (textField == self.displayNameTextField)
{
[self.emailTextField becomeFirstResponder];
}
}
return YES;
}
#pragma mark -
- (MXAuthenticationSession*)validateAuthenticationSession:(MXAuthenticationSession*)authSession
{
// Check whether at least one of the listed flow is supported.
BOOL isSupported = NO;
for (MXLoginFlow *loginFlow in authSession.flows)
{
// Check whether flow type is defined
if ([loginFlow.type isEqualToString:kMXLoginFlowTypePassword])
{
isSupported = YES;
break;
}
else if (loginFlow.stages.count == 1 && [loginFlow.stages.firstObject isEqualToString:kMXLoginFlowTypePassword])
{
isSupported = YES;
break;
}
}
if (isSupported)
{
if (authSession.flows.count == 1)
{
// Return the original session.
return authSession;
}
else
{
// Keep only the supported flow.
MXAuthenticationSession *updatedAuthSession = [[MXAuthenticationSession alloc] init];
updatedAuthSession.session = authSession.session;
updatedAuthSession.params = authSession.params;
updatedAuthSession.flows = @[[MXLoginFlow modelFromJSON:@{@"stages":@[kMXLoginFlowTypePassword]}]];
return updatedAuthSession;
}
}
return nil;
}
@end
@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="x74-04-ezp" customClass="MXKAuthInputsPasswordBasedView">
<rect key="frame" x="0.0" y="0.0" width="300" height="179"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Matrix ID (e.g. @bob:matrix.org or bob)" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="i51-ym-i9T">
<rect key="frame" x="14" y="8" width="272" height="30"/>
<constraints>
<constraint firstAttribute="width" constant="272" id="IhR-uY-MJZ"/>
<constraint firstAttribute="height" constant="30" id="x3j-uC-j0U"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="next"/>
<connections>
<outlet property="delegate" destination="x74-04-ezp" id="yCp-R2-dNp"/>
</connections>
</textField>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Password" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="6rs-rR-DkS">
<rect key="frame" x="14" y="46" width="272" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="Fh2-oZ-UBe"/>
<constraint firstAttribute="width" constant="272" id="pCs-je-5Rd"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="done" secureTextEntry="YES"/>
<connections>
<outlet property="delegate" destination="x74-04-ezp" id="t8U-qP-IHU"/>
</connections>
</textField>
<textField hidden="YES" opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Display name (e.g. Bob Obson)" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="BQM-LP-8Eq" userLabel="DisplayName">
<rect key="frame" x="14" y="84" width="272" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="Iwg-IP-ITp"/>
<constraint firstAttribute="width" constant="272" id="ZG2-Aw-XkP"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="next" secureTextEntry="YES"/>
<connections>
<outlet property="delegate" destination="x74-04-ezp" id="qkT-a6-G0Y"/>
</connections>
</textField>
<textField hidden="YES" opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Email address (optional)" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="odF-W1-Vdr" userLabel="Email">
<rect key="frame" x="14" y="126" width="272" height="30"/>
<constraints>
<constraint firstAttribute="width" constant="272" id="wZg-GR-5XL"/>
<constraint firstAttribute="height" constant="30" id="zVy-8W-zmR"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="done" secureTextEntry="YES"/>
<connections>
<outlet property="delegate" destination="x74-04-ezp" id="isi-U5-D9A"/>
</connections>
</textField>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" preferredMaxLayoutWidth="0.0" translatesAutoresizingMaskIntoConstraints="NO" id="OHZ-Ye-nch" userLabel="EmailInfoLabel">
<rect key="frame" x="14" y="156" width="272" height="15"/>
<string key="text">Specify an email address lets other users find you on Matrix more easily, and will give you a way to reset your password in the future.</string>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="i51-ym-i9T" firstAttribute="top" secondItem="x74-04-ezp" secondAttribute="top" constant="8" id="2sO-Wi-AFi"/>
<constraint firstItem="BQM-LP-8Eq" firstAttribute="top" secondItem="6rs-rR-DkS" secondAttribute="bottom" constant="8" id="2vZ-NH-f0H"/>
<constraint firstAttribute="centerX" secondItem="odF-W1-Vdr" secondAttribute="centerX" id="BbL-ex-otM"/>
<constraint firstItem="OHZ-Ye-nch" firstAttribute="leading" secondItem="x74-04-ezp" secondAttribute="leading" constant="14" id="Ctv-P7-FQl"/>
<constraint firstItem="6rs-rR-DkS" firstAttribute="top" secondItem="i51-ym-i9T" secondAttribute="bottom" constant="8" id="EJf-md-dcj"/>
<constraint firstAttribute="height" constant="179" id="JbD-Ce-bTY"/>
<constraint firstItem="OHZ-Ye-nch" firstAttribute="top" secondItem="odF-W1-Vdr" secondAttribute="bottom" id="LTI-Eq-pH6"/>
<constraint firstAttribute="centerX" secondItem="i51-ym-i9T" secondAttribute="centerX" id="WKP-y0-Jon"/>
<constraint firstAttribute="centerX" secondItem="BQM-LP-8Eq" secondAttribute="centerX" id="aQQ-kb-tLS"/>
<constraint firstAttribute="centerX" secondItem="6rs-rR-DkS" secondAttribute="centerX" id="nIk-ev-cFe"/>
<constraint firstItem="odF-W1-Vdr" firstAttribute="top" secondItem="BQM-LP-8Eq" secondAttribute="bottom" constant="12" id="qjk-LC-4NN"/>
<constraint firstAttribute="centerX" secondItem="OHZ-Ye-nch" secondAttribute="centerX" id="tvZ-5z-KAm"/>
<constraint firstAttribute="trailing" secondItem="OHZ-Ye-nch" secondAttribute="trailing" constant="14" id="yQg-6n-P8L"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<nil key="simulatedTopBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
<outlet property="displayNameTextField" destination="BQM-LP-8Eq" id="Ptq-yP-WaZ"/>
<outlet property="emailInfoLabel" destination="OHZ-Ye-nch" id="fh6-Sz-X5K"/>
<outlet property="emailTextField" destination="odF-W1-Vdr" id="DOS-H7-MZy"/>
<outlet property="passWordTextField" destination="6rs-rR-DkS" id="VeL-kt-Fpp"/>
<outlet property="userLoginTextField" destination="i51-ym-i9T" id="XKi-6m-tFv"/>
<outlet property="viewHeightConstraint" destination="JbD-Ce-bTY" id="09n-jN-RM1"/>
</connections>
</view>
</objects>
</document>
@@ -0,0 +1,242 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2019 The Matrix.org Foundation C.I.C
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 <MatrixSDK/MatrixSDK.h>
#import "MXKView.h"
extern NSString *const MXKAuthErrorDomain;
/**
Authentication types
*/
typedef enum {
/**
Type used to sign up.
*/
MXKAuthenticationTypeRegister,
/**
Type used to sign in.
*/
MXKAuthenticationTypeLogin,
/**
Type used to restore an existing account by reseting the password.
*/
MXKAuthenticationTypeForgotPassword
} MXKAuthenticationType;
@class MXKAuthInputsView;
/**
`MXKAuthInputsView` delegate
*/
@protocol MXKAuthInputsViewDelegate <NSObject>
/**
Tells the delegate that an alert must be presented.
@param authInputsView the authentication inputs view.
@param alert the alert to present.
*/
- (void)authInputsView:(MXKAuthInputsView*)authInputsView presentAlertController:(UIAlertController*)alert;
/**
For some input fields, the return key of the keyboard is defined as `Done` key.
By this method, the delegate is notified when this key is pressed.
*/
- (void)authInputsViewDidPressDoneKey:(MXKAuthInputsView *)authInputsView;
@optional
/**
The matrix REST Client used to validate third-party identifiers.
*/
- (MXRestClient *)authInputsViewThirdPartyIdValidationRestClient:(MXKAuthInputsView *)authInputsView;
/**
The identity service used to validate third-party identifiers.
*/
- (MXIdentityService *)authInputsViewThirdPartyIdValidationIdentityService:(MXKAuthInputsView *)authInputsView;
/**
Tell the delegate to present a view controller modally.
Note: This method is used to display the countries list during the phone number handling.
@param authInputsView the authentication inputs view.
@param viewControllerToPresent the view controller to present.
@param animated YES to animate the presentation.
*/
- (void)authInputsView:(MXKAuthInputsView *)authInputsView presentViewController:(UIViewController*)viewControllerToPresent animated:(BOOL)animated;
/**
Tell the delegate to cancel the current operation.
*/
- (void)authInputsViewDidCancelOperation:(MXKAuthInputsView *)authInputsView;
/**
Tell the delegate to autodiscover the server configuration.
*/
- (void)authInputsView:(MXKAuthInputsView *)authInputsView autoDiscoverServerWithDomain:(NSString*)domain;
@end
/**
`MXKAuthInputsView` is a base class to handle authentication inputs.
*/
@interface MXKAuthInputsView : MXKView <UITextFieldDelegate>
{
@protected
/**
The authentication type (`MXKAuthenticationTypeLogin` by default).
*/
MXKAuthenticationType type;
/**
The authentication session (nil by default).
*/
MXAuthenticationSession *currentSession;
/**
Alert used to display inputs error.
*/
UIAlertController *inputsAlert;
}
/**
The view delegate.
*/
@property (nonatomic, weak) id <MXKAuthInputsViewDelegate> delegate;
/**
The current authentication type (`MXKAuthenticationTypeLogin` by default).
*/
@property (nonatomic, readonly) MXKAuthenticationType authType;
/**
The current authentication session if any.
*/
@property (nonatomic, readonly) MXAuthenticationSession *authSession;
/**
The current filled user identifier (nil by default).
*/
@property (nonatomic, readonly) NSString *userId;
/**
The current filled password (nil by default).
*/
@property (nonatomic, readonly) NSString *password;
/**
The layout constraint defined on the view height. This height takes into account shown/hidden fields.
*/
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *viewHeightConstraint;
/**
Returns the `UINib` object initialized for the auth inputs view.
@return The initialized `UINib` object or `nil` if there were errors during
initialization or the nib file could not be located.
*/
+ (UINib *)nib;
/**
Creates and returns a new `MXKAuthInputsView` object.
@discussion This is the designated initializer for programmatic instantiation.
@return An initialized `MXKAuthInputsView` object if successful, `nil` otherwise.
*/
+ (instancetype)authInputsView;
/**
Finalize the authentication inputs view with a session and a type.
Use this method to restore the view in its initial step.
@discussion You may override this method to check/update the flows listed in the provided authentication session.
@param authSession the authentication session returned by the homeserver.
@param authType the authentication type (see 'MXKAuthenticationType').
@return YES if the provided session and type are supported by the MXKAuthInputsView-inherited class. Note the unsupported flows should be here removed from the stored authentication session (see the resulting session in the property named 'authSession').
*/
- (BOOL)setAuthSession:(MXAuthenticationSession *)authSession withAuthType:(MXKAuthenticationType)authType;
/**
Check the validity of the required parameters.
@return an error message in case of wrong parameters (nil by default).
*/
- (NSString*)validateParameters;
/**
Prepare the set of the inputs in order to launch an authentication process.
@param callback the block called when the parameters are prepared. The resulting parameter dictionary is nil
if something fails (for example when a parameter or a required input is missing). The failure reason is provided in error parameter (nil by default).
*/
- (void)prepareParameters:(void (^)(NSDictionary *parameters, NSError *error))callback;
/**
Update the current authentication session by providing the list of successful stages.
@param completedStages the list of stages the client has completed successfully. This is an array of MXLoginFlowType.
@param callback the block called when the parameters have been updated for the next stage. The resulting parameter dictionary is nil
if something fails (for example when a parameter or a required input is missing). The failure reason is provided in error parameter (nil by default).
*/
- (void)updateAuthSessionWithCompletedStages:(NSArray *)completedStages didUpdateParameters:(void (^)(NSDictionary *parameters, NSError *error))callback;
/**
Update the current authentication session by providing a set of registration parameters.
@discussion This operation failed if the current authentication type is MXKAuthenticationTypeLogin.
@param registrationParameters a set of parameters to use during the current registration process.
@return YES if the provided set of parameters is supported.
*/
- (BOOL)setExternalRegistrationParameters:(NSDictionary *)registrationParameters;
/**
Update the current authentication session by providing soft logout credentials.
*/
@property (nonatomic) MXCredentials *softLogoutCredentials;
/**
Tell whether all required fields are set
*/
- (BOOL)areAllRequiredFieldsSet;
/**
Force dismiss keyboard
*/
- (void)dismissKeyboard;
/**
Switch in next authentication flow step by updating the layout.
@discussion This method is supposed to be called only if the current operation succeeds.
*/
- (void)nextStep;
/**
Dispose any resources and listener.
*/
- (void)destroy;
@end
@@ -0,0 +1,156 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations 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 "MXKAuthInputsView.h"
#import "NSBundle+MatrixKit.h"
#import "MXKSwiftHeader.h"
NSString *const MXKAuthErrorDomain = @"MXKAuthErrorDomain";
@implementation MXKAuthInputsView
+ (UINib *)nib
{
// By default, no nib is available.
return nil;
}
+ (instancetype)authInputsView
{
// Check whether a xib is defined
if ([[self class] nib])
{
return [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject;
}
return [[[self class] alloc] init];
}
- (void)awakeFromNib
{
[super awakeFromNib];
[self setTranslatesAutoresizingMaskIntoConstraints: NO];
type = MXKAuthenticationTypeLogin;
}
- (instancetype)init
{
self = [super init];
if (self)
{
type = MXKAuthenticationTypeLogin;
}
return self;
}
#pragma mark -
- (BOOL)setAuthSession:(MXAuthenticationSession *)authSession withAuthType:(MXKAuthenticationType)authType
{
if (authSession)
{
type = authType;
currentSession = authSession;
return YES;
}
return NO;
}
- (NSString *)validateParameters
{
// Currently no field to check here
return nil;
}
- (void)prepareParameters:(void (^)(NSDictionary *parameters, NSError *error))callback
{
// Do nothing by default
if (callback)
{
callback (nil, [NSError errorWithDomain:MXKAuthErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey:[MatrixKitL10n notSupportedYet]}]);
}
}
- (void)updateAuthSessionWithCompletedStages:(NSArray *)completedStages didUpdateParameters:(void (^)(NSDictionary *parameters, NSError *error))callback
{
// Do nothing by default
if (callback)
{
callback (nil, [NSError errorWithDomain:MXKAuthErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey:[MatrixKitL10n notSupportedYet]}]);
}
}
- (BOOL)setExternalRegistrationParameters:(NSDictionary *)registrationParameters
{
// Not supported by default
return NO;
}
- (BOOL)areAllRequiredFieldsSet
{
// Currently no field to check here
return YES;
}
- (void)dismissKeyboard
{
}
- (void)nextStep
{
}
- (void)destroy
{
if (inputsAlert)
{
[inputsAlert dismissViewControllerAnimated:NO completion:nil];
inputsAlert = nil;
}
}
#pragma mark -
- (MXKAuthenticationType)authType
{
return type;
}
- (MXAuthenticationSession*)authSession
{
return currentSession;
}
- (NSString*)userId
{
return nil;
}
- (NSString*)password
{
return nil;
}
@end
@@ -0,0 +1,30 @@
/*
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 <MatrixSDK/MatrixSDK.h>
#import <WebKit/WebKit.h>
@interface MXKAuthenticationFallbackWebView : WKWebView <WKNavigationDelegate>
/**
Open authentication fallback page into the webview.
@param fallbackPage the fallback page hosted by a homeserver.
@param success the block called when the user has been successfully logged in or registered.
*/
- (void)openFallbackPage:(NSString*)fallbackPage success:(void (^)(MXLoginResponse *loginResponse))success;
@end
@@ -0,0 +1,181 @@
/*
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 "MXKAuthenticationFallbackWebView.h"
// Generic method to make a bridge between JS and the WKWebView
NSString *kMXKJavascriptSendObjectMessage = @"window.sendObjectMessage = function(parameters) { \
var iframe = document.createElement('iframe'); \
iframe.setAttribute('src', 'js:' + JSON.stringify(parameters)); \
\
document.documentElement.appendChild(iframe); \
iframe.parentNode.removeChild(iframe); \
iframe = null; \
};";
// The function the fallback page calls when the registration is complete
NSString *kMXKJavascriptOnRegistered = @"window.matrixRegistration.onRegistered = function(homeserverUrl, userId, accessToken) { \
sendObjectMessage({ \
'action': 'onRegistered', \
'homeServer': homeserverUrl, \
'userId': userId, \
'accessToken': accessToken \
}); \
};";
// The function the fallback page calls when the login is complete
NSString *kMXKJavascriptOnLogin = @"window.matrixLogin.onLogin = function(response) { \
sendObjectMessage({ \
'action': 'onLogin', \
'response': response \
}); \
};";
@interface MXKAuthenticationFallbackWebView ()
{
// The block called when the login or the registration is successful
void (^onSuccess)(MXLoginResponse *);
// Activity indicator
UIActivityIndicatorView *activityIndicator;
}
@end
@implementation MXKAuthenticationFallbackWebView
- (void)dealloc
{
if (activityIndicator)
{
[activityIndicator removeFromSuperview];
activityIndicator = nil;
}
}
- (void)openFallbackPage:(NSString *)fallbackPage success:(void (^)(MXLoginResponse *))success
{
self.navigationDelegate = self;
onSuccess = success;
// Add activity indicator
activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
activityIndicator.center = self.center;
[self addSubview:activityIndicator];
[activityIndicator startAnimating];
// Delete cookies to launch login process from scratch
for(NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies])
{
[[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
}
[self loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:fallbackPage]]];
}
#pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
if (activityIndicator)
{
[activityIndicator stopAnimating];
[activityIndicator removeFromSuperview];
activityIndicator = nil;
}
[self evaluateJavaScript:kMXKJavascriptSendObjectMessage completionHandler:^(id _Nullable response, NSError * _Nullable error) {
}];
[self evaluateJavaScript:kMXKJavascriptOnRegistered completionHandler:^(id _Nullable response, NSError * _Nullable error) {
}];
[self evaluateJavaScript:kMXKJavascriptOnLogin completionHandler:^(id _Nullable response, NSError * _Nullable error) {
}];
}
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
MXLogDebug(@"[MXKAuthenticationFallbackWebView] decidePolicyForNavigationAction");
NSString *urlString = navigationAction.request.URL.absoluteString;
if ([urlString hasPrefix:@"js:"])
{
// do not log urlString, it may have an access token
MXLogDebug(@"[MXKAuthenticationFallbackWebView] URL has js: prefix");
// Listen only to scheme of the JS-WKWebView bridge
NSString *jsonString = [[[urlString componentsSeparatedByString:@"js:"] lastObject] stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
NSDictionary *parameters = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers
error:&error];
if (error)
{
MXLogDebug(@"[MXKAuthenticationFallbackWebView] Error when parsing json: %@", error);
}
else
{
if ([@"onRegistered" isEqualToString:parameters[@"action"]])
{
// Translate the JS registration event to MXLoginResponse
// We cannot use [MXLoginResponse modelFromJSON:] because of https://github.com/matrix-org/synapse/issues/4756
// Because of this issue, we cannot get the device_id allocated by the homeserver
// TODO: Fix it once the homeserver issue is fixed (filed at https://github.com/vector-im/riot-meta/issues/273).
MXLoginResponse *loginResponse = [MXLoginResponse new];
loginResponse.homeserver = parameters[@"homeServer"];
loginResponse.userId = parameters[@"userId"];
loginResponse.accessToken = parameters[@"accessToken"];
MXLogDebug(@"[MXKAuthenticationFallbackWebView] Registered on homeserver: %@", loginResponse.homeserver);
// Sanity check
if (loginResponse.homeserver.length && loginResponse.userId.length && loginResponse.accessToken.length)
{
MXLogDebug(@"[MXKAuthenticationFallbackWebView] Call success block");
// And inform the client
onSuccess(loginResponse);
}
}
else if ([@"onLogin" isEqualToString:parameters[@"action"]])
{
// Translate the JS login event to MXLoginResponse
MXLoginResponse *loginResponse;
MXJSONModelSetMXJSONModel(loginResponse, MXLoginResponse, parameters[@"response"]);
MXLogDebug(@"[MXKAuthenticationFallbackWebView] Logged in on homeserver: %@", loginResponse.homeserver);
// Sanity check
if (loginResponse.homeserver.length && loginResponse.userId.length && loginResponse.accessToken.length)
{
MXLogDebug(@"[MXKAuthenticationFallbackWebView] Call success block");
// And inform the client
onSuccess(loginResponse);
}
}
}
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
decisionHandler(WKNavigationActionPolicyAllow);
}
@end
@@ -0,0 +1,33 @@
/*
Copyright 2016 OpenMarket Ltd
Copyright 2018 New Vector 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 <MatrixSDK/MatrixSDK.h>
#import <WebKit/WebKit.h>
@interface MXKAuthenticationRecaptchaWebView : WKWebView
/**
Open reCAPTCHA widget into a webview.
@param siteKey the site key.
@param homeServer the homeserver URL.
@param callback the block called when the user has received reCAPTCHA response.
*/
- (void)openRecaptchaWidgetWithSiteKey:(NSString*)siteKey fromHomeServer:(NSString*)homeServer callback:(void (^)(NSString *response))callback;
@end
@@ -0,0 +1,126 @@
/*
Copyright 2016 OpenMarket Ltd
Copyright 2018 New Vector 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 "MXKAuthenticationRecaptchaWebView.h"
NSString *kMXKRecaptchaHTMLString = @"<html> \
<head> \
<meta name='viewport' content='initial-scale=1.0' /> \
<script type=\"text/javascript\"> \
var verifyCallback = function(response) { \
/* Generic method to make a bridge between JS and the WKWebView*/ \
var iframe = document.createElement('iframe'); \
iframe.setAttribute('src', 'js:' + JSON.stringify({'action': 'verifyCallback', 'response': response})); \
\
document.documentElement.appendChild(iframe); \
iframe.parentNode.removeChild(iframe); \
iframe = null; \
}; \
var onloadCallback = function() { \
grecaptcha.render('recaptcha_widget', { \
'sitekey' : '%@', \
'callback': verifyCallback \
}); \
}; \
</script> \
</head> \
<body> \
<div id=\"recaptcha_widget\"></div> \
<script src=\"https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit\" async defer> \
</script> \
</body> \
</html>";
@interface MXKAuthenticationRecaptchaWebView () <WKNavigationDelegate>
{
// The block called when the reCAPTCHA response is received
void (^onResponse)(NSString *);
// Activity indicator
UIActivityIndicatorView *activityIndicator;
}
@end
@implementation MXKAuthenticationRecaptchaWebView
- (void)dealloc
{
if (activityIndicator)
{
[activityIndicator removeFromSuperview];
activityIndicator = nil;
}
}
- (void)openRecaptchaWidgetWithSiteKey:(NSString*)siteKey fromHomeServer:(NSString*)homeServer callback:(void (^)(NSString *response))callback
{
self.navigationDelegate = self;
onResponse = callback;
// Add activity indicator
activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
activityIndicator.center = self.center;
[self addSubview:activityIndicator];
[activityIndicator startAnimating];
NSString *htmlString = [NSString stringWithFormat:kMXKRecaptchaHTMLString, siteKey];
[self loadHTMLString:htmlString baseURL:[NSURL URLWithString:homeServer]];
}
#pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
if (activityIndicator)
{
[activityIndicator stopAnimating];
[activityIndicator removeFromSuperview];
activityIndicator = nil;
}
}
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
NSString *urlString = navigationAction.request.URL.absoluteString;
if ([urlString hasPrefix:@"js:"])
{
// Listen only to scheme of the JS-WKWebView bridge
NSString *jsonString = [[[urlString componentsSeparatedByString:@"js:"] lastObject] stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
NSDictionary *parameters = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers
error:&error];
if (!error)
{
if ([@"verifyCallback" isEqualToString:parameters[@"action"]])
{
// Transfer the reCAPTCHA response
onResponse(parameters[@"response"]);
}
}
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
decisionHandler(WKNavigationActionPolicyAllow);
}
@end
@@ -0,0 +1,38 @@
/*
Copyright 2018 New Vector 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.
*/
#pragma mark - Imports
@import Foundation;
@import UIKit;
#pragma mark - Types
typedef void (^MXKBarButtonItemAction)(void);
#pragma mark - Interface
/**
`MXKBarButtonItem` is a subclass of UIBarButtonItem allowing to use convenient action block instead of action selector.
*/
@interface MXKBarButtonItem : UIBarButtonItem
#pragma mark - Instance Methods
- (instancetype)initWithImage:(UIImage *)image style:(UIBarButtonItemStyle)style action:(MXKBarButtonItemAction)action;
- (instancetype)initWithTitle:(NSString *)title style:(UIBarButtonItemStyle)style action:(MXKBarButtonItemAction)action;
@end
@@ -0,0 +1,67 @@
/*
Copyright 2018 New Vector 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.
*/
#pragma mark - Imports
#import "MXKBarButtonItem.h"
#pragma mark - Private Interface
@interface MXKBarButtonItem ()
#pragma mark - Private Properties
@property (nonatomic, copy) MXKBarButtonItemAction actionBlock;
@end
#pragma mark - Implementation
@implementation MXKBarButtonItem
#pragma mark - Public methods
- (instancetype)initWithImage:(UIImage *)image style:(UIBarButtonItemStyle)style action:(MXKBarButtonItemAction)action
{
self = [self initWithImage:image style:style target:self action:@selector(executeAction:)];
if (self)
{
self.actionBlock = action;
}
return self;
}
- (instancetype)initWithTitle:(NSString *)title style:(UIBarButtonItemStyle)style action:(MXKBarButtonItemAction)action
{
self = [self initWithTitle:title style:style target:self action:@selector(executeAction:)];
if (self)
{
self.actionBlock = action;
}
return self;
}
#pragma mark - Private methods
- (void)executeAction:(id)sender
{
if (self.actionBlock)
{
self.actionBlock();
}
}
@end
@@ -0,0 +1,90 @@
/*
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 "MXKTableViewCell.h"
#import "MXKCellRendering.h"
#import "MXKImageView.h"
/**
List the accessory view types for a 'MXKContactTableCell' instance.
*/
typedef enum : NSUInteger {
/**
Don't show accessory view by default.
*/
MXKContactTableCellAccessoryCustom,
/**
The accessory view is automatically handled. It shown only for contact with matrix identifier(s).
*/
MXKContactTableCellAccessoryMatrixIcon
} MXKContactTableCellAccessoryType;
#pragma mark - MXKCellRenderingDelegate cell tap locations
/**
Action identifier used when the user tapped on contact thumbnail view.
The `userInfo` dictionary contains an `NSString` object under the `kMXKContactCellContactIdKey` key, representing the contact id of the tapped avatar.
*/
extern NSString *const kMXKContactCellTapOnThumbnailView;
/**
Notifications `userInfo` keys
*/
extern NSString *const kMXKContactCellContactIdKey;
/**
'MXKContactTableCell' is a base class for displaying a contact.
*/
@interface MXKContactTableCell : MXKTableViewCell <MXKCellRendering>
@property (strong, nonatomic) IBOutlet MXKImageView *thumbnailView;
@property (strong, nonatomic) IBOutlet UILabel *contactDisplayNameLabel;
@property (strong, nonatomic) IBOutlet UILabel *matrixDisplayNameLabel;
@property (strong, nonatomic) IBOutlet UILabel *matrixIDLabel;
@property (strong, nonatomic) IBOutlet UIView *contactAccessoryView;
@property (unsafe_unretained, nonatomic) IBOutlet NSLayoutConstraint *contactAccessoryViewHeightConstraint;
@property (unsafe_unretained, nonatomic) IBOutlet NSLayoutConstraint *contactAccessoryViewWidthConstraint;
@property (strong, nonatomic) IBOutlet UIImageView *contactAccessoryImageView;
@property (strong, nonatomic) IBOutlet UIButton *contactAccessoryButton;
/**
The default picture displayed when no picture is available.
*/
@property (nonatomic) UIImage *picturePlaceholder;
/**
The thumbnail display box type ('MXKTableViewCellDisplayBoxTypeDefault' by default)
*/
@property (nonatomic) MXKTableViewCellDisplayBoxType thumbnailDisplayBoxType;
/**
The accessory view type ('MXKContactTableCellAccessoryCustom' by default)
*/
@property (nonatomic) MXKContactTableCellAccessoryType contactAccessoryViewType;
/**
Tell whether the matrix presence of the contact is displayed or not (NO by default)
*/
@property (nonatomic) BOOL hideMatrixPresence;
@end
@@ -0,0 +1,349 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations 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 "MXKContactTableCell.h"
@import MatrixSDK.MXTools;
#import "MXKContactManager.h"
#import "MXKAppSettings.h"
#import "NSBundle+MatrixKit.h"
#pragma mark - Constant definitions
NSString *const kMXKContactCellTapOnThumbnailView = @"kMXKContactCellTapOnThumbnailView";
NSString *const kMXKContactCellContactIdKey = @"kMXKContactCellContactIdKey";
@interface MXKContactTableCell()
{
/**
The current displayed contact.
*/
MXKContact *contact;
/**
The observer of the presence for matrix user.
*/
id mxPresenceObserver;
}
@end
@implementation MXKContactTableCell
@synthesize delegate;
- (void)awakeFromNib
{
[super awakeFromNib];
self.thumbnailDisplayBoxType = MXKTableViewCellDisplayBoxTypeDefault;
// No accessory view by default
self.contactAccessoryViewType = MXKContactTableCellAccessoryCustom;
self.hideMatrixPresence = NO;
}
- (void)customizeTableViewCellRendering
{
[super customizeTableViewCellRendering];
self.thumbnailView.defaultBackgroundColor = [UIColor clearColor];
}
- (void)layoutSubviews
{
[super layoutSubviews];
if (self.thumbnailDisplayBoxType == MXKTableViewCellDisplayBoxTypeCircle)
{
// Round image view for thumbnail
self.thumbnailView.layer.cornerRadius = self.thumbnailView.frame.size.width / 2;
self.thumbnailView.clipsToBounds = YES;
}
else if (self.thumbnailDisplayBoxType == MXKTableViewCellDisplayBoxTypeRoundedCorner)
{
self.thumbnailView.layer.cornerRadius = 5;
self.thumbnailView.clipsToBounds = YES;
}
else
{
self.thumbnailView.layer.cornerRadius = 0;
self.thumbnailView.clipsToBounds = NO;
}
}
- (UIImage*)picturePlaceholder
{
return [NSBundle mxk_imageFromMXKAssetsBundleWithName:@"default-profile"];
}
- (void)setContactAccessoryViewType:(MXKContactTableCellAccessoryType)contactAccessoryViewType
{
_contactAccessoryViewType = contactAccessoryViewType;
if (contactAccessoryViewType == MXKContactTableCellAccessoryMatrixIcon)
{
// Load default matrix icon
self.contactAccessoryImageView.image = [NSBundle mxk_imageFromMXKAssetsBundleWithName:@"matrixUser"];
self.contactAccessoryImageView.hidden = NO;
self.contactAccessoryButton.hidden = YES;
// Update accessory view visibility
[self refreshMatrixIdentifiers];
}
else
{
// Hide accessory view by default
self.contactAccessoryView.hidden = YES;
self.contactAccessoryImageView.hidden = YES;
self.contactAccessoryButton.hidden = YES;
}
}
#pragma mark - MXKCellRendering
- (void)render:(MXKCellData *)cellData
{
// Sanity check: accept only object of MXKContact classes or sub-classes
NSParameterAssert([cellData isKindOfClass:[MXKContact class]]);
contact = (MXKContact*)cellData;
// remove any pending observers
[[NSNotificationCenter defaultCenter] removeObserver:self];
if (mxPresenceObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:mxPresenceObserver];
mxPresenceObserver = nil;
}
self.thumbnailView.layer.borderWidth = 0;
if (contact)
{
// Be warned when the thumbnail is updated
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onThumbnailUpdate:) name:kMXKContactThumbnailUpdateNotification object:nil];
if (! self.hideMatrixPresence)
{
// Observe contact presence change
mxPresenceObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKContactManagerMatrixUserPresenceChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
// get the matrix identifiers
NSArray* matrixIdentifiers = self->contact.matrixIdentifiers;
if (matrixIdentifiers.count > 0)
{
// Consider only the first id
NSString *matrixUserID = matrixIdentifiers.firstObject;
if ([matrixUserID isEqualToString:notif.object])
{
[self refreshPresenceUserRing:[MXTools presence:[notif.userInfo objectForKey:kMXKContactManagerMatrixPresenceKey]]];
}
}
}];
}
if (!contact.isMatrixContact)
{
// Be warned when the linked matrix IDs are updated
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onMatrixIdUpdate:) name:kMXKContactManagerDidUpdateLocalContactMatrixIDsNotification object:nil];
}
NSArray* matrixIDs = contact.matrixIdentifiers;
if (matrixIDs.count)
{
self.contactDisplayNameLabel.hidden = YES;
self.matrixDisplayNameLabel.hidden = NO;
self.matrixDisplayNameLabel.text = contact.displayName;
self.matrixIDLabel.hidden = NO;
self.matrixIDLabel.text = [matrixIDs firstObject];
}
else
{
self.contactDisplayNameLabel.hidden = NO;
self.contactDisplayNameLabel.text = contact.displayName;
self.matrixDisplayNameLabel.hidden = YES;
self.matrixIDLabel.hidden = YES;
}
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onContactThumbnailTap:)];
[tap setNumberOfTouchesRequired:1];
[tap setNumberOfTapsRequired:1];
[tap setDelegate:self];
[self.thumbnailView addGestureRecognizer:tap];
}
[self refreshContactThumbnail];
[self manageMatrixIcon];
}
+ (CGFloat)heightForCellData:(MXKCellData*)cellData withMaximumWidth:(CGFloat)maxWidth
{
// The height is fixed
return 50;
}
- (void)didEndDisplay
{
// remove any pending observers
[[NSNotificationCenter defaultCenter] removeObserver:self];
if (mxPresenceObserver) {
[[NSNotificationCenter defaultCenter] removeObserver:mxPresenceObserver];
mxPresenceObserver = nil;
}
// Remove all gesture recognizer
while (self.thumbnailView.gestureRecognizers.count)
{
[self.thumbnailView removeGestureRecognizer:self.thumbnailView.gestureRecognizers[0]];
}
self.delegate = nil;
contact = nil;
}
#pragma mark -
- (void)refreshMatrixIdentifiers
{
// Look for a potential matrix user linked with this contact
NSArray* matrixIdentifiers = contact.matrixIdentifiers;
if ((matrixIdentifiers.count > 0) && (! self.hideMatrixPresence))
{
// Consider only the first matrix identifier
NSString* matrixUserID = matrixIdentifiers.firstObject;
// Consider here all sessions reported into contact manager
NSArray* mxSessions = [MXKContactManager sharedManager].mxSessions;
for (MXSession *mxSession in mxSessions)
{
MXUser *mxUser = [mxSession userWithUserId:matrixUserID];
if (mxUser)
{
[self refreshPresenceUserRing:mxUser.presence];
break;
}
}
}
// Update accessory view visibility
if (self.contactAccessoryViewType == MXKContactTableCellAccessoryMatrixIcon)
{
self.contactAccessoryView.hidden = (!matrixIdentifiers.count);
}
}
- (void)refreshContactThumbnail
{
self.thumbnailView.image = [contact thumbnailWithPreferedSize:self.thumbnailView.frame.size];
if (!self.thumbnailView.image)
{
self.thumbnailView.image = self.picturePlaceholder;
}
}
- (void)refreshPresenceUserRing:(MXPresence)presenceStatus
{
UIColor* ringColor;
switch (presenceStatus)
{
case MXPresenceOnline:
ringColor = [[MXKAppSettings standardAppSettings] presenceColorForOnlineUser];
break;
case MXPresenceUnavailable:
ringColor = [[MXKAppSettings standardAppSettings] presenceColorForUnavailableUser];
break;
case MXPresenceOffline:
ringColor = [[MXKAppSettings standardAppSettings] presenceColorForOfflineUser];
break;
default:
ringColor = nil;
}
// if the thumbnail is defined
if (ringColor && (! self.hideMatrixPresence))
{
self.thumbnailView.layer.borderWidth = 2;
self.thumbnailView.layer.borderColor = ringColor.CGColor;
}
else
{
// remove the border
// else it draws black border
self.thumbnailView.layer.borderWidth = 0;
}
}
- (void)manageMatrixIcon
{
// try to update the thumbnail with the matrix thumbnail
if (contact.matrixIdentifiers)
{
[self refreshContactThumbnail];
}
[self refreshMatrixIdentifiers];
}
- (void)onMatrixIdUpdate:(NSNotification *)notif
{
// sanity check
if ([notif.object isKindOfClass:[NSString class]])
{
NSString* contactID = notif.object;
if ([contactID isEqualToString:contact.contactID])
{
[self manageMatrixIcon];
}
}
}
- (void)onThumbnailUpdate:(NSNotification *)notif
{
// sanity check
if ([notif.object isKindOfClass:[NSString class]])
{
NSString* contactID = notif.object;
if ([contactID isEqualToString:contact.contactID])
{
[self refreshContactThumbnail];
[self refreshMatrixIdentifiers];
}
}
}
#pragma mark - Action
- (IBAction)onContactThumbnailTap:(id)sender
{
if (self.delegate)
{
[self.delegate cell:self didRecognizeAction:kMXKContactCellTapOnThumbnailView userInfo:@{kMXKContactCellContactIdKey: contact.contactID}];
}
}
@end
@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="ChX-Tl-3Zt" customClass="MXKContactTableCell">
<rect key="frame" x="0.0" y="0.0" width="600" height="50"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="ChX-Tl-3Zt" id="SeD-4h-8Bk">
<rect key="frame" x="0.0" y="0.0" width="600" height="49"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleAspectFit" translatesAutoresizingMaskIntoConstraints="NO" id="2UL-NX-Nd1" userLabel="Contact Image View" customClass="MXKImageView">
<rect key="frame" x="8" y="5" width="40" height="40"/>
<color key="backgroundColor" red="0.89720267057418823" green="0.89720267057418823" blue="0.89720267057418823" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<gestureRecognizers/>
<constraints>
<constraint firstAttribute="height" constant="40" id="QUm-Hf-dkn"/>
<constraint firstAttribute="width" constant="40" id="z2e-H8-DcL"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Single line" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AqS-OH-Gcq">
<rect key="frame" x="60" y="10" width="510" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="zWe-gm-gkP"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Line 1" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MBc-s3-6mn">
<rect key="frame" x="60" y="8" width="510" height="22"/>
<constraints>
<constraint firstAttribute="height" constant="22" id="7wT-nd-kfp"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Line 2" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Dqg-OY-Nhb" userLabel="Line 2">
<rect key="frame" x="60" y="28" width="510" height="18"/>
<constraints>
<constraint firstAttribute="height" constant="18" id="BEu-8L-a1q"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" red="0.33333333333333331" green="0.33333333333333331" blue="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="uMV-DW-X26" userLabel="Accessory View">
<rect key="frame" x="574" y="17" width="16" height="16"/>
<subviews>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="BcH-3j-P0i" userLabel="Accessory Image View">
<rect key="frame" x="0.0" y="0.0" width="16" height="16"/>
</imageView>
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="j2S-s7-q50" userLabel="Accessory Button">
<rect key="frame" x="-7" y="-7" width="30" height="30"/>
<state key="normal">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="centerY" secondItem="j2S-s7-q50" secondAttribute="centerY" id="3Qb-sa-m7f"/>
<constraint firstAttribute="bottom" secondItem="BcH-3j-P0i" secondAttribute="bottom" id="KIl-sC-FVk"/>
<constraint firstAttribute="centerX" secondItem="j2S-s7-q50" secondAttribute="centerX" id="Wp1-CK-YKk"/>
<constraint firstAttribute="width" constant="16" id="cWZ-C3-LfR"/>
<constraint firstAttribute="trailing" secondItem="BcH-3j-P0i" secondAttribute="trailing" id="mol-ud-P3Q"/>
<constraint firstItem="BcH-3j-P0i" firstAttribute="top" secondItem="uMV-DW-X26" secondAttribute="top" id="sT8-GB-Hzx"/>
<constraint firstItem="BcH-3j-P0i" firstAttribute="leading" secondItem="uMV-DW-X26" secondAttribute="leading" id="wTq-hz-2Wl"/>
<constraint firstAttribute="height" constant="16" id="xAv-oq-PkL"/>
</constraints>
</view>
</subviews>
<constraints>
<constraint firstAttribute="centerY" secondItem="2UL-NX-Nd1" secondAttribute="centerY" id="2z5-cx-W9f"/>
<constraint firstItem="Dqg-OY-Nhb" firstAttribute="leading" secondItem="2UL-NX-Nd1" secondAttribute="trailing" constant="12" id="5I6-PQ-4Bt"/>
<constraint firstAttribute="centerY" secondItem="AqS-OH-Gcq" secondAttribute="centerY" id="9jE-l2-2tm"/>
<constraint firstAttribute="trailing" secondItem="uMV-DW-X26" secondAttribute="trailing" constant="10" id="DDZ-6z-ppJ"/>
<constraint firstAttribute="centerY" secondItem="uMV-DW-X26" secondAttribute="centerY" id="OFV-RA-kUY"/>
<constraint firstItem="2UL-NX-Nd1" firstAttribute="leading" secondItem="SeD-4h-8Bk" secondAttribute="leading" constant="8" id="Tno-FP-CJJ"/>
<constraint firstItem="MBc-s3-6mn" firstAttribute="width" secondItem="Dqg-OY-Nhb" secondAttribute="width" id="Wk2-qO-Gdj"/>
<constraint firstItem="AqS-OH-Gcq" firstAttribute="leading" secondItem="2UL-NX-Nd1" secondAttribute="trailing" constant="12" id="dSV-cR-GP0"/>
<constraint firstItem="MBc-s3-6mn" firstAttribute="leading" secondItem="2UL-NX-Nd1" secondAttribute="trailing" constant="12" id="f7x-gz-aR7"/>
<constraint firstItem="uMV-DW-X26" firstAttribute="leading" secondItem="Dqg-OY-Nhb" secondAttribute="trailing" constant="4" id="oJV-sA-GzJ"/>
<constraint firstItem="AqS-OH-Gcq" firstAttribute="width" secondItem="MBc-s3-6mn" secondAttribute="width" id="qGv-M1-qC6"/>
<constraint firstItem="MBc-s3-6mn" firstAttribute="top" secondItem="SeD-4h-8Bk" secondAttribute="topMargin" id="qf2-UP-3lW"/>
<constraint firstAttribute="bottomMargin" secondItem="Dqg-OY-Nhb" secondAttribute="bottom" constant="-5" id="tLG-uu-oPr"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="contactAccessoryButton" destination="j2S-s7-q50" id="9hP-Zr-kHK"/>
<outlet property="contactAccessoryImageView" destination="BcH-3j-P0i" id="Omi-4G-fbt"/>
<outlet property="contactAccessoryView" destination="uMV-DW-X26" id="6kX-sp-tuz"/>
<outlet property="contactAccessoryViewHeightConstraint" destination="xAv-oq-PkL" id="MGt-PN-Brr"/>
<outlet property="contactAccessoryViewWidthConstraint" destination="cWZ-C3-LfR" id="0pP-6i-gop"/>
<outlet property="contactDisplayNameLabel" destination="AqS-OH-Gcq" id="Znz-dt-zKJ"/>
<outlet property="matrixDisplayNameLabel" destination="MBc-s3-6mn" id="BWe-zO-aM3"/>
<outlet property="matrixIDLabel" destination="Dqg-OY-Nhb" id="SlZ-le-uC1"/>
<outlet property="thumbnailView" destination="2UL-NX-Nd1" id="uMq-9r-M4G"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -0,0 +1,85 @@
/*
Copyright 2016 OpenMarket Ltd
Copyright 2017 Vector Creations 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 <MatrixSDK/MatrixSDK.h>
#import "MXKView.h"
/**
MXKDeviceView class may be used to display the information of a user's device.
The displayed device may be renamed or removed.
*/
@class MXKDeviceView;
@protocol MXKDeviceViewDelegate <NSObject>
/**
Tells the delegate that an alert must be presented.
@param deviceView the device view.
@param alert the alert to present.
*/
- (void)deviceView:(MXKDeviceView*)deviceView presentAlertController:(UIAlertController*)alert;
@optional
/**
Tells the delegate to dismiss the device view.
@param deviceView the device view.
@param isUpdated tell whether the device was updated (renamed, removed...).
*/
- (void)dismissDeviceView:(MXKDeviceView*)deviceView didUpdate:(BOOL)isUpdated;
@end
@interface MXKDeviceView : MXKView <UIGestureRecognizerDelegate>
@property (weak, nonatomic) IBOutlet UIView *bgView;
@property (weak, nonatomic) IBOutlet UIView *containerView;
@property (weak, nonatomic) IBOutlet UITextView *textView;
@property (weak, nonatomic) IBOutlet UIButton *cancelButton;
@property (weak, nonatomic) IBOutlet UIButton *renameButton;
@property (weak, nonatomic) IBOutlet UIButton *deleteButton;
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
/**
Initialize a device view to display the information of a user's device.
@param device a user's device.
@param session the matrix session.
@return the newly created instance.
*/
- (instancetype)initWithDevice:(MXDevice*)device andMatrixSession:(MXSession*)session;
/**
The delegate.
*/
@property (nonatomic, weak) id<MXKDeviceViewDelegate> delegate;
/**
The default text color in the text view. [UIColor blackColor] by default.
*/
@property (nonatomic) UIColor *defaultTextColor;
/**
Action registered on 'UIControlEventTouchUpInside' event for each UIButton instance.
*/
- (IBAction)onButtonPressed:(id)sender;
@end
@@ -0,0 +1,481 @@
/*
Copyright 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector 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 "MXKDeviceView.h"
#import "NSBundle+MatrixKit.h"
#import "MXKConstants.h"
#import "MXKSwiftHeader.h"
static NSAttributedString *verticalWhitespace = nil;
@interface MXKDeviceView ()
{
/**
The displayed device
*/
MXDevice *mxDevice;
/**
The matrix session.
*/
MXSession *mxSession;
/**
The current alert
*/
UIAlertController *currentAlert;
/**
Current request in progress.
*/
MXHTTPOperation *mxCurrentOperation;
}
@end
@implementation MXKDeviceView
+ (UINib *)nib
{
// Check whether a nib file is available
NSBundle *mainBundle = [NSBundle mxk_bundleForClass:self.class];
NSString *path = [mainBundle pathForResource:NSStringFromClass([self class]) ofType:@"nib"];
if (path)
{
return [UINib nibWithNibName:NSStringFromClass([self class]) bundle:mainBundle];
}
return [UINib nibWithNibName:NSStringFromClass([MXKDeviceView class]) bundle:[NSBundle mxk_bundleForClass:[MXKDeviceView class]]];
}
- (void)awakeFromNib
{
[super awakeFromNib];
// Add tap recognizer to discard the view on bg view tap
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onBgViewTap:)];
[tap setNumberOfTouchesRequired:1];
[tap setNumberOfTapsRequired:1];
[tap setDelegate:self];
[self.bgView addGestureRecognizer:tap];
// Localize string
[_cancelButton setTitle:[MatrixKitL10n ok] forState:UIControlStateNormal];
[_cancelButton setTitle:[MatrixKitL10n ok] forState:UIControlStateHighlighted];
[_renameButton setTitle:[MatrixKitL10n rename] forState:UIControlStateNormal];
[_renameButton setTitle:[MatrixKitL10n rename] forState:UIControlStateHighlighted];
[_deleteButton setTitle:[MatrixKitL10n delete] forState:UIControlStateNormal];
[_deleteButton setTitle:[MatrixKitL10n delete] forState:UIControlStateHighlighted];
}
- (void)layoutSubviews
{
[super layoutSubviews];
// Scroll to the top the text view content
self.textView.contentOffset = CGPointZero;
}
#pragma mark - Override MXKView
-(void)customizeViewRendering
{
[super customizeViewRendering];
_defaultTextColor = [UIColor blackColor];
// Add shadow on added view
_containerView.layer.cornerRadius = 5;
_containerView.layer.shadowOffset = CGSizeMake(0, 1);
_containerView.layer.shadowOpacity = 0.5f;
}
#pragma mark -
- (void)removeFromSuperviewDidUpdate:(BOOL)isUpdated
{
if (currentAlert)
{
[currentAlert dismissViewControllerAnimated:NO completion:nil];
currentAlert = nil;
}
if (mxCurrentOperation)
{
[mxCurrentOperation cancel];
mxCurrentOperation = nil;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(dismissDeviceView:didUpdate:)])
{
[self.delegate dismissDeviceView:self didUpdate:isUpdated];
}
else
{
[self removeFromSuperview];
}
}
- (instancetype)initWithDevice:(MXDevice*)device andMatrixSession:(MXSession*)session
{
self = [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject;
if (self)
{
mxDevice = device;
mxSession = session;
[self setTranslatesAutoresizingMaskIntoConstraints: NO];
if (mxDevice)
{
// Device information
NSMutableAttributedString *deviceInformationString = [[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n deviceDetailsTitle]
attributes:@{NSForegroundColorAttributeName : _defaultTextColor,
NSFontAttributeName: [UIFont boldSystemFontOfSize:15]}];
[deviceInformationString appendAttributedString:[MXKDeviceView verticalWhitespace]];
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n deviceDetailsName]
attributes:@{NSForegroundColorAttributeName : _defaultTextColor,
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:device.displayName.length ? device.displayName : @""
attributes:@{NSForegroundColorAttributeName : _defaultTextColor,
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
[deviceInformationString appendAttributedString:[MXKDeviceView verticalWhitespace]];
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n deviceDetailsIdentifier]
attributes:@{NSForegroundColorAttributeName : _defaultTextColor,
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:device.deviceId
attributes:@{NSForegroundColorAttributeName : _defaultTextColor,
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
[deviceInformationString appendAttributedString:[MXKDeviceView verticalWhitespace]];
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n deviceDetailsLastSeen]
attributes:@{NSForegroundColorAttributeName : _defaultTextColor,
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
NSDate *lastSeenDate = [NSDate dateWithTimeIntervalSince1970:device.lastSeenTs/1000];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:[[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0]]];
[dateFormatter setDateStyle:NSDateFormatterShortStyle];
[dateFormatter setTimeStyle:NSDateFormatterShortStyle];
[dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
NSString *lastSeen = [MatrixKitL10n deviceDetailsLastSeenFormat:device.lastSeenIp :[dateFormatter stringFromDate:lastSeenDate]];
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:lastSeen
attributes:@{NSForegroundColorAttributeName : _defaultTextColor,
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
[deviceInformationString appendAttributedString:[MXKDeviceView verticalWhitespace]];
self.textView.attributedText = deviceInformationString;
}
else
{
_textView.text = nil;
}
// Hide potential activity indicator
[_activityIndicator stopAnimating];
}
return self;
}
- (void)dealloc
{
mxDevice = nil;
mxSession = nil;
}
+ (NSAttributedString *)verticalWhitespace
{
if (verticalWhitespace == nil)
{
verticalWhitespace = [[NSAttributedString alloc] initWithString:@"\n\n" attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:4]}];
}
return verticalWhitespace;
}
#pragma mark - Actions
- (IBAction)onBgViewTap:(UITapGestureRecognizer*)sender
{
[self removeFromSuperviewDidUpdate:NO];
}
- (IBAction)onButtonPressed:(id)sender
{
if (sender == _cancelButton)
{
[self removeFromSuperviewDidUpdate:NO];
}
else if (sender == _renameButton)
{
[self renameDevice];
}
else if (sender == _deleteButton)
{
[self deleteDevice];
}
}
#pragma mark -
- (void)renameDevice
{
if (!self.delegate)
{
// Ignore
MXLogDebug(@"[MXKDeviceView] Rename device failed, delegate is missing");
return;
}
// Prompt the user to enter a device name.
[currentAlert dismissViewControllerAnimated:NO completion:nil];
__weak typeof(self) weakSelf = self;
currentAlert = [UIAlertController alertControllerWithTitle:[MatrixKitL10n deviceDetailsRenamePromptTitle]
message:[MatrixKitL10n deviceDetailsRenamePromptMessage] preferredStyle:UIAlertControllerStyleAlert];
[currentAlert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.secureTextEntry = NO;
textField.placeholder = nil;
textField.keyboardType = UIKeyboardTypeDefault;
if (weakSelf)
{
typeof(self) self = weakSelf;
textField.text = self->mxDevice.displayName;
}
}];
[currentAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n cancel]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
}
}]];
[currentAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n ok]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
NSString *text = [self->currentAlert textFields].firstObject.text;
self->currentAlert = nil;
[self.activityIndicator startAnimating];
self->mxCurrentOperation = [self->mxSession.matrixRestClient setDeviceName:text forDeviceId:self->mxDevice.deviceId success:^{
if (weakSelf)
{
typeof(self) self = weakSelf;
self->mxCurrentOperation = nil;
[self.activityIndicator stopAnimating];
[self removeFromSuperviewDidUpdate:YES];
}
} failure:^(NSError *error) {
if (weakSelf)
{
typeof(self) self = weakSelf;
// Notify MatrixKit user
NSString *myUserId = self->mxSession.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
self->mxCurrentOperation = nil;
MXLogDebug(@"[MXKDeviceView] Rename device (%@) failed", self->mxDevice.deviceId);
[self.activityIndicator stopAnimating];
[self removeFromSuperviewDidUpdate:NO];
}
}];
}
}]];
[self.delegate deviceView:self presentAlertController:currentAlert];
}
- (void)deleteDevice
{
if (!self.delegate)
{
// Ignore
MXLogDebug(@"[MXKDeviceView] Delete device failed, delegate is missing");
return;
}
// Get an authentication session to prepare device deletion
[self.activityIndicator startAnimating];
mxCurrentOperation = [mxSession.matrixRestClient getSessionToDeleteDeviceByDeviceId:mxDevice.deviceId success:^(MXAuthenticationSession *authSession) {
self->mxCurrentOperation = nil;
// Check whether the password based type is supported
BOOL isPasswordBasedTypeSupported = NO;
for (MXLoginFlow *loginFlow in authSession.flows)
{
if ([loginFlow.type isEqualToString:kMXLoginFlowTypePassword] || [loginFlow.stages indexOfObject:kMXLoginFlowTypePassword] != NSNotFound)
{
isPasswordBasedTypeSupported = YES;
break;
}
}
if (isPasswordBasedTypeSupported && authSession.session)
{
// Prompt for a password
[self->currentAlert dismissViewControllerAnimated:NO completion:nil];
__weak typeof(self) weakSelf = self;
// Prompt the user before deleting the device.
self->currentAlert = [UIAlertController alertControllerWithTitle:[MatrixKitL10n deviceDetailsDeletePromptTitle] message:[MatrixKitL10n deviceDetailsDeletePromptMessage] preferredStyle:UIAlertControllerStyleAlert];
[self->currentAlert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.secureTextEntry = YES;
textField.placeholder = nil;
textField.keyboardType = UIKeyboardTypeDefault;
}];
[self->currentAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n cancel]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
[self.activityIndicator stopAnimating];
}
}]];
[self->currentAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n submit]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
UITextField *textField = [self->currentAlert textFields].firstObject;
self->currentAlert = nil;
NSString *userId = self->mxSession.myUser.userId;
NSDictionary *authParams;
// Sanity check
if (userId)
{
authParams = @{@"session":authSession.session,
@"user": userId,
@"password": textField.text,
@"type": kMXLoginFlowTypePassword};
}
self->mxCurrentOperation = [self->mxSession.matrixRestClient deleteDeviceByDeviceId:self->mxDevice.deviceId authParams:authParams success:^{
if (weakSelf)
{
typeof(self) self = weakSelf;
self->mxCurrentOperation = nil;
[self.activityIndicator stopAnimating];
[self removeFromSuperviewDidUpdate:YES];
}
} failure:^(NSError *error) {
if (weakSelf)
{
typeof(self) self = weakSelf;
// Notify MatrixKit user
NSString *myUserId = self->mxSession.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
self->mxCurrentOperation = nil;
MXLogDebug(@"[MXKDeviceView] Delete device (%@) failed", self->mxDevice.deviceId);
[self.activityIndicator stopAnimating];
[self removeFromSuperviewDidUpdate:NO];
}
}];
}
}]];
[self.delegate deviceView:self presentAlertController:self->currentAlert];
}
else
{
MXLogDebug(@"[MXKDeviceView] Delete device (%@) failed, auth session flow type is not supported", self->mxDevice.deviceId);
[self.activityIndicator stopAnimating];
}
} failure:^(NSError *error) {
self->mxCurrentOperation = nil;
MXLogDebug(@"[MXKDeviceView] Delete device (%@) failed, unable to get auth session", self->mxDevice.deviceId);
[self.activityIndicator stopAnimating];
// Notify MatrixKit user
NSString *myUserId = self->mxSession.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
}];
}
@end
@@ -0,0 +1,108 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="8VI-1E-fge" customClass="MXKDeviceView">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view alpha="0.5" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="D7c-Zw-n8I">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<color key="backgroundColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="c3W-Ma-n01">
<rect key="frame" x="10" y="74" width="580" height="250"/>
<subviews>
<textView autoresizesSubviews="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" bounces="NO" editable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3Vk-Jx-L6Y">
<rect key="frame" x="10" y="10" width="560" height="190"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ump-1C-SzI">
<rect key="frame" x="57.5" y="210" width="30" height="30"/>
<state key="normal" title="OK">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="onButtonPressed:" destination="8VI-1E-fge" eventType="touchUpInside" id="t42-2S-MsY"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="o3Z-D3-2S3">
<rect key="frame" x="335" y="210" width="56" height="30"/>
<state key="normal" title="Rename">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="onButtonPressed:" destination="8VI-1E-fge" eventType="touchUpInside" id="hbe-VP-vE5"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="qkC-t0-Qd0">
<rect key="frame" x="485" y="210" width="45" height="30"/>
<state key="normal" title="Delete">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="onButtonPressed:" destination="8VI-1E-fge" eventType="touchUpInside" id="ljK-ul-fY2"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="3Vk-Jx-L6Y" firstAttribute="leading" secondItem="c3W-Ma-n01" secondAttribute="leading" constant="10" id="ECh-zm-rMb"/>
<constraint firstAttribute="trailing" secondItem="3Vk-Jx-L6Y" secondAttribute="trailing" constant="10" id="J2X-t1-Wuo"/>
<constraint firstItem="qkC-t0-Qd0" firstAttribute="top" secondItem="3Vk-Jx-L6Y" secondAttribute="bottom" constant="10" id="QLy-iD-ead"/>
<constraint firstAttribute="bottom" secondItem="o3Z-D3-2S3" secondAttribute="bottom" constant="10" id="RLg-NE-JtP"/>
<constraint firstAttribute="centerX" secondItem="Ump-1C-SzI" secondAttribute="centerX" multiplier="4" id="anR-Ea-ml6"/>
<constraint firstAttribute="bottom" secondItem="qkC-t0-Qd0" secondAttribute="bottom" constant="10" id="eJw-AZ-Okc"/>
<constraint firstItem="3Vk-Jx-L6Y" firstAttribute="top" secondItem="c3W-Ma-n01" secondAttribute="top" constant="10" id="ej1-zq-aDf"/>
<constraint firstAttribute="centerX" secondItem="o3Z-D3-2S3" secondAttribute="centerX" multiplier="4/5" id="h0U-I5-5dA"/>
<constraint firstAttribute="bottom" secondItem="Ump-1C-SzI" secondAttribute="bottom" constant="10" id="kLj-CN-wSG"/>
<constraint firstAttribute="height" priority="750" constant="250" id="rtj-ON-bkY"/>
<constraint firstAttribute="centerX" secondItem="qkC-t0-Qd0" secondAttribute="centerX" multiplier="4/7" id="s2R-r3-jqz"/>
</constraints>
</view>
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="4za-i5-u2D">
<rect key="frame" x="290" y="290" width="20" height="20"/>
</activityIndicatorView>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="D7c-Zw-n8I" firstAttribute="top" secondItem="8VI-1E-fge" secondAttribute="top" id="9Md-NO-OM3"/>
<constraint firstAttribute="trailing" secondItem="c3W-Ma-n01" secondAttribute="trailing" constant="10" id="F6j-Ia-5EQ"/>
<constraint firstItem="D7c-Zw-n8I" firstAttribute="leading" secondItem="8VI-1E-fge" secondAttribute="leading" id="Gzh-sa-sB4"/>
<constraint firstItem="c3W-Ma-n01" firstAttribute="height" relation="lessThanOrEqual" secondItem="8VI-1E-fge" secondAttribute="height" constant="-20" id="OE8-Ll-IX0"/>
<constraint firstItem="4za-i5-u2D" firstAttribute="centerX" secondItem="8VI-1E-fge" secondAttribute="centerX" id="SGT-wg-tcC"/>
<constraint firstItem="c3W-Ma-n01" firstAttribute="top" secondItem="8VI-1E-fge" secondAttribute="top" constant="74" id="a6K-LL-lzT"/>
<constraint firstItem="4za-i5-u2D" firstAttribute="centerY" secondItem="8VI-1E-fge" secondAttribute="centerY" id="gjb-ga-edx"/>
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="c3W-Ma-n01" secondAttribute="bottom" constant="10" id="oD7-T4-DuW"/>
<constraint firstAttribute="bottom" secondItem="D7c-Zw-n8I" secondAttribute="bottom" id="qda-fo-h6c"/>
<constraint firstItem="c3W-Ma-n01" firstAttribute="leading" secondItem="8VI-1E-fge" secondAttribute="leading" constant="10" id="ryV-yq-aqs"/>
<constraint firstAttribute="trailing" secondItem="D7c-Zw-n8I" secondAttribute="trailing" id="wCd-aV-jjY"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<nil key="simulatedTopBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
<outlet property="activityIndicator" destination="4za-i5-u2D" id="IH9-BZ-e26"/>
<outlet property="bgView" destination="D7c-Zw-n8I" id="iQs-LG-SpD"/>
<outlet property="cancelButton" destination="Ump-1C-SzI" id="U8M-nT-JiK"/>
<outlet property="containerView" destination="c3W-Ma-n01" id="Stk-lk-Wew"/>
<outlet property="deleteButton" destination="qkC-t0-Qd0" id="bRu-al-rte"/>
<outlet property="renameButton" destination="o3Z-D3-2S3" id="4tr-TC-EcJ"/>
<outlet property="textView" destination="3Vk-Jx-L6Y" id="uOw-Bq-neN"/>
</connections>
</view>
</objects>
</document>
@@ -0,0 +1,103 @@
/*
Copyright 2016 OpenMarket Ltd
Copyright 2017 Vector Creations 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 <MatrixSDK/MatrixSDK.h>
#import "MXKView.h"
@protocol MXKEncryptionInfoViewDelegate;
/**
MXKEncryptionInfoView class may be used to display the available information on a encrypted event.
The event sender device may be verified, unverified, blocked or unblocked from this view.
*/
@interface MXKEncryptionInfoView : MXKView
/**
The displayed event
*/
@property (nonatomic, readonly) MXEvent *mxEvent;
/**
The matrix session.
*/
@property (nonatomic, readonly) MXSession *mxSession;
/**
The event device info
*/
@property (nonatomic, readonly) MXDeviceInfo *mxDeviceInfo;
@property (weak, nonatomic) IBOutlet UITextView *textView;
@property (weak, nonatomic) IBOutlet UIButton *cancelButton;
@property (weak, nonatomic) IBOutlet UIButton *verifyButton;
@property (weak, nonatomic) IBOutlet UIButton *blockButton;
@property (weak, nonatomic) IBOutlet UIButton *confirmVerifyButton;
@property (nonatomic, weak) id<MXKEncryptionInfoViewDelegate> delegate;
/**
Initialise an `MXKEncryptionInfoView` instance based on an encrypted event
@param event the encrypted event.
@param session the related matrix session.
@return the newly created instance.
*/
- (instancetype)initWithEvent:(MXEvent*)event andMatrixSession:(MXSession*)session;
/**
Initialise an `MXKEncryptionInfoView` instance based only on a device information.
@param deviceInfo the device information.
@param session the related matrix session.
@return the newly created instance.
*/
- (instancetype)initWithDeviceInfo:(MXDeviceInfo*)deviceInfo andMatrixSession:(MXSession*)session;
/**
The default text color in the text view. [UIColor blackColor] by default.
*/
@property (nonatomic) UIColor *defaultTextColor;
/**
Action registered on 'UIControlEventTouchUpInside' event for each UIButton instance.
*/
- (IBAction)onButtonPressed:(id)sender;
@end
@protocol MXKEncryptionInfoViewDelegate <NSObject>
/**
Called when the user changes the verified state of a device.
@param encryptionInfoView the view.
@param deviceInfo the device that has changed.
*/
- (void)encryptionInfoView:(MXKEncryptionInfoView*)encryptionInfoView didDeviceInfoVerifiedChange:(MXDeviceInfo*)deviceInfo;
@optional
/**
Called when the user close the view without changing value.
@param encryptionInfoView the view.
*/
- (void)encryptionInfoViewDidClose:(MXKEncryptionInfoView*)encryptionInfoView;
@end
@@ -0,0 +1,492 @@
/*
Copyright 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector 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 "MXKEncryptionInfoView.h"
#import "NSBundle+MatrixKit.h"
#import "MXKConstants.h"
#import "MXKSwiftHeader.h"
static NSAttributedString *verticalWhitespace = nil;
@interface MXKEncryptionInfoView ()
{
/**
Current request in progress.
*/
MXHTTPOperation *mxCurrentOperation;
}
@end
@implementation MXKEncryptionInfoView
+ (UINib *)nib
{
// Check whether a nib file is available
NSBundle *mainBundle = [NSBundle mxk_bundleForClass:self.class];
NSString *path = [mainBundle pathForResource:NSStringFromClass([self class]) ofType:@"nib"];
if (path)
{
return [UINib nibWithNibName:NSStringFromClass([self class]) bundle:mainBundle];
}
return [UINib nibWithNibName:NSStringFromClass([MXKEncryptionInfoView class]) bundle:[NSBundle mxk_bundleForClass:[MXKEncryptionInfoView class]]];
}
- (void)awakeFromNib
{
[super awakeFromNib];
// Localize string
[_cancelButton setTitle:[MatrixKitL10n ok] forState:UIControlStateNormal];
[_cancelButton setTitle:[MatrixKitL10n ok] forState:UIControlStateHighlighted];
[_confirmVerifyButton setTitle:[MatrixKitL10n roomEventEncryptionVerifyOk] forState:UIControlStateNormal];
[_confirmVerifyButton setTitle:[MatrixKitL10n roomEventEncryptionVerifyOk] forState:UIControlStateHighlighted];
}
- (void)layoutSubviews
{
[super layoutSubviews];
// Scroll to the top the text view content
self.textView.contentOffset = CGPointZero;
}
- (void)removeFromSuperview
{
if (mxCurrentOperation)
{
[mxCurrentOperation cancel];
mxCurrentOperation = nil;
}
[super removeFromSuperview];
}
#pragma mark - Override MXKView
-(void)customizeViewRendering
{
[super customizeViewRendering];
_defaultTextColor = [UIColor blackColor];
}
#pragma mark -
- (instancetype)initWithEvent:(MXEvent*)event andMatrixSession:(MXSession*)session
{
self = [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject;
if (self)
{
_mxEvent = event;
_mxSession = session;
_mxDeviceInfo = nil;
[self setTranslatesAutoresizingMaskIntoConstraints: NO];
[self updateTextViewText];
}
return self;
}
- (instancetype)initWithDeviceInfo:(MXDeviceInfo*)deviceInfo andMatrixSession:(MXSession*)session
{
self = [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject;
if (self)
{
_mxEvent = nil;
_mxDeviceInfo = deviceInfo;
_mxSession = session;
[self setTranslatesAutoresizingMaskIntoConstraints: NO];
[self updateTextViewText];
}
return self;
}
- (void)dealloc
{
_mxEvent = nil;
_mxSession = nil;
_mxDeviceInfo = nil;
}
#pragma mark -
- (void)updateTextViewText
{
// Prepare the text view content
NSMutableAttributedString *textViewAttributedString = [[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n roomEventEncryptionInfoTitle]
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
NSFontAttributeName: [UIFont boldSystemFontOfSize:17]}];
if (_mxEvent)
{
NSString *senderId = _mxEvent.sender;
if (_mxSession && _mxSession.crypto && !_mxDeviceInfo)
{
_mxDeviceInfo = [_mxSession.crypto eventDeviceInfo:_mxEvent];
if (!_mxDeviceInfo)
{
// Trigger a server request to get the device information for the event sender
mxCurrentOperation = [_mxSession.crypto downloadKeys:@[senderId] forceDownload:NO success:^(MXUsersDevicesMap<MXDeviceInfo *> *usersDevicesInfoMap, NSDictionary<NSString *,MXCrossSigningInfo *> *crossSigningKeysMap) {
self->mxCurrentOperation = nil;
// Sanity check: check whether some device information has been retrieved.
self->_mxDeviceInfo = [self.mxSession.crypto eventDeviceInfo:self.mxEvent];
if (self.mxDeviceInfo)
{
[self updateTextViewText];
}
} failure:^(NSError *error) {
self->mxCurrentOperation = nil;
MXLogDebug(@"[MXKEncryptionInfoView] Crypto failed to download device info for user: %@", self.mxEvent.sender);
// Notify MatrixKit user
NSString *myUserId = self.mxSession.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
}];
}
}
// Event information
NSMutableAttributedString *eventInformationString = [[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n roomEventEncryptionInfoEvent]
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
NSFontAttributeName: [UIFont boldSystemFontOfSize:15]}];
[eventInformationString appendAttributedString:[MXKEncryptionInfoView verticalWhitespace]];
NSString *senderKey = _mxEvent.senderKey;
NSString *claimedKey = _mxEvent.keysClaimed[@"ed25519"];
NSString *algorithm = _mxEvent.wireContent[@"algorithm"];
NSString *sessionId = _mxEvent.wireContent[@"session_id"];
NSString *decryptionError;
if (_mxEvent.decryptionError)
{
decryptionError = [NSString stringWithFormat:@"** %@ **", _mxEvent.decryptionError.localizedDescription];
}
if (!senderKey.length)
{
senderKey = [MatrixKitL10n roomEventEncryptionInfoEventNone];
}
if (!claimedKey.length)
{
claimedKey = [MatrixKitL10n roomEventEncryptionInfoEventNone];
}
if (!algorithm.length)
{
algorithm = [MatrixKitL10n roomEventEncryptionInfoEventUnencrypted];
}
if (!sessionId.length)
{
sessionId = [MatrixKitL10n roomEventEncryptionInfoEventNone];
}
[eventInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n roomEventEncryptionInfoEventUserId] attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
[eventInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:senderId
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
[eventInformationString appendAttributedString:[MXKEncryptionInfoView verticalWhitespace]];
[eventInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n roomEventEncryptionInfoEventIdentityKey]
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
[eventInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:senderKey
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
[eventInformationString appendAttributedString:[MXKEncryptionInfoView verticalWhitespace]];
[eventInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n roomEventEncryptionInfoEventFingerprintKey]
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
[eventInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:claimedKey
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
[eventInformationString appendAttributedString:[MXKEncryptionInfoView verticalWhitespace]];
[eventInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n roomEventEncryptionInfoEventAlgorithm]
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
[eventInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:algorithm
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
[eventInformationString appendAttributedString:[MXKEncryptionInfoView verticalWhitespace]];
if (decryptionError.length)
{
[eventInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n roomEventEncryptionInfoEventDecryptionError]
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
[eventInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:decryptionError
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
[eventInformationString appendAttributedString:[MXKEncryptionInfoView verticalWhitespace]];
}
[eventInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n roomEventEncryptionInfoEventSessionId]
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
[eventInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:sessionId
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
[eventInformationString appendAttributedString:[MXKEncryptionInfoView verticalWhitespace]];
[textViewAttributedString appendAttributedString:eventInformationString];
}
// Device information
NSMutableAttributedString *deviceInformationString = [[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n roomEventEncryptionInfoDevice]
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
NSFontAttributeName: [UIFont boldSystemFontOfSize:15]}];
[deviceInformationString appendAttributedString:[MXKEncryptionInfoView verticalWhitespace]];
if (_mxDeviceInfo)
{
NSString *name = _mxDeviceInfo.displayName;
NSString *deviceId = _mxDeviceInfo.deviceId;
NSMutableAttributedString *verification;
NSString *fingerprint = _mxDeviceInfo.fingerprint;
// Display here the Verify and Block buttons except if the device is the current one.
_verifyButton.hidden = _blockButton.hidden = [_mxDeviceInfo.deviceId isEqualToString:_mxSession.matrixRestClient.credentials.deviceId];
switch (_mxDeviceInfo.trustLevel.localVerificationStatus)
{
case MXDeviceUnknown:
case MXDeviceUnverified:
{
verification = [[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n roomEventEncryptionInfoDeviceNotVerified]
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}];
[_verifyButton setTitle:[MatrixKitL10n roomEventEncryptionInfoVerify] forState:UIControlStateNormal];
[_verifyButton setTitle:[MatrixKitL10n roomEventEncryptionInfoVerify] forState:UIControlStateHighlighted];
[_blockButton setTitle:[MatrixKitL10n roomEventEncryptionInfoBlock] forState:UIControlStateNormal];
[_blockButton setTitle:[MatrixKitL10n roomEventEncryptionInfoBlock] forState:UIControlStateHighlighted];
break;
}
case MXDeviceVerified:
{
verification = [[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n roomEventEncryptionInfoDeviceVerified]
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
NSFontAttributeName: [UIFont systemFontOfSize:14]}];
[_verifyButton setTitle:[MatrixKitL10n roomEventEncryptionInfoUnverify] forState:UIControlStateNormal];
[_verifyButton setTitle:[MatrixKitL10n roomEventEncryptionInfoUnverify] forState:UIControlStateHighlighted];
[_blockButton setTitle:[MatrixKitL10n roomEventEncryptionInfoBlock] forState:UIControlStateNormal];
[_blockButton setTitle:[MatrixKitL10n roomEventEncryptionInfoBlock] forState:UIControlStateHighlighted];
break;
}
case MXDeviceBlocked:
{
verification = [[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n roomEventEncryptionInfoDeviceBlocked]
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}];
[_verifyButton setTitle:[MatrixKitL10n roomEventEncryptionInfoVerify] forState:UIControlStateNormal];
[_verifyButton setTitle:[MatrixKitL10n roomEventEncryptionInfoVerify] forState:UIControlStateHighlighted];
[_blockButton setTitle:[MatrixKitL10n roomEventEncryptionInfoUnblock] forState:UIControlStateNormal];
[_blockButton setTitle:[MatrixKitL10n roomEventEncryptionInfoUnblock] forState:UIControlStateHighlighted];
break;
}
default:
break;
}
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n roomEventEncryptionInfoDeviceName]
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:(name.length ? name : @"")
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
[deviceInformationString appendAttributedString:[MXKEncryptionInfoView verticalWhitespace]];
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n roomEventEncryptionInfoDeviceId] attributes:@{NSForegroundColorAttributeName: _defaultTextColor, NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:deviceId
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
[deviceInformationString appendAttributedString:[MXKEncryptionInfoView verticalWhitespace]];
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n roomEventEncryptionInfoDeviceVerification] attributes:@{NSForegroundColorAttributeName: _defaultTextColor, NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
[deviceInformationString appendAttributedString:verification];
[deviceInformationString appendAttributedString:[MXKEncryptionInfoView verticalWhitespace]];
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n roomEventEncryptionInfoDeviceFingerprint] attributes:@{NSForegroundColorAttributeName: _defaultTextColor, NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:fingerprint
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
[deviceInformationString appendAttributedString:[MXKEncryptionInfoView verticalWhitespace]];
}
else
{
// Unknown device
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n roomEventEncryptionInfoDeviceUnknown] attributes:@{NSForegroundColorAttributeName: _defaultTextColor, NSFontAttributeName: [UIFont italicSystemFontOfSize:14]}]];
}
[textViewAttributedString appendAttributedString:deviceInformationString];
self.textView.attributedText = textViewAttributedString;
}
+ (NSAttributedString *)verticalWhitespace
{
if (verticalWhitespace == nil)
{
verticalWhitespace = [[NSAttributedString alloc] initWithString:@"\n\n" attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:4]}];
}
return verticalWhitespace;
}
#pragma mark - Actions
- (IBAction)onButtonPressed:(id)sender
{
if (sender == _cancelButton)
{
[self removeFromSuperview];
if ([_delegate respondsToSelector:@selector(encryptionInfoViewDidClose:)])
{
[_delegate encryptionInfoViewDidClose:self];
}
}
// Note: Verify and Block buttons are hidden when the deviceInfo is not available
else if (sender == _confirmVerifyButton && _mxDeviceInfo)
{
[_mxSession.crypto setDeviceVerification:MXDeviceVerified forDevice:_mxDeviceInfo.deviceId ofUser:_mxDeviceInfo.userId success:^{
// Refresh data
_mxDeviceInfo = [self.mxSession.crypto eventDeviceInfo:self.mxEvent];
if (self->_delegate)
{
[self->_delegate encryptionInfoView:self didDeviceInfoVerifiedChange:self.mxDeviceInfo];
}
[self removeFromSuperview];
} failure:^(NSError *error) {
[self removeFromSuperview];
}];
}
else if (_mxDeviceInfo)
{
MXDeviceVerification verificationStatus;
if (sender == _verifyButton)
{
verificationStatus = ((_mxDeviceInfo.trustLevel.localVerificationStatus == MXDeviceVerified) ? MXDeviceUnverified : MXDeviceVerified);
}
else if (sender == _blockButton)
{
verificationStatus = ((_mxDeviceInfo.trustLevel.localVerificationStatus == MXDeviceBlocked) ? MXDeviceUnverified : MXDeviceBlocked);
}
else
{
// Unexpected case
MXLogDebug(@"[MXKEncryptionInfoView] Invalid button pressed.");
return;
}
if (verificationStatus == MXDeviceVerified)
{
// Prompt user
NSMutableAttributedString *textViewAttributedString = [[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n roomEventEncryptionVerifyTitle] attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
NSFontAttributeName: [UIFont boldSystemFontOfSize:17]}];
NSString *message = [MatrixKitL10n roomEventEncryptionVerifyMessage:_mxDeviceInfo.displayName :_mxDeviceInfo.deviceId :_mxDeviceInfo.fingerprint];
[textViewAttributedString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:message
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
self.textView.attributedText = textViewAttributedString;
[_cancelButton setTitle:[MatrixKitL10n cancel] forState:UIControlStateNormal];
[_cancelButton setTitle:[MatrixKitL10n cancel] forState:UIControlStateHighlighted];
_verifyButton.hidden = _blockButton.hidden = YES;
_confirmVerifyButton.hidden = NO;
}
else
{
[_mxSession.crypto setDeviceVerification:verificationStatus forDevice:_mxDeviceInfo.deviceId ofUser:_mxDeviceInfo.userId success:^{
// Refresh data
_mxDeviceInfo = [self.mxSession.crypto eventDeviceInfo:self.mxEvent];
if (self->_delegate)
{
[self->_delegate encryptionInfoView:self didDeviceInfoVerifiedChange:self.mxDeviceInfo];
}
[self removeFromSuperview];
} failure:^(NSError *error) {
[self removeFromSuperview];
}];
}
}
}
@end
@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="8VI-1E-fge" customClass="MXKEncryptionInfoView">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<textView autoresizesSubviews="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" bounces="NO" editable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3Vk-Jx-L6Y">
<rect key="frame" x="10" y="10" width="580" height="540"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ump-1C-SzI">
<rect key="frame" x="60" y="560" width="30" height="30"/>
<state key="normal" title="OK">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="onButtonPressed:" destination="8VI-1E-fge" eventType="touchUpInside" id="t42-2S-MsY"/>
</connections>
</button>
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="qkC-t0-Qd0">
<rect key="frame" x="355" y="560" width="40" height="30"/>
<state key="normal" title="Verify">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="onButtonPressed:" destination="8VI-1E-fge" eventType="touchUpInside" id="ljK-ul-fY2"/>
</connections>
</button>
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IdO-jn-iTV">
<rect key="frame" x="507" y="560" width="38" height="30"/>
<state key="normal" title="Block">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="onButtonPressed:" destination="8VI-1E-fge" eventType="touchUpInside" id="j21-e2-e3C"/>
</connections>
</button>
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="L4h-aG-8UN">
<rect key="frame" x="506" y="560" width="40" height="30"/>
<state key="normal" title="Verify">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="onButtonPressed:" destination="8VI-1E-fge" eventType="touchUpInside" id="nKr-w7-ECp"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="L4h-aG-8UN" firstAttribute="top" secondItem="3Vk-Jx-L6Y" secondAttribute="bottom" constant="10" id="0tX-be-JRZ"/>
<constraint firstAttribute="centerX" secondItem="qkC-t0-Qd0" secondAttribute="centerX" multiplier="0.8" id="7iU-9D-miY"/>
<constraint firstAttribute="bottom" secondItem="qkC-t0-Qd0" secondAttribute="bottom" constant="10" id="DyR-rX-nt0"/>
<constraint firstAttribute="bottom" secondItem="Ump-1C-SzI" secondAttribute="bottom" constant="10" id="Nfo-nh-gvW"/>
<constraint firstItem="3Vk-Jx-L6Y" firstAttribute="top" secondItem="8VI-1E-fge" secondAttribute="top" constant="10" id="Oi3-Zq-HKf"/>
<constraint firstAttribute="bottom" secondItem="L4h-aG-8UN" secondAttribute="bottom" constant="10" id="Owt-zM-W2X"/>
<constraint firstItem="3Vk-Jx-L6Y" firstAttribute="leading" secondItem="8VI-1E-fge" secondAttribute="leading" constant="10" id="Tqb-eF-AIh"/>
<constraint firstAttribute="centerX" secondItem="Ump-1C-SzI" secondAttribute="centerX" multiplier="4" id="YMC-ob-tqT"/>
<constraint firstAttribute="bottom" secondItem="IdO-jn-iTV" secondAttribute="bottom" constant="10" id="YcV-iN-9WU"/>
<constraint firstAttribute="trailing" secondItem="3Vk-Jx-L6Y" secondAttribute="trailing" constant="10" id="ZbI-rp-TR9"/>
<constraint firstAttribute="centerX" secondItem="L4h-aG-8UN" secondAttribute="centerX" multiplier="0.57" id="g1v-Mg-fQ6"/>
<constraint firstAttribute="centerX" secondItem="IdO-jn-iTV" secondAttribute="centerX" multiplier="0.57" id="gAm-ph-zPW"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<nil key="simulatedTopBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
<outlet property="blockButton" destination="IdO-jn-iTV" id="HI0-ZU-esJ"/>
<outlet property="cancelButton" destination="Ump-1C-SzI" id="U8M-nT-JiK"/>
<outlet property="confirmVerifyButton" destination="L4h-aG-8UN" id="GX6-dB-JG6"/>
<outlet property="textView" destination="3Vk-Jx-L6Y" id="uOw-Bq-neN"/>
<outlet property="verifyButton" destination="qkC-t0-Qd0" id="6LD-04-WgO"/>
</connections>
</view>
</objects>
</document>
@@ -0,0 +1,69 @@
/*
Copyright 2017 Vector Creations 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>
@class MXSession, MXKViewController, MXKRoomDataSource;
/**
`MXKEncryptionKeysExportView` is a dialog to export encryption keys from
the user's crypto store.
*/
@interface MXKEncryptionKeysExportView : NSObject
/**
The UIAlertController instance which handles the dialog.
*/
@property (nonatomic, readonly) UIAlertController *alertController;
/**
The minimum length of the passphrase. 1 by default.
*/
@property (nonatomic) NSUInteger passphraseMinLength;
/**
Create the `MXKEncryptionKeysExportView` instance.
@param mxSession the mxSession to export keys from.
@return the newly created MXKEncryptionKeysExportView instance.
*/
- (instancetype)initWithMatrixSession:(MXSession*)mxSession;
/**
Show the dialog in a given view controller.
@param mxkViewController the mxkViewController where to show the dialog.
@param keyFile the path where to export keys to.
@param onComplete a block called when the operation is done.
*/
- (void)showInViewController:(MXKViewController*)mxkViewController toExportKeysToFile:(NSURL*)keyFile onComplete:(void(^)(BOOL success))onComplete;
/**
Show the dialog in a given view controller.
@param viewController the UIViewController where to show the dialog.
@param keyFile the path where to export keys to.
@param onLoading a block called when to show a spinner.
@param onComplete a block called when the operation is done.
*/
- (void)showInUIViewController:(UIViewController*)viewController
toExportKeysToFile:(NSURL*)keyFile
onLoading:(void(^)(BOOL onLoading))onLoading
onComplete:(void(^)(BOOL success))onComplete;
@end
@@ -0,0 +1,192 @@
/*
Copyright 2017 Vector Creations 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 "MXKEncryptionKeysExportView.h"
#import "MXKViewController.h"
#import "MXKRoomDataSource.h"
#import "NSBundle+MatrixKit.h"
#import <MatrixSDK/MatrixSDK.h>
#import "MXKSwiftHeader.h"
@interface MXKEncryptionKeysExportView ()
{
MXSession *mxSession;
}
@end
@implementation MXKEncryptionKeysExportView
- (instancetype)initWithMatrixSession:(MXSession *)matrixSession
{
self = [super init];
if (self)
{
mxSession = matrixSession;
_passphraseMinLength = 1;
_alertController = [UIAlertController alertControllerWithTitle:[MatrixKitL10n e2eExportRoomKeys] message:[MatrixKitL10n e2eExportPrompt] preferredStyle:UIAlertControllerStyleAlert];
}
return self;
}
- (void)showInViewController:(MXKViewController *)mxkViewController toExportKeysToFile:(NSURL *)keyFile onComplete:(void (^)(BOOL success))onComplete
{
[self showInUIViewController:mxkViewController toExportKeysToFile:keyFile onLoading:^(BOOL onLoading) {
if (onLoading)
{
[mxkViewController startActivityIndicator];
}
else
{
[mxkViewController stopActivityIndicator];
}
} onComplete:onComplete];
}
- (void)showInUIViewController:(UIViewController*)viewController
toExportKeysToFile:(NSURL*)keyFile
onLoading:(void(^)(BOOL onLoading))onLoading
onComplete:(void(^)(BOOL success))onComplete
{
__weak typeof(self) weakSelf = self;
// Finalise the dialog
[_alertController addTextFieldWithConfigurationHandler:^(UITextField *textField)
{
textField.secureTextEntry = YES;
textField.placeholder = [MatrixKitL10n e2ePassphraseCreate];
[textField resignFirstResponder];
}];
[_alertController addTextFieldWithConfigurationHandler:^(UITextField *textField)
{
textField.secureTextEntry = YES;
textField.placeholder = [MatrixKitL10n e2ePassphraseConfirm];
[textField resignFirstResponder];
}];
[_alertController addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n cancel]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
onComplete(NO);
}
}]];
[_alertController addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n e2eExport]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
// Retrieve the password and confirmation
UITextField *textField = [self.alertController textFields].firstObject;
NSString *password = textField.text;
textField = [self.alertController textFields][1];
NSString *confirmation = textField.text;
// Check they are valid
if (password.length < self.passphraseMinLength || ![password isEqualToString:confirmation])
{
NSString *error;
if (!password.length)
{
error = [MatrixKitL10n e2ePassphraseEmpty];
}
else if (password.length < self.passphraseMinLength)
{
error = [MatrixKitL10n e2ePassphraseTooShort:self.passphraseMinLength];
}
else
{
error = [MatrixKitL10n e2ePassphraseNotMatch];
}
UIAlertController *otherAlert = [UIAlertController alertControllerWithTitle:[MatrixKitL10n error] message:error preferredStyle:UIAlertControllerStyleAlert];
[otherAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n ok] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
if (weakSelf)
{
onComplete(NO);
}
}]];
[viewController presentViewController:otherAlert animated:YES completion:nil];
}
else
{
// Start the export process
onLoading(YES);
[self->mxSession.crypto exportRoomKeysWithPassword:password success:^(NSData *keyFileData) {
if (weakSelf)
{
onLoading(NO);
// Write the result to the passed file
[keyFileData writeToURL:keyFile atomically:YES];
onComplete(YES);
}
} failure:^(NSError *error) {
if (weakSelf)
{
onLoading(NO);
// TODO: i18n the error
UIAlertController *otherAlert = [UIAlertController alertControllerWithTitle:[MatrixKitL10n error] message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert];
[otherAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n ok] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
if (weakSelf)
{
onComplete(NO);
}
}]];
[viewController presentViewController:otherAlert animated:YES completion:nil];
}
}];
}
}
}]];
// And show it
[viewController presentViewController:_alertController animated:YES completion:nil];
}
@end
@@ -0,0 +1,49 @@
/*
Copyright 2017 Vector Creations 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>
@class MXSession, MXKViewController;
/**
`MXKEncryptionKeysImportView` is a dialog to import encryption keys into
the user's crypto store.
*/
@interface MXKEncryptionKeysImportView : NSObject
/**
The UIAlertController instance which handles the dialog.
*/
@property (nonatomic, readonly) UIAlertController *alertController;
/**
Create the `MXKEncryptionKeysImportView` instance.
@param mxSession the mxSession to import keys to.
@return the newly created MXKEncryptionKeysImportView instance.
*/
- (instancetype)initWithMatrixSession:(MXSession*)mxSession;
/**
Show the dialog in a given view controller.
@param mxkViewController the mxkViewController where to show the dialog.
@param fileURL the url of the keys file.
@param onComplete a block called when the operation is done (whatever it succeeded or failed).
*/
- (void)showInViewController:(MXKViewController*)mxkViewController toImportKeys:(NSURL*)fileURL onComplete:(void(^)(void))onComplete;
@end
@@ -0,0 +1,122 @@
/*
Copyright 2017 Vector Creations 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 "MXKEncryptionKeysImportView.h"
#import "MXKViewController.h"
#import "NSBundle+MatrixKit.h"
#import <MatrixSDK/MatrixSDK.h>
#import "MXKSwiftHeader.h"
@interface MXKEncryptionKeysImportView ()
{
MXSession *mxSession;
}
@end
@implementation MXKEncryptionKeysImportView
- (instancetype)initWithMatrixSession:(MXSession *)matrixSession
{
self = [super init];
if (self)
{
mxSession = matrixSession;
_alertController = [UIAlertController alertControllerWithTitle:[MatrixKitL10n e2eImportRoomKeys] message:[MatrixKitL10n e2eImportPrompt] preferredStyle:UIAlertControllerStyleAlert];
}
return self;
}
- (void)showInViewController:(MXKViewController*)mxkViewController toImportKeys:(NSURL*)fileURL onComplete:(void(^)(void))onComplete
{
__weak typeof(self) weakSelf = self;
// Finalise the dialog
[_alertController addTextFieldWithConfigurationHandler:^(UITextField *textField)
{
textField.secureTextEntry = YES;
textField.placeholder = [MatrixKitL10n e2ePassphraseEnter];
[textField resignFirstResponder];
}];
[_alertController addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n cancel]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
onComplete();
}
}]];
[_alertController addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n e2eImport]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
// Retrieve the password
UITextField *textField = [self.alertController textFields].firstObject;
NSString *password = textField.text;
// Start the import process
[mxkViewController startActivityIndicator];
[self->mxSession.crypto importRoomKeys:[NSData dataWithContentsOfURL:fileURL] withPassword:password success:^(NSUInteger total, NSUInteger imported) {
if (weakSelf)
{
[mxkViewController stopActivityIndicator];
onComplete();
}
} failure:^(NSError *error) {
if (weakSelf)
{
[mxkViewController stopActivityIndicator];
// TODO: i18n the error
UIAlertController *otherAlert = [UIAlertController alertControllerWithTitle:[MatrixKitL10n error] message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert];
[otherAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n ok] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
if (weakSelf)
{
onComplete();
}
}]];
[mxkViewController presentViewController:otherAlert animated:YES completion:nil];
}
}];
}
}]];
// And show it
[mxkViewController presentViewController:_alertController animated:YES completion:nil];
}
@end
@@ -0,0 +1,39 @@
/*
Copyright 2017 Vector Creations 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 "MXKTableViewCell.h"
#import "MXKCellRendering.h"
#import "MXKGroupCellDataStoring.h"
/**
`MXKGroupTableViewCell` instances display a group.
*/
@interface MXKGroupTableViewCell : MXKTableViewCell <MXKCellRendering>
{
@protected
/**
The current cell data displayed by the table view cell
*/
id<MXKGroupCellDataStoring> groupCellData;
}
@property (weak, nonatomic) IBOutlet UILabel *groupName;
@property (weak, nonatomic) IBOutlet UILabel *groupDescription;
@property (weak, nonatomic) IBOutlet UILabel *memberCount;
@end
@@ -0,0 +1,92 @@
/*
Copyright 2017 Vector Creations 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 "MXKGroupTableViewCell.h"
#import "NSBundle+MatrixKit.h"
#import "MXKSwiftHeader.h"
@implementation MXKGroupTableViewCell
@synthesize delegate;
#pragma mark - Class methods
- (void)render:(MXKCellData *)cellData
{
groupCellData = (id<MXKGroupCellDataStoring>)cellData;
if (groupCellData)
{
// Render the current group values.
_groupName.text = groupCellData.groupDisplayname;
_groupDescription.text = groupCellData.group.profile.shortDescription;
if (_groupDescription.text.length)
{
_groupDescription.hidden = NO;
}
else
{
// Hide and fill the label with a fake description to harmonize the height of all the cells.
// This is a drawback of the self-sizing cell.
_groupDescription.hidden = YES;
_groupDescription.text = @"No description";
}
if (_memberCount)
{
if (groupCellData.group.summary.usersSection.totalUserCountEstimate > 1)
{
_memberCount.text = [MatrixKitL10n numMembersOther:@(groupCellData.group.summary.usersSection.totalUserCountEstimate).stringValue];
}
else if (groupCellData.group.summary.usersSection.totalUserCountEstimate == 1)
{
_memberCount.text = [MatrixKitL10n numMembersOne:@(1).stringValue];
}
else
{
_memberCount.text = nil;
}
}
}
else
{
_groupName.text = nil;
_groupDescription.text = nil;
_memberCount.text = nil;
}
}
- (MXKCellData*)renderedCellData
{
return groupCellData;
}
+ (CGFloat)heightForCellData:(MXKCellData *)cellData withMaximumWidth:(CGFloat)maxWidth
{
// The height is fixed
//@TODO: change this to handle dynamic font
return 70;
}
- (void)prepareForReuse
{
[super prepareForReuse];
groupCellData = nil;
}
@end
@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13527"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" id="L2L-l5-wPx" customClass="MXKGroupTableViewCell">
<rect key="frame" x="0.0" y="0.0" width="600" height="70"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="L2L-l5-wPx" id="aXz-IR-jj5">
<rect key="frame" x="0.0" y="0.0" width="600" height="69.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="RoomTitle" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="Lg1-xQ-AGn">
<rect key="frame" x="8" y="8" width="506" height="23"/>
<fontDescription key="fontDescription" type="system" pointSize="19"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="LastEventDescription" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dQt-mN-T6b">
<rect key="frame" x="8" y="42" width="584" height="18"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="xx users" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="360-Go-RcG">
<rect key="frame" x="520" y="8" width="72" height="18"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="72" id="uOj-6w-G8q"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="Lg1-xQ-AGn" firstAttribute="leading" secondItem="aXz-IR-jj5" secondAttribute="leading" constant="8" id="3qc-BC-FYt"/>
<constraint firstItem="360-Go-RcG" firstAttribute="leading" secondItem="Lg1-xQ-AGn" secondAttribute="trailing" constant="6" id="8ff-tS-Ulq"/>
<constraint firstItem="dQt-mN-T6b" firstAttribute="leading" secondItem="aXz-IR-jj5" secondAttribute="leading" constant="8" id="UYQ-HG-YP1"/>
<constraint firstItem="360-Go-RcG" firstAttribute="top" secondItem="aXz-IR-jj5" secondAttribute="top" constant="8" id="XyO-tl-6SX"/>
<constraint firstAttribute="trailing" secondItem="360-Go-RcG" secondAttribute="trailing" constant="8" id="YqC-WC-Wqe"/>
<constraint firstAttribute="bottom" secondItem="dQt-mN-T6b" secondAttribute="bottom" constant="9" id="sLb-Q4-AFW"/>
<constraint firstItem="Lg1-xQ-AGn" firstAttribute="top" secondItem="aXz-IR-jj5" secondAttribute="top" constant="8" id="uCT-KE-X09"/>
<constraint firstAttribute="trailing" secondItem="dQt-mN-T6b" secondAttribute="trailing" constant="8" id="ygG-1B-fu3"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="groupDescription" destination="dQt-mN-T6b" id="meF-te-1lf"/>
<outlet property="groupName" destination="Lg1-xQ-AGn" id="ABQ-44-bUs"/>
<outlet property="memberCount" destination="360-Go-RcG" id="oYv-dg-CoR"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -0,0 +1,121 @@
/*
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>
#import "MXKCellData.h"
@protocol MXKCellRenderingDelegate;
/**
`MXKCellRendering` defines a protocol a view must conform to display a cell.
A cell is a generic term. It can be a UITableViewCell or a UICollectionViewCell or any object
expected by the end view controller.
*/
@protocol MXKCellRendering <NSObject>
/**
* Returns the `UINib` object initialized for the cell.
*
* @return The initialized `UINib` object or `nil` if there were errors during
* initialization or the nib file could not be located.
*/
+ (UINib *)nib;
/**
Configure the cell in order to display the passed data.
The object implementing the `MXKCellRendering` protocol should be able to cast the past object
into its original class.
@param cellData the data object to render.
*/
- (void)render:(MXKCellData*)cellData;
/**
Compute the height of the cell to display the passed data.
@TODO: To support correctly the dynamic fonts, we have to remove this method and
its use by enabling self sizing cells at the table view level.
When we create a self-sizing table view cell, we need to set the property `estimatedRowHeight` of the table view
and use constraints to define the cells size.
@param cellData the data object to render.
@param maxWidth the maximum available width.
@return the cell height
*/
+ (CGFloat)heightForCellData:(MXKCellData*)cellData withMaximumWidth:(CGFloat)maxWidth;
@optional
/**
User's actions delegate.
*/
@property (nonatomic, weak) id<MXKCellRenderingDelegate> delegate;
/**
This optional getter allow to retrieve the data object currently rendered by the cell.
@return the current rendered data object.
*/
- (MXKCellData*)renderedCellData;
/**
Reset the cell.
The cell is no more displayed. This is time to release resources and removing listeners.
In case of UITableViewCell or UIContentViewCell object, the cell must reset in a state
that it can be reusable.
*/
- (void)didEndDisplay;
@end
/**
`MXKCellRenderingDelegate` defines a protocol used when the user has interactions with
the cell view.
*/
@protocol MXKCellRenderingDelegate <NSObject>
/**
Tells the delegate that a user action (button pressed, tap, long press...) has been observed in the cell.
The action is described by the `actionIdentifier` param.
This identifier is specific and depends to the cell view class implementing MXKCellRendering.
@param cell the cell in which gesture has been observed.
@param actionIdentifier an identifier indicating the action type (tap, long press...) and which part of the cell is concerned.
@param userInfo a dict containing additional information. It depends on actionIdentifier. May be nil.
*/
- (void)cell:(id<MXKCellRendering>)cell didRecognizeAction:(NSString*)actionIdentifier userInfo:(NSDictionary *)userInfo;
/**
Asks the delegate if a user action (click on a link) can be done.
The action is described by the `actionIdentifier` param.
This identifier is specific and depends to the cell view class implementing MXKCellRendering.
@param cell the cell in which gesture has been observed.
@param actionIdentifier an identifier indicating the action type (link click) and which part of the cell is concerned.
@param userInfo a dict containing additional information. It depends on actionIdentifier. May be nil.
@param defaultValue the value to return by default if the action is not handled.
@return a boolean value which depends on actionIdentifier.
*/
- (BOOL)cell:(id<MXKCellRendering>)cell shouldDoAction:(NSString*)actionIdentifier userInfo:(NSDictionary *)userInfo defaultValue:(BOOL)defaultValue;
@end
@@ -0,0 +1,47 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations 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>
/**
'MXKCollectionViewCell' class is used to define custom UICollectionViewCell.
Each 'MXKCollectionViewCell-inherited' class has its own 'reuseIdentifier'.
*/
@interface MXKCollectionViewCell : UICollectionViewCell
/**
Returns the `UINib` object initialized for the cell.
@return The initialized `UINib` object or `nil` if there were errors during
initialization or the nib file could not be located.
*/
+ (UINib *)nib;
/**
The default reuseIdentifier of the 'MXKCollectionViewCell-inherited' class.
*/
+ (NSString*)defaultReuseIdentifier;
/**
Customize the rendering of the collection view cell and its subviews (Do nothing by default).
This method is called when the view is initialized or prepared for reuse.
Override this method to customize the collection view cell at the application level.
*/
- (void)customizeCollectionViewCellRendering;
@end
@@ -0,0 +1,76 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations 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 "MXKCollectionViewCell.h"
@implementation MXKCollectionViewCell
+ (UINib *)nib
{
// Check whether a nib file is available
NSBundle *mainBundle = [NSBundle bundleForClass:self.class];
NSString *path = [mainBundle pathForResource:NSStringFromClass([self class]) ofType:@"nib"];
if (path)
{
return [UINib nibWithNibName:NSStringFromClass([self class]) bundle:mainBundle];
}
return nil;
}
+ (NSString*)defaultReuseIdentifier
{
return NSStringFromClass([self class]);
}
- (void)awakeFromNib
{
[super awakeFromNib];
[self customizeCollectionViewCellRendering];
}
- (void)prepareForReuse
{
[super prepareForReuse];
[self customizeCollectionViewCellRendering];
}
- (instancetype)initWithFrame:(CGRect)frame
{
// Check whether a xib is defined
if ([[self class] nib])
{
self = [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject;
self.frame = frame;
}
else
{
self = [super initWithFrame:frame];
[self customizeCollectionViewCellRendering];
}
return self;
}
- (void)customizeCollectionViewCellRendering
{
// Do nothing by default.
}
@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 "MXKCollectionViewCell.h"
#import <AVKit/AVKit.h>
#import "MXKImageView.h"
/**
'MXKMediaCollectionViewCell' class is used to display picture or video thumbnail.
*/
@interface MXKMediaCollectionViewCell : MXKCollectionViewCell
@property (weak, nonatomic) IBOutlet UIView *customView;
@property (weak, nonatomic) IBOutlet MXKImageView *mxkImageView;
@property (weak, nonatomic) IBOutlet UIImageView *centerIcon;
@property (weak, nonatomic) IBOutlet UIImageView *bottomLeftIcon;
@property (weak, nonatomic) IBOutlet UIImageView *bottomRightIcon;
@property (weak, nonatomic) IBOutlet UIImageView *topRightIcon;
/**
A potential player used in the cell.
*/
@property (nonatomic) AVPlayerViewController *moviePlayer;
/**
A potential observer used to update cell display.
*/
@property (nonatomic) id notificationObserver;
@end
@@ -0,0 +1,73 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector 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 "MXKMediaCollectionViewCell.h"
@implementation MXKMediaCollectionViewCell
- (void)prepareForReuse
{
[super prepareForReuse];
[self.moviePlayer.player pause];
self.moviePlayer.player = nil;
self.moviePlayer = nil;
// Restore the cell in reusable state
self.mxkImageView.hidden = NO;
self.mxkImageView.stretchable = NO;
// Cancel potential image download
self.mxkImageView.enableInMemoryCache = NO;
[self.mxkImageView setImageURI:nil
withType:nil
andImageOrientation:UIImageOrientationUp
previewImage:nil
mediaManager:nil];
self.customView.hidden = YES;
self.centerIcon.hidden = YES;
// Remove added view in custon view
NSArray *subViews = self.customView.subviews;
for (UIView *view in subViews)
{
[view removeFromSuperview];
}
// Remove potential media download observer
if (self.notificationObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:self.notificationObserver];
self.notificationObserver = nil;
}
// Remove all gesture recognizers
while (self.gestureRecognizers.count)
{
[self removeGestureRecognizer:self.gestureRecognizers[0]];
}
self.tag = -1;
}
- (void)dealloc
{
[self.moviePlayer.player pause];
self.moviePlayer.player = nil;
}
@end
@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="eCk-zY-LXq" customClass="MXKMediaCollectionViewCell">
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="TP2-Sr-1hA" userLabel="CustomView">
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
<view contentMode="scaleAspectFit" translatesAutoresizingMaskIntoConstraints="NO" id="T1Q-RS-8o6" customClass="MXKImageView">
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="l9u-PV-2Kx" userLabel="centerIcon">
<rect key="frame" x="28" y="28" width="25" height="25"/>
<constraints>
<constraint firstAttribute="height" constant="25" id="fdh-Aa-NDo"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="25" id="pCq-on-PJR"/>
<constraint firstAttribute="width" constant="25" id="toX-JA-StF"/>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="25" id="usE-Mq-K8k"/>
</constraints>
</imageView>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="YB6-Jv-WMr" userLabel="bottomLeftIcon">
<rect key="frame" x="8" y="53" width="20" height="20"/>
<constraints>
<constraint firstAttribute="width" constant="20" id="fDo-LF-4CI"/>
<constraint firstAttribute="height" constant="20" id="yQL-WA-w9j"/>
</constraints>
</imageView>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="S1K-Sj-lhV" userLabel="topRightIcon">
<rect key="frame" x="52" y="7" width="20" height="20"/>
<constraints>
<constraint firstAttribute="width" constant="20" id="YJM-RC-2sA"/>
<constraint firstAttribute="height" constant="20" id="ozP-cX-0lT"/>
</constraints>
</imageView>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="5Yd-df-HbB" userLabel="bottomRightIcon">
<rect key="frame" x="52" y="53" width="20" height="20"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="UEZ-5S-KMf"/>
<constraint firstAttribute="width" constant="20" id="bbS-D2-SZi"/>
</constraints>
</imageView>
</subviews>
</view>
<constraints>
<constraint firstAttribute="trailing" secondItem="5Yd-df-HbB" secondAttribute="trailing" priority="750" constant="8" id="2bi-ya-uC0"/>
<constraint firstAttribute="trailing" secondItem="T1Q-RS-8o6" secondAttribute="trailing" id="2mi-ZG-4dV"/>
<constraint firstItem="YB6-Jv-WMr" firstAttribute="leading" secondItem="eCk-zY-LXq" secondAttribute="leading" priority="750" constant="8" id="43S-Eh-tnF"/>
<constraint firstItem="l9u-PV-2Kx" firstAttribute="centerY" secondItem="eCk-zY-LXq" secondAttribute="centerY" id="7oz-CQ-w1O"/>
<constraint firstAttribute="bottom" secondItem="T1Q-RS-8o6" secondAttribute="bottom" id="8rM-p9-Vwc"/>
<constraint firstItem="T1Q-RS-8o6" firstAttribute="leading" secondItem="eCk-zY-LXq" secondAttribute="leading" id="AET-tr-Wti"/>
<constraint firstItem="T1Q-RS-8o6" firstAttribute="top" secondItem="eCk-zY-LXq" secondAttribute="top" id="Fzk-Dy-HI1"/>
<constraint firstAttribute="trailing" secondItem="TP2-Sr-1hA" secondAttribute="trailing" id="NWc-zx-iym"/>
<constraint firstAttribute="bottom" secondItem="TP2-Sr-1hA" secondAttribute="bottom" id="Psv-4k-pFA"/>
<constraint firstAttribute="bottom" secondItem="YB6-Jv-WMr" secondAttribute="bottom" priority="750" constant="7" id="RTC-59-huH"/>
<constraint firstAttribute="bottom" secondItem="5Yd-df-HbB" secondAttribute="bottom" priority="750" constant="7" id="S8E-Zu-cIr"/>
<constraint firstAttribute="trailing" secondItem="S1K-Sj-lhV" secondAttribute="trailing" priority="750" constant="8" id="XfO-xa-DlO"/>
<constraint firstItem="l9u-PV-2Kx" firstAttribute="centerX" secondItem="eCk-zY-LXq" secondAttribute="centerX" id="d32-f1-U3Q"/>
<constraint firstItem="TP2-Sr-1hA" firstAttribute="top" secondItem="eCk-zY-LXq" secondAttribute="top" id="eBE-6d-RBK"/>
<constraint firstItem="TP2-Sr-1hA" firstAttribute="leading" secondItem="eCk-zY-LXq" secondAttribute="leading" id="lR0-ej-w9L"/>
<constraint firstItem="S1K-Sj-lhV" firstAttribute="top" secondItem="eCk-zY-LXq" secondAttribute="top" priority="750" constant="7" id="neN-7y-A6j"/>
</constraints>
<connections>
<outlet property="bottomLeftIcon" destination="YB6-Jv-WMr" id="EAI-25-P6H"/>
<outlet property="bottomRightIcon" destination="5Yd-df-HbB" id="X1R-n5-kOH"/>
<outlet property="centerIcon" destination="l9u-PV-2Kx" id="5E4-tR-DBr"/>
<outlet property="customView" destination="TP2-Sr-1hA" id="eOK-uE-mrx"/>
<outlet property="mxkImageView" destination="T1Q-RS-8o6" id="kPS-Hx-zbu"/>
<outlet property="topRightIcon" destination="S1K-Sj-lhV" id="yc9-aM-Rjc"/>
</connections>
</collectionViewCell>
</objects>
</document>
@@ -0,0 +1,32 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations 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 <MatrixSDK/MatrixSDK.h>
#import "MXKView.h"
@interface MXKEventDetailsView : MXKView
@property (weak, nonatomic) IBOutlet UITextView *textView;
@property (weak, nonatomic) IBOutlet UIButton *redactButton;
@property (weak, nonatomic) IBOutlet UIButton *closeButton;
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
- (instancetype)initWithEvent:(MXEvent*)event andMatrixSession:(MXSession*)session;
@end
@@ -0,0 +1,204 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2018 New Vector 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 "MXKEventDetailsView.h"
#import "NSBundle+MatrixKit.h"
#import "MXKConstants.h"
#import "MXKSwiftHeader.h"
@interface MXKEventDetailsView ()
{
/**
The displayed event
*/
MXEvent *mxEvent;
/**
The matrix session.
*/
MXSession *mxSession;
}
@end
@implementation MXKEventDetailsView
+ (UINib *)nib
{
// Check whether a nib file is available
NSBundle *mainBundle = [NSBundle mxk_bundleForClass:self.class];
NSString *path = [mainBundle pathForResource:NSStringFromClass([self class]) ofType:@"nib"];
if (path)
{
return [UINib nibWithNibName:NSStringFromClass([self class]) bundle:mainBundle];
}
return [UINib nibWithNibName:NSStringFromClass([MXKEventDetailsView class]) bundle:[NSBundle mxk_bundleForClass:[MXKEventDetailsView class]]];
}
- (void)awakeFromNib
{
[super awakeFromNib];
// Localize string
[_redactButton setTitle:[MatrixKitL10n redact] forState:UIControlStateNormal];
[_redactButton setTitle:[MatrixKitL10n redact] forState:UIControlStateHighlighted];
[_closeButton setTitle:[MatrixKitL10n close] forState:UIControlStateNormal];
[_closeButton setTitle:[MatrixKitL10n close] forState:UIControlStateHighlighted];
}
- (instancetype)initWithEvent:(MXEvent*)event andMatrixSession:(MXSession*)session
{
self = [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject;
if (self)
{
mxEvent = event;
mxSession = session;
[self setTranslatesAutoresizingMaskIntoConstraints: NO];
// Disable redact button by default
_redactButton.enabled = NO;
if (mxEvent)
{
NSMutableDictionary *eventDict = [NSMutableDictionary dictionaryWithDictionary:mxEvent.JSONDictionary];
// Remove event type added by SDK
[eventDict removeObjectForKey:@"event_type"];
// Remove null values and empty dictionaries
for (NSString *key in eventDict.allKeys)
{
if ([[eventDict objectForKey:key] isEqual:[NSNull null]])
{
[eventDict removeObjectForKey:key];
}
else if ([[eventDict objectForKey:key] isKindOfClass:[NSDictionary class]])
{
NSDictionary *dict = [eventDict objectForKey:key];
if (!dict.count)
{
[eventDict removeObjectForKey:key];
}
else
{
NSMutableDictionary *updatedDict = [NSMutableDictionary dictionaryWithDictionary:dict];
for (NSString *subKey in dict.allKeys)
{
if ([[dict objectForKey:subKey] isEqual:[NSNull null]])
{
[updatedDict removeObjectForKey:subKey];
}
}
[eventDict setObject:updatedDict forKey:key];
}
}
}
// Set text view content
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:eventDict
options:NSJSONWritingPrettyPrinted
error:&error];
_textView.text = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
// Check whether the user can redact this event
// Do not allow to redact the event that enabled encryption (m.room.encryption)
// because it breaks everything
if (!mxEvent.isRedactedEvent && mxEvent.eventType != MXEventTypeRoomEncryption)
{
// Here the event has not been already redacted, check the user's power level
MXRoom *mxRoom = [mxSession roomWithRoomId:mxEvent.roomId];
if (mxRoom)
{
MXWeakify(self);
[mxRoom state:^(MXRoomState *roomState) {
MXStrongifyAndReturnIfNil(self);
MXRoomPowerLevels *powerLevels = [roomState powerLevels];
NSInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:self->mxSession.myUser.userId];
if (powerLevels.redact)
{
if (userPowerLevel >= powerLevels.redact)
{
self.redactButton.enabled = YES;
}
}
else if (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsMessage:kMXEventTypeStringRoomRedaction])
{
self.redactButton.enabled = YES;
}
}];
}
}
}
else
{
_textView.text = nil;
}
// Hide potential activity indicator
[_activityIndicator stopAnimating];
}
return self;
}
- (void)dealloc
{
mxEvent = nil;
mxSession = nil;
}
#pragma mark - Actions
- (IBAction)onButtonPressed:(id)sender
{
if (sender == _redactButton)
{
MXRoom *mxRoom = [mxSession roomWithRoomId:mxEvent.roomId];
if (mxRoom)
{
[_activityIndicator startAnimating];
[mxRoom redactEvent:mxEvent.eventId reason:nil success:^{
[self->_activityIndicator stopAnimating];
[self removeFromSuperview];
} failure:^(NSError *error) {
MXLogDebug(@"[MXKEventDetailsView] Redact event (%@) failed", self->mxEvent.eventId);
// Notify MatrixKit user
NSString *myUserId = mxRoom.mxSession.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
[self->_activityIndicator stopAnimating];
}];
}
}
else if (sender == _closeButton)
{
[self removeFromSuperview];
}
}
@end
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="8VI-1E-fge" customClass="MXKEventDetailsView">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" bounces="NO" editable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3Vk-Jx-L6Y">
<rect key="frame" x="10" y="10" width="580" height="540"/>
<color key="backgroundColor" red="0.90000000000000002" green="0.90000000000000002" blue="0.90000000000000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="mcb-vF-Tre">
<rect key="frame" x="290" y="290" width="20" height="20"/>
</activityIndicatorView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ump-1C-SzI">
<rect key="frame" x="126" y="560" width="49" height="30"/>
<state key="normal" title="Redact">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="onButtonPressed:" destination="8VI-1E-fge" eventType="touchUpInside" id="t42-2S-MsY"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="qkC-t0-Qd0">
<rect key="frame" x="436" y="560" width="39" height="30"/>
<state key="normal" title="Close">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="onButtonPressed:" destination="8VI-1E-fge" eventType="touchUpInside" id="ljK-ul-fY2"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="3Vk-Jx-L6Y" firstAttribute="leading" secondItem="8VI-1E-fge" secondAttribute="leading" constant="10" id="1NW-e9-N6b"/>
<constraint firstAttribute="centerY" secondItem="mcb-vF-Tre" secondAttribute="centerY" id="6gd-2U-X0P"/>
<constraint firstAttribute="centerX" secondItem="qkC-t0-Qd0" secondAttribute="centerX" multiplier="0.66" id="7iU-9D-miY"/>
<constraint firstItem="qkC-t0-Qd0" firstAttribute="top" secondItem="3Vk-Jx-L6Y" secondAttribute="bottom" constant="10" id="Ch9-Be-zPy"/>
<constraint firstAttribute="bottom" secondItem="qkC-t0-Qd0" secondAttribute="bottom" constant="10" id="DyR-rX-nt0"/>
<constraint firstAttribute="bottom" secondItem="Ump-1C-SzI" secondAttribute="bottom" constant="10" id="Nfo-nh-gvW"/>
<constraint firstAttribute="trailing" secondItem="3Vk-Jx-L6Y" secondAttribute="trailing" constant="10" id="Tb0-xj-YuB"/>
<constraint firstAttribute="centerX" secondItem="Ump-1C-SzI" secondAttribute="centerX" multiplier="2" id="YMC-ob-tqT"/>
<constraint firstAttribute="centerX" secondItem="mcb-vF-Tre" secondAttribute="centerX" id="iQ6-t4-4X3"/>
<constraint firstItem="3Vk-Jx-L6Y" firstAttribute="top" secondItem="8VI-1E-fge" secondAttribute="top" constant="10" id="paN-Lu-ZgL"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<nil key="simulatedTopBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
<outlet property="activityIndicator" destination="mcb-vF-Tre" id="xpi-vr-YgP"/>
<outlet property="closeButton" destination="qkC-t0-Qd0" id="B4g-P8-vHe"/>
<outlet property="redactButton" destination="Ump-1C-SzI" id="HUY-kK-FP8"/>
<outlet property="textView" destination="3Vk-Jx-L6Y" id="uOw-Bq-neN"/>
</connections>
</view>
</objects>
</document>
+126
View File
@@ -0,0 +1,126 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector 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 <MatrixSDK/MatrixSDK.h>
#import "MXKView.h"
@class MXKAttachment;
/**
Customize UIView in order to display image defined with remote url. Zooming inside the image (Stretching) is supported.
*/
@interface MXKImageView : MXKView <UIScrollViewDelegate>
typedef void (^blockMXKImageView_onClick)(MXKImageView *imageView, NSString* title);
/**
Load an image by its Matrix Content URI.
The image is loaded from the media cache (if any). If the image is not available yet,
it is downloaded from the Matrix media repository only if a media manager instance is provided.
The image extension is extracted from the provided mime type (if any). By default 'image/jpeg' is considered.
@param mxContentURI the Matrix Content URI
@param mimeType the media mime type, it is used to define the file extension (may be nil).
@param orientation the actual orientation of the encoded image (used UIImageOrientationUp by default).
@param previewImage image displayed until the actual image is available.
@param mediaManager the media manager instance used to download the image if it is not already in cache.
*/
- (void)setImageURI:(NSString *)mxContentURI
withType:(NSString *)mimeType
andImageOrientation:(UIImageOrientation)orientation
previewImage:(UIImage*)previewImage
mediaManager:(MXMediaManager*)mediaManager;
/**
Load an image by its Matrix Content URI to fit a specific view size.
CAUTION: this method is available only for the unencrypted content.
The image is loaded from the media cache (if any). If the image is not available yet,
it is downloaded from the Matrix media repository only if a media manager instance is provided.
The image extension is extracted from the provided mime type (if any). By default 'image/jpeg' is considered.
@param mxContentURI the Matrix Content URI
@param mimeType the media mime type, it is used to define the file extension (may be nil).
@param orientation the actual orientation of the encoded image (used UIImageOrientationUp by default).
@param previewImage image displayed until the actual image is available.
@param mediaManager the media manager instance used to download the image if it is not already in cache.
*/
- (void)setImageURI:(NSString *)mxContentURI
withType:(NSString *)mimeType
andImageOrientation:(UIImageOrientation)orientation
toFitViewSize:(CGSize)viewSize
withMethod:(MXThumbnailingMethod)thumbnailingMethod
previewImage:(UIImage*)previewImage
mediaManager:(MXMediaManager*)mediaManager;
/**
* Load an image attachment into the image viewer and display the full res image.
* This method must be used to display encrypted attachments
* @param attachment The attachment
*/
- (void)setAttachment:(MXKAttachment *)attachment;
/**
* Load an attachment into the image viewer and display its thumbnail, if it has one.
* This method must be used to display encrypted attachments
* @param attachment The attachment
*/
- (void)setAttachmentThumb:(MXKAttachment *)attachment;
/**
Toggle display to fullscreen.
No change is applied on the status bar here, the caller has to handle it.
*/
- (void)showFullScreen;
/**
The default background color.
Default is [UIColor blackColor].
*/
@property (nonatomic) UIColor *defaultBackgroundColor;
// Use this boolean to hide activity indicator during image downloading
@property (nonatomic) BOOL hideActivityIndicator;
@property (strong, nonatomic) UIImage *image;
@property (nonatomic, readonly) UIImageView *imageView;
@property (nonatomic) BOOL stretchable;
@property (nonatomic, readonly) BOOL fullScreen;
// the image is cached in memory.
// The medias manager uses a LRU cache.
// to avoid loading from the file system.
@property (nonatomic) BOOL enableInMemoryCache;
// mediaManager folder where the image is stored
@property (nonatomic) NSString* mediaFolder;
// Let the user defines some custom buttons over the tabbar
- (void)setLeftButtonTitle :leftButtonTitle handler:(blockMXKImageView_onClick)handler;
- (void)setRightButtonTitle:rightButtonTitle handler:(blockMXKImageView_onClick)handler;
- (void)dismissSelection;
@end
+925
View File
@@ -0,0 +1,925 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector 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 "MXKImageView.h"
#import "MXKPieChartView.h"
#import "MXKAttachment.h"
#import "MXKTools.h"
@interface MXKImageView ()
{
NSString *mxcURI;
NSString *mimeType;
UIImageOrientation imageOrientation;
// additional settings used in case of thumbnail.
CGSize thumbnailViewSize;
MXThumbnailingMethod thumbnailMethod;
UIImage *currentImage;
// the loading view is composed with the spinner and a pie chart
// the spinner is display until progress > 0
UIView *loadingView;
UIActivityIndicatorView *waitingDownloadSpinner;
MXKPieChartView *pieChartView;
UILabel *progressInfoLabel;
// validation buttons
UIButton *leftButton;
UIButton *rightButton;
NSString *leftButtonTitle;
NSString *rightButtonTitle;
blockMXKImageView_onClick leftHandler;
blockMXKImageView_onClick rightHandler;
UIView* bottomBarView;
// Subviews
UIScrollView *scrollView;
// Current attachment being displayed in the MXKImageView
MXKAttachment *currentAttachment;
}
@end
@implementation MXKImageView
@synthesize stretchable, mediaFolder, imageView;
#define CUSTOM_IMAGE_VIEW_BUTTON_WIDTH 100
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self stopActivityIndicator];
if (loadingView)
{
[loadingView removeFromSuperview];
loadingView = nil;
}
if (bottomBarView)
{
[bottomBarView removeFromSuperview];
bottomBarView = nil;
}
pieChartView = nil;
}
#pragma mark - Override MXKView
-(void)customizeViewRendering
{
[super customizeViewRendering];
self.backgroundColor = (_defaultBackgroundColor ? _defaultBackgroundColor : [UIColor blackColor]);
self.contentMode = UIViewContentModeScaleAspectFit;
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleTopMargin;
}
#pragma mark -
- (void)startActivityIndicator
{
// create the views if they don't exist
if (!waitingDownloadSpinner)
{
waitingDownloadSpinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
CGRect frame = waitingDownloadSpinner.frame;
frame.size.width += 30;
frame.size.height += 30;
waitingDownloadSpinner.bounds = frame;
[waitingDownloadSpinner.layer setCornerRadius:5];
}
if (!loadingView)
{
loadingView = [[UIView alloc] init];
loadingView.frame = waitingDownloadSpinner.bounds;
waitingDownloadSpinner.frame = waitingDownloadSpinner.bounds;
[loadingView addSubview:waitingDownloadSpinner];
loadingView.backgroundColor = [UIColor clearColor];
[self addSubview:loadingView];
}
if (!pieChartView)
{
pieChartView = [[MXKPieChartView alloc] init];
pieChartView.frame = loadingView.bounds;
pieChartView.progress = 0;
pieChartView.progressColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.25];
pieChartView.unprogressColor = [UIColor clearColor];
[loadingView addSubview:pieChartView];
}
// display the download statistics
if (_fullScreen && !progressInfoLabel)
{
progressInfoLabel = [[UILabel alloc] init];
progressInfoLabel.backgroundColor = [UIColor whiteColor];
progressInfoLabel.textColor = [UIColor blackColor];
progressInfoLabel.font = [UIFont systemFontOfSize:8];
progressInfoLabel.alpha = 0.25;
progressInfoLabel.text = @"";
progressInfoLabel.numberOfLines = 0;
[progressInfoLabel sizeToFit];
[self addSubview:progressInfoLabel];
}
// initvalue
loadingView.hidden = NO;
pieChartView.progress = 0;
// Adjust color
if ([self.backgroundColor isEqual:[UIColor blackColor]])
{
waitingDownloadSpinner.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhite;
// a preview image could be displayed
// ensure that the white spinner is visible
// it could be drawn on a white area
waitingDownloadSpinner.backgroundColor = [UIColor darkGrayColor];
}
else
{
waitingDownloadSpinner.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
}
// ensure that the spinner is drawn at the top
[loadingView.superview bringSubviewToFront:loadingView];
// Adjust position
CGPoint center = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2);
loadingView.center = center;
// Start
[waitingDownloadSpinner startAnimating];
}
- (void)stopActivityIndicator
{
if (waitingDownloadSpinner && waitingDownloadSpinner.isAnimating)
{
[waitingDownloadSpinner stopAnimating];
}
pieChartView.progress = 0;
loadingView.hidden = YES;
if (progressInfoLabel)
{
[progressInfoLabel removeFromSuperview];
progressInfoLabel = nil;
}
}
#pragma mark - setters/getters
- (void)setDefaultBackgroundColor:(UIColor *)defaultBackgroundColor
{
_defaultBackgroundColor = defaultBackgroundColor;
[self customizeViewRendering];
}
- (void)setImage:(UIImage *)anImage
{
// remove the observers
[[NSNotificationCenter defaultCenter] removeObserver:self];
currentImage = anImage;
imageView.image = anImage;
[self initScrollZoomFactors];
}
- (UIImage*)image
{
return currentImage;
}
- (void)showFullScreen
{
// The full screen display mode is supported only if the shared application instance is available.
UIApplication *sharedApplication = [UIApplication performSelector:@selector(sharedApplication)];
if (sharedApplication)
{
_fullScreen = YES;
[self initLayout];
if (self.superview)
{
[super removeFromSuperview];
}
UIWindow *window = [sharedApplication keyWindow];
self.frame = window.bounds;
[window addSubview:self];
}
}
#pragma mark -
- (IBAction)onButtonToggle:(id)sender
{
if (sender == leftButton)
{
dispatch_async(dispatch_get_main_queue(), ^{
self->leftHandler(self, self->leftButtonTitle);
});
}
else if (sender == rightButton)
{
dispatch_async(dispatch_get_main_queue(), ^{
self->rightHandler(self, self->rightButtonTitle);
});
}
}
// add a generic button to the bottom view
// return the added UIButton
- (UIButton*) addbuttonWithTitle:(NSString*)title
{
UIButton* button = [[UIButton alloc] init];
[button setTitle:title forState:UIControlStateNormal];
[button setTitle:title forState:UIControlStateHighlighted];
if (_fullScreen)
{
// use the same text color as the tabbar
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
}
// TODO
// else {
// // use the same text color as the tabbar
// [button setTitleColor:[AppDelegate theDelegate].masterTabBarController.tabBar.tintColor forState:UIControlStateNormal];
// [button setTitleColor:[AppDelegate theDelegate].masterTabBarController.tabBar.tintColor forState:UIControlStateHighlighted];
// }
// keep the bottomView background color
button.backgroundColor = [UIColor clearColor];
[button addTarget:self action:@selector(onButtonToggle:) forControlEvents:UIControlEventTouchUpInside];
[bottomBarView addSubview:button];
return button;
}
- (void)initScrollZoomFactors
{
// check if the image can be zoomed
if (self.image && self.stretchable && imageView.frame.size.width && imageView.frame.size.height)
{
// ensure that the content size is properly initialized
scrollView.contentSize = scrollView.frame.size;
// compute the appliable zoom factor
// assume that the user does not expect to zoom more than 100%
CGSize imageSize = self.image.size;
CGFloat scaleX = imageSize.width / imageView.frame.size.width;
CGFloat scaleY = imageSize.height / imageView.frame.size.height;
if (scaleX < scaleY)
{
scaleX = scaleY;
}
if (scaleX < 1.0)
{
scaleX = 1.0;
}
scrollView.zoomScale = 1.0;
scrollView.minimumZoomScale = 1.0;
scrollView.maximumZoomScale = scaleX;
// update the image frame to ensure that it fits to the scrollview frame
imageView.frame = scrollView.bounds;
}
}
- (void)removeFromSuperview
{
[super removeFromSuperview];
[[NSNotificationCenter defaultCenter] removeObserver:self];
if (pieChartView)
{
[self stopActivityIndicator];
}
}
- (void)initLayout
{
// create the subviews if they don't exist
if (!scrollView)
{
scrollView = [[UIScrollView alloc] init];
scrollView.delegate = self;
scrollView.backgroundColor = [UIColor clearColor];
[self addSubview:scrollView];
imageView = [[UIImageView alloc] init];
imageView.backgroundColor = [UIColor clearColor];
imageView.userInteractionEnabled = YES;
imageView.contentMode = self.contentMode;
[scrollView addSubview:imageView];
}
}
- (void)layoutSubviews
{
// call upper layer
[super layoutSubviews];
[self initLayout];
// the image has been updated
if (imageView.image != self.image)
{
imageView.image = self.image;
}
CGRect tabBarFrame = CGRectZero;
UITabBarController *tabBarController = nil;
UIEdgeInsets safeAreaInsets = UIEdgeInsetsZero;
if (leftButtonTitle || rightButtonTitle)
{
UIApplication *sharedApplication = [UIApplication performSelector:@selector(sharedApplication)];
if (sharedApplication)
{
safeAreaInsets = [sharedApplication keyWindow].safeAreaInsets;
UIViewController *rootViewController = [sharedApplication keyWindow].rootViewController;
tabBarController = rootViewController.tabBarController;
if (!tabBarController && [rootViewController isKindOfClass:[UITabBarController class]])
{
tabBarController = (UITabBarController*)rootViewController;
}
}
if (tabBarController)
{
tabBarFrame = tabBarController.tabBar.frame;
}
else
{
// Define a default tabBar frame
tabBarFrame = CGRectMake(0, 0, self.frame.size.width, 44 + safeAreaInsets.bottom);
}
}
// update the scrollview frame
CGRect oneSelfFrame = self.frame;
CGRect scrollViewFrame = CGRectIntegral(scrollView.frame);
if (leftButtonTitle || rightButtonTitle)
{
oneSelfFrame.size.height -= tabBarFrame.size.height;
}
oneSelfFrame = CGRectIntegral(oneSelfFrame);
oneSelfFrame.origin = scrollViewFrame.origin = CGPointZero;
// use integral rect to avoid rounded value issue (float precision)
if (!CGRectEqualToRect(oneSelfFrame, scrollViewFrame))
{
scrollView.frame = oneSelfFrame;
imageView.frame = oneSelfFrame;
[self initScrollZoomFactors];
}
// check if the dedicated buttons are already added
if (leftButtonTitle || rightButtonTitle)
{
if (!bottomBarView)
{
bottomBarView = [[UIView alloc] init];
if (leftButtonTitle)
{
leftButton = [self addbuttonWithTitle:leftButtonTitle];
}
rightButton = [[UIButton alloc] init];
if (rightButtonTitle)
{
rightButton = [self addbuttonWithTitle:rightButtonTitle];
}
// in fullscreen, display both buttons above the view (do the same if there is no tab bar)
if (_fullScreen || tabBarController == nil)
{
bottomBarView.backgroundColor = [UIColor blackColor];
[self addSubview:bottomBarView];
}
else
{
// default tabbar background color
CGFloat base = 248.0 / 255.0f;
bottomBarView.backgroundColor = [UIColor colorWithRed:base green:base blue:base alpha:1.0];
// Display them over the tabbar
[tabBarController.tabBar addSubview:bottomBarView];
}
}
if (_fullScreen)
{
tabBarFrame.origin.y = self.frame.size.height - tabBarFrame.size.height;
}
else
{
tabBarFrame.origin.y = 0;
}
bottomBarView.frame = tabBarFrame;
if (leftButton)
{
leftButton.frame = CGRectMake(safeAreaInsets.left, 0, CUSTOM_IMAGE_VIEW_BUTTON_WIDTH, bottomBarView.frame.size.height - safeAreaInsets.bottom);
}
if (rightButton)
{
rightButton.frame = CGRectMake(bottomBarView.frame.size.width - CUSTOM_IMAGE_VIEW_BUTTON_WIDTH - safeAreaInsets.right, 0, CUSTOM_IMAGE_VIEW_BUTTON_WIDTH, bottomBarView.frame.size.height - safeAreaInsets.bottom);
}
}
if (!loadingView.hidden)
{
// ensure that the spinner is drawn at the top
[loadingView.superview bringSubviewToFront:loadingView];
// Adjust positions
CGPoint center = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2);
loadingView.center = center;
CGRect progressInfoLabelFrame = progressInfoLabel.frame;
progressInfoLabelFrame.origin.x = center.x - (progressInfoLabelFrame.size.width / 2);
progressInfoLabelFrame.origin.y = 10 + loadingView.frame.origin.y + loadingView.frame.size.height;
progressInfoLabel.frame = progressInfoLabelFrame;
}
}
- (void)setHideActivityIndicator:(BOOL)hideActivityIndicator
{
_hideActivityIndicator = hideActivityIndicator;
if (hideActivityIndicator)
{
[self stopActivityIndicator];
}
else if (mxcURI)
{
NSString *downloadId = [MXMediaManager downloadIdForMatrixContentURI:mxcURI inFolder:mediaFolder];
if ([MXMediaManager existingDownloaderWithIdentifier:downloadId])
{
// Loading is in progress, start activity indicator
[self startActivityIndicator];
}
}
}
- (void)setImageURI:(NSString *)mxContentURI
withType:(NSString *)mimeType
andImageOrientation:(UIImageOrientation)orientation
previewImage:(UIImage*)previewImage
mediaManager:(MXMediaManager*)mediaManager
{
[self setImageURI:mxContentURI
withType:mimeType
andImageOrientation:orientation
isThumbnail:NO
previewImage:previewImage
mediaManager:mediaManager];
}
- (void)setImageURI:(NSString *)mxContentURI
withType:(NSString *)mimeType
andImageOrientation:(UIImageOrientation)orientation
toFitViewSize:(CGSize)viewSize
withMethod:(MXThumbnailingMethod)thumbnailingMethod
previewImage:(UIImage*)previewImage
mediaManager:(MXMediaManager*)mediaManager
{
// Store the thumbnail settings
thumbnailViewSize = viewSize;
thumbnailMethod = thumbnailingMethod;
[self setImageURI:mxContentURI
withType:mimeType
andImageOrientation:orientation
isThumbnail:YES
previewImage:previewImage
mediaManager:mediaManager];
}
- (void)setImageURI:(NSString *)mxContentURI
withType:(NSString *)mimeType
andImageOrientation:(UIImageOrientation)orientation
isThumbnail:(BOOL)isThumbnail
previewImage:(UIImage*)previewImage
mediaManager:(MXMediaManager*)mediaManager
{
// Remove any pending observers
[[NSNotificationCenter defaultCenter] removeObserver:self];
// Reset other data
currentAttachment = nil;
mxcURI = mxContentURI;
if (!mxcURI)
{
// Set preview by default
self.image = previewImage;
return;
}
// Store image orientation
imageOrientation = orientation;
// Store the mime type used to define the cache path of the image.
mimeType = mimeType;
if (!mimeType.length)
{
// Set default mime type if no information is available
mimeType = @"image/jpeg";
}
// Retrieve the image from cache if any
NSString *cacheFilePath;
if (isThumbnail)
{
cacheFilePath = [MXMediaManager thumbnailCachePathForMatrixContentURI:mxcURI
andType:mimeType
inFolder:mediaFolder
toFitViewSize:thumbnailViewSize
withMethod:thumbnailMethod];
}
else
{
cacheFilePath = [MXMediaManager cachePathForMatrixContentURI:mxcURI
andType:mimeType
inFolder:mediaFolder];
}
UIImage* image = _enableInMemoryCache ? [MXMediaManager loadThroughCacheWithFilePath:cacheFilePath] : [MXMediaManager loadPictureFromFilePath:cacheFilePath];
if (image)
{
if (imageOrientation != UIImageOrientationUp)
{
self.image = [UIImage imageWithCGImage:image.CGImage scale:1.0 orientation:imageOrientation];
}
else
{
self.image = image;
}
[self stopActivityIndicator];
}
else
{
// Set preview until the image is loaded
self.image = previewImage;
// Check whether the image download is in progress
NSString *downloadId;
if (isThumbnail)
{
downloadId = [MXMediaManager thumbnailDownloadIdForMatrixContentURI:mxcURI
inFolder:mediaFolder
toFitViewSize:thumbnailViewSize
withMethod:thumbnailMethod];
}
else
{
downloadId = [MXMediaManager downloadIdForMatrixContentURI:mxcURI inFolder:mediaFolder];
}
MXMediaLoader* loader = [MXMediaManager existingDownloaderWithIdentifier:downloadId];
if (!loader && mediaManager)
{
// Trigger the download
if (isThumbnail)
{
loader = [mediaManager downloadThumbnailFromMatrixContentURI:mxcURI
withType:mimeType
inFolder:mediaFolder
toFitViewSize:thumbnailViewSize
withMethod:thumbnailMethod
success:nil
failure:nil];
}
else
{
loader = [mediaManager downloadMediaFromMatrixContentURI:mxcURI
withType:mimeType
inFolder:mediaFolder];
}
}
if (loader)
{
// update the progress UI with the current info
if (!_hideActivityIndicator)
{
[self startActivityIndicator];
}
[self updateProgressUI:loader.statisticsDict];
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXMediaLoaderStateDidChangeNotification object:loader];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onMediaLoaderStateDidChange:) name:kMXMediaLoaderStateDidChangeNotification object:loader];
}
}
}
- (void)setAttachment:(MXKAttachment *)attachment
{
// Remove any pending observers
[[NSNotificationCenter defaultCenter] removeObserver:self];
// Set default orientation
imageOrientation = UIImageOrientationUp;
mediaFolder = attachment.eventRoomId;
mxcURI = attachment.contentURL;
mimeType = attachment.contentInfo[@"mimetype"];
if (!mimeType.length)
{
// Set default mime type if no information is available
mimeType = @"image/jpeg";
}
// while we wait for the content to download
self.image = [attachment getCachedThumbnail];
if (!_hideActivityIndicator)
{
[self startActivityIndicator];
}
currentAttachment = attachment;
MXWeakify(self);
[attachment getImage:^(MXKAttachment *attachment2, UIImage *img) {
MXStrongifyAndReturnIfNil(self);
if (self->currentAttachment != attachment2)
{
return;
}
self.image = img;
[self stopActivityIndicator];
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXMediaLoaderStateDidChangeNotification object:nil];
} failure:^(MXKAttachment *attachment2, NSError *error) {
MXLogDebug(@"Unable to fetch image attachment! %@", error);
MXStrongifyAndReturnIfNil(self);
if (self->currentAttachment != attachment2)
{
return;
}
[self stopActivityIndicator];
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXMediaLoaderStateDidChangeNotification object:nil];
}];
// Check whether the image download is in progress
NSString *downloadId = attachment.downloadId;
MXMediaLoader* loader = [MXMediaManager existingDownloaderWithIdentifier:downloadId];
if (loader)
{
// Observer this loader to display progress
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(checkProgressOnMediaLoaderStateChange:)
name:kMXMediaLoaderStateDidChangeNotification
object:loader];
}
}
- (void)setAttachmentThumb:(MXKAttachment *)attachment
{
// Remove any pending observers
[[NSNotificationCenter defaultCenter] removeObserver:self];
// Store image orientation
imageOrientation = attachment.thumbnailOrientation;
mediaFolder = attachment.eventRoomId;
// Remove the existing image (if any) by using the potential preview.
self.image = attachment.previewImage;
if (!_hideActivityIndicator)
{
[self startActivityIndicator];
}
currentAttachment = attachment;
MXWeakify(self);
[attachment getThumbnail:^(MXKAttachment *attachment2, UIImage *img) {
MXStrongifyAndReturnIfNil(self);
dispatch_async(dispatch_get_main_queue(), ^{
if (self->currentAttachment != attachment2)
{
return;
}
if (img && self->imageOrientation != UIImageOrientationUp)
{
self.image = [UIImage imageWithCGImage:img.CGImage scale:1.0 orientation:self->imageOrientation];
}
else
{
self.image = img;
}
[self stopActivityIndicator];
});
} failure:^(MXKAttachment *attachment2, NSError *error) {
MXStrongifyAndReturnIfNil(self);
dispatch_async(dispatch_get_main_queue(), ^{
if (self->currentAttachment != attachment2)
{
return;
}
[self stopActivityIndicator];
});
}];
}
- (void)updateProgressUI:(NSDictionary*)downloadStatsDict
{
// Sanity check: updateProgressUI may be called while there is no stats available
// This happens when the download failed at the very beginning.
if (nil == downloadStatsDict)
{
return;
}
NSNumber* progressNumber = [downloadStatsDict valueForKey:kMXMediaLoaderProgressValueKey];
if (progressNumber)
{
pieChartView.progress = progressNumber.floatValue;
waitingDownloadSpinner.hidden = YES;
}
if (progressInfoLabel)
{
NSNumber* downloadRate = [downloadStatsDict valueForKey:kMXMediaLoaderCurrentDataRateKey];
NSNumber* completedBytesCount = [downloadStatsDict valueForKey:kMXMediaLoaderCompletedBytesCountKey];
NSNumber* totalBytesCount = [downloadStatsDict valueForKey:kMXMediaLoaderTotalBytesCountKey];
NSMutableString* text = [[NSMutableString alloc] init];
if (completedBytesCount && totalBytesCount)
{
NSString* progressString = [NSString stringWithFormat:@"%@ / %@", [NSByteCountFormatter stringFromByteCount:completedBytesCount.longLongValue countStyle:NSByteCountFormatterCountStyleFile], [NSByteCountFormatter stringFromByteCount:totalBytesCount.longLongValue countStyle:NSByteCountFormatterCountStyleFile]];
[text appendString:progressString];
}
if (downloadRate)
{
if (completedBytesCount && totalBytesCount)
{
CGFloat remainimgTime = ((totalBytesCount.floatValue - completedBytesCount.floatValue)) / downloadRate.floatValue;
[text appendFormat:@" (%@)", [MXKTools formatSecondsInterval:remainimgTime]];
}
[text appendFormat:@"\n %@/s", [NSByteCountFormatter stringFromByteCount:downloadRate.longLongValue countStyle:NSByteCountFormatterCountStyleFile]];
}
progressInfoLabel.text = text;
// on multilines, sizeToFit uses the current width
// so reset it
progressInfoLabel.frame = CGRectZero;
[progressInfoLabel sizeToFit];
//
CGRect progressInfoLabelFrame = progressInfoLabel.frame;
progressInfoLabelFrame.origin.x = self.center.x - (progressInfoLabelFrame.size.width / 2);
progressInfoLabelFrame.origin.y = 10 + loadingView.frame.origin.y + loadingView.frame.size.height;
progressInfoLabel.frame = progressInfoLabelFrame;
}
}
- (void)onMediaLoaderStateDidChange:(NSNotification *)notif
{
MXMediaLoader *loader = (MXMediaLoader*)notif.object;
switch (loader.state) {
case MXMediaLoaderStateDownloadInProgress:
[self updateProgressUI:loader.statisticsDict];
break;
case MXMediaLoaderStateDownloadCompleted:
{
[self stopActivityIndicator];
// update the image
UIImage* image = [MXMediaManager loadPictureFromFilePath:loader.downloadOutputFilePath];
if (image)
{
if (imageOrientation != UIImageOrientationUp)
{
self.image = [UIImage imageWithCGImage:image.CGImage scale:1.0 orientation:imageOrientation];
}
else
{
self.image = image;
}
}
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXMediaLoaderStateDidChangeNotification object:loader];
break;
}
case MXMediaLoaderStateDownloadFailed:
case MXMediaLoaderStateCancelled:
[self stopActivityIndicator];
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXMediaLoaderStateDidChangeNotification object:loader];
break;
default:
break;
}
}
- (void)checkProgressOnMediaLoaderStateChange:(NSNotification *)notif
{
MXMediaLoader *loader = (MXMediaLoader*)notif.object;
switch (loader.state) {
case MXMediaLoaderStateDownloadInProgress:
[self updateProgressUI:loader.statisticsDict];
break;
default:
break;
}
}
#pragma mark - buttons management
- (void)setLeftButtonTitle: aLeftButtonTitle handler:(blockMXKImageView_onClick)handler
{
leftButtonTitle = aLeftButtonTitle;
leftHandler = handler;
}
- (void)setRightButtonTitle:aRightButtonTitle handler:(blockMXKImageView_onClick)handler
{
rightButtonTitle = aRightButtonTitle;
rightHandler = handler;
}
- (void)dismissSelection
{
if (bottomBarView)
{
[bottomBarView removeFromSuperview];
bottomBarView = nil;
}
}
#pragma mark - UIScrollViewDelegate
// require to be able to zoom an image
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.stretchable ? imageView : nil;
}
@end
@@ -0,0 +1,31 @@
/*
Copyright 2019 New Vector 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>
NS_ASSUME_NONNULL_BEGIN
/**
MXKMessageTextView is a UITextView subclass with link detection without text selection.
*/
@interface MXKMessageTextView : UITextView
// The last hit test location received by the view.
@property (nonatomic, readonly) CGPoint lastHitTestLocation;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,57 @@
/*
Copyright 2019 New Vector 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 "MXKMessageTextView.h"
#import "UITextView+MatrixKit.h"
@interface MXKMessageTextView()
@property (nonatomic, readwrite) CGPoint lastHitTestLocation;
@end
@implementation MXKMessageTextView
- (BOOL)canBecomeFirstResponder
{
return NO;
}
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
return NO;
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
self.lastHitTestLocation = point;
return [super hitTest:point withEvent:event];
}
// Indicate to receive a touch event only if a link is hitted.
// Otherwise it means that the touch event will pass through and could be received by a view below.
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
if (![super pointInside:point withEvent:event])
{
return NO;
}
return [self isThereALinkNearPoint:point];
}
@end
@@ -0,0 +1,26 @@
/*
Copyright 2017 Aram Sargsyan
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 "MXKView.h"
@interface MXKPieChartHUD : MXKView
+ (MXKPieChartHUD *)showLoadingHudOnView:(UIView *)view WithMessage:(NSString *)message;
- (void)setProgress:(CGFloat)progress;
@end
@@ -0,0 +1,115 @@
/*
Copyright 2017 Aram Sargsyan
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 "MXKPieChartHUD.h"
#import "NSBundle+MatrixKit.h"
#import "MXKPieChartView.h"
@interface MXKPieChartHUD ()
@property (weak, nonatomic) IBOutlet UIView *hudView;
@property (weak, nonatomic) IBOutlet MXKPieChartView *pieChartView;
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
@end
@implementation MXKPieChartHUD
#pragma mark - Lifecycle
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self configureFromNib];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[self configureFromNib];
}
return self;
}
- (void)configureFromNib
{
NSBundle *bundle = [NSBundle mxk_bundleForClass:self.class];
[bundle loadNibNamed:NSStringFromClass(self.class) owner:self options:nil];
[self customizeViewRendering];
self.hudView.frame = self.bounds;
self.clipsToBounds = YES;
self.layer.cornerRadius = 10.0;
[self addSubview:self.hudView];
}
- (void)customizeViewRendering
{
[super customizeViewRendering];
self.pieChartView.backgroundColor = [UIColor clearColor];
self.pieChartView.progressColor = [UIColor whiteColor];
self.pieChartView.unprogressColor = [UIColor clearColor];
self.pieChartView.tintColor = [UIColor whiteColor];
}
- (void)layoutSubviews
{
[super layoutSubviews];
[self.pieChartView.layer setCornerRadius:self.pieChartView.frame.size.width / 2];
}
#pragma mark - Public
+ (MXKPieChartHUD *)showLoadingHudOnView:(UIView *)view WithMessage:(NSString *)message
{
MXKPieChartHUD *hud = [[MXKPieChartHUD alloc] init];
[view addSubview:hud];
hud.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *centerXConstraint = [NSLayoutConstraint constraintWithItem:hud attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
NSLayoutConstraint *centerYConstraint = [NSLayoutConstraint constraintWithItem:hud attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:hud attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:160];
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:hud attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:105];
[NSLayoutConstraint activateConstraints:@[centerXConstraint, centerYConstraint, widthConstraint, heightConstraint]];
hud.titleLabel.text = message;
return hud;
}
- (void)setProgress:(CGFloat)progress
{
[UIView animateWithDuration:0.2 animations:^{
[self.pieChartView setProgress:progress];
}];
}
@end
@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12120" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="MXKPieChartHUD">
<connections>
<outlet property="hudView" destination="iN0-l3-epB" id="og8-jA-KD7"/>
<outlet property="pieChartView" destination="f4A-bz-ZFE" id="Yuf-mb-gdi"/>
<outlet property="titleLabel" destination="bZG-ub-27R" id="BBm-ry-r4b"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="446" height="251"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view alpha="0.59999999999999998" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="kHt-vK-fvg" userLabel="Overlay View">
<rect key="frame" x="0.0" y="0.0" width="446" height="251"/>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="oXu-qz-7Vy" userLabel="Content View">
<rect key="frame" x="0.0" y="0.0" width="446" height="251"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="f4A-bz-ZFE" customClass="MXKPieChartView">
<rect key="frame" x="200.5" y="88" width="45" height="45"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="width" secondItem="f4A-bz-ZFE" secondAttribute="height" multiplier="1:1" id="M4E-Hv-0LA"/>
<constraint firstAttribute="width" constant="45" id="S4I-la-2tP"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label111" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="bZG-ub-27R">
<rect key="frame" x="10" y="214.5" width="426" height="26.5"/>
<fontDescription key="fontDescription" type="system" pointSize="22"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="bZG-ub-27R" secondAttribute="bottom" constant="10" id="H0V-Q4-weJ"/>
<constraint firstAttribute="trailing" secondItem="bZG-ub-27R" secondAttribute="trailing" priority="750" constant="10" id="MCI-qE-Pih"/>
<constraint firstItem="f4A-bz-ZFE" firstAttribute="centerY" secondItem="oXu-qz-7Vy" secondAttribute="centerY" constant="-15" id="QFo-Ym-MUh"/>
<constraint firstItem="bZG-ub-27R" firstAttribute="leading" secondItem="oXu-qz-7Vy" secondAttribute="leading" priority="750" constant="10" id="WNL-5a-Umv"/>
<constraint firstItem="bZG-ub-27R" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="oXu-qz-7Vy" secondAttribute="leading" constant="10" id="Z4o-fX-dWp"/>
<constraint firstItem="f4A-bz-ZFE" firstAttribute="centerX" secondItem="oXu-qz-7Vy" secondAttribute="centerX" id="i5g-L8-LGJ"/>
<constraint firstItem="bZG-ub-27R" firstAttribute="top" relation="greaterThanOrEqual" secondItem="f4A-bz-ZFE" secondAttribute="bottom" constant="10" id="tGZ-mn-PzZ"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="bZG-ub-27R" secondAttribute="trailing" constant="10" id="ucQ-m2-ZQj"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="kHt-vK-fvg" secondAttribute="bottom" id="5VY-8I-dHe"/>
<constraint firstAttribute="trailing" secondItem="kHt-vK-fvg" secondAttribute="trailing" id="FHO-W4-hNj"/>
<constraint firstAttribute="trailing" secondItem="oXu-qz-7Vy" secondAttribute="trailing" id="KGT-N4-891"/>
<constraint firstAttribute="bottom" secondItem="oXu-qz-7Vy" secondAttribute="bottom" id="KpV-xG-hDT"/>
<constraint firstItem="oXu-qz-7Vy" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="Xbp-ik-gW0"/>
<constraint firstItem="kHt-vK-fvg" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="ZjB-mP-gcc"/>
<constraint firstItem="oXu-qz-7Vy" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="a99-RH-B6U"/>
<constraint firstItem="kHt-vK-fvg" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="vUg-49-vYQ"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="-105" y="164.5"/>
</view>
</objects>
</document>
@@ -0,0 +1,35 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations 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 "MXKView.h"
@interface MXKPieChartView : MXKView
/**
The current progress level in [0, 1] range.
The pie chart is automatically hidden if progress <= 0.
It is shown for other progress values.
*/
@property (nonatomic) CGFloat progress;
@property (strong, nonatomic) UIColor* progressColor;
@property (strong, nonatomic) UIColor* unprogressColor;
@end
@@ -0,0 +1,139 @@
/*
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 "MXKPieChartView.h"
@interface MXKPieChartView ()
{
// graphical items
CAShapeLayer* backgroundContainerLayer;
CAShapeLayer* powerContainerLayer;
}
@end
@implementation MXKPieChartView
- (void)setProgress:(CGFloat)progress
{
// Consider only positive progress value
if (progress <= 0)
{
_progress = 0;
self.hidden = YES;
}
else
{
// Ensure that the progress value does not excceed 1.0
_progress = MIN(progress, 1.0);
self.hidden = NO;
}
// defines the view settings
CGFloat radius = self.frame.size.width / 2;
// draw a rounded view
[self.layer setCornerRadius:radius];
self.backgroundColor = [UIColor clearColor];
// draw the pie
CALayer* layer = [self layer];
// remove any previous drawn layer
if (powerContainerLayer)
{
[powerContainerLayer removeFromSuperlayer];
powerContainerLayer = nil;
}
// define default colors
if (!_progressColor)
{
_progressColor = [UIColor redColor];
}
if (!_unprogressColor)
{
_unprogressColor = [UIColor lightGrayColor];
}
// the background cell color is hidden the cell is selected.
// so put in grey the cell background triggers a weird display (the background grey is hidden but not the red part).
// add an other layer fixes the UX.
if (!backgroundContainerLayer)
{
backgroundContainerLayer = [CAShapeLayer layer];
[backgroundContainerLayer setZPosition:0];
[backgroundContainerLayer setStrokeColor:NULL];
backgroundContainerLayer.fillColor = _unprogressColor.CGColor;
// build the path
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, radius, radius);
CGPathAddArc(path, NULL, radius, radius, radius, 0 , 2 * M_PI, 0);
CGPathCloseSubpath(path);
[backgroundContainerLayer setPath:path];
CFRelease(path);
// add the sub layer
[layer addSublayer:backgroundContainerLayer];
}
if (_progress)
{
// create the filled layer
powerContainerLayer = [CAShapeLayer layer];
[powerContainerLayer setZPosition:0];
[powerContainerLayer setStrokeColor:NULL];
// power level is drawn in red
powerContainerLayer.fillColor = _progressColor.CGColor;
// build the path
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, radius, radius);
CGPathAddArc(path, NULL, radius, radius, radius, -M_PI / 2, (_progress * 2 * M_PI) - (M_PI / 2), 0);
CGPathCloseSubpath(path);
[powerContainerLayer setPath:path];
CFRelease(path);
// add the sub layer
[layer addSublayer:powerContainerLayer];
}
}
- (void)setProgressColor:(UIColor *)progressColor
{
_progressColor = progressColor;
self.progress = _progress;
}
- (void)setUnprogressColor:(UIColor *)unprogressColor
{
_unprogressColor = unprogressColor;
if (backgroundContainerLayer)
{
[backgroundContainerLayer removeFromSuperlayer];
backgroundContainerLayer = nil;
}
self.progress = _progress;
}
@end
@@ -0,0 +1,99 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector 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 <MatrixSDK/MatrixSDK.h>
#import "MXKView.h"
typedef NS_ENUM(NSInteger, ReadReceiptsAlignment)
{
/**
The latest receipt is displayed on left
*/
ReadReceiptAlignmentLeft = 0,
/**
The latest receipt is displayed on right
*/
ReadReceiptAlignmentRight = 1,
};
/**
`MXKReceiptSendersContainer` is a view dedicated to display receipt senders by using their avatars.
This container handles automatically the number of visible avatars. A label is added when avatars are not all visible (see 'moreLabel' property).
*/
@interface MXKReceiptSendersContainer : MXKView
/**
The maximum number of avatars displayed in the container. 3 by default.
*/
@property (nonatomic) NSInteger maxDisplayedAvatars;
/**
The space between avatars. 2.0 points by default.
*/
@property (nonatomic) CGFloat avatarMargin;
/**
The label added beside avatars when avatars are not all visible.
*/
@property (nonatomic) UILabel* moreLabel;
/**
The more label text color (If set to nil `moreLabel.textColor` use `UIColor.blackColor` as default color).
*/
@property (nonatomic) UIColor* moreLabelTextColor;
/*
The read receipt objects for details required in the details view
*/
@property (nonatomic) NSArray <MXReceiptData *> *readReceipts;
/*
The array of the room members that will be displayed in the container
*/
@property (nonatomic, readonly) NSArray <MXRoomMember *> *roomMembers;
/*
The placeholders of the room members that will be shown if the users don't have avatars
*/
@property (nonatomic, readonly) NSArray <UIImage *> *placeholders;
/**
Initializes an `MXKReceiptSendersContainer` object with a frame and a media manager.
This is the designated initializer.
@param frame the container frame. Note that avatar will be displayed in full height in this container.
@param mediaManager the media manager used to download the matrix user's avatar.
@return The newly-initialized MXKReceiptSendersContainer instance
*/
- (instancetype)initWithFrame:(CGRect)frame andMediaManager:(MXMediaManager*)mediaManager;
/**
Refresh the container content by using the provided room members.
@param roomMembers list of room members sorted from the latest receipt to the oldest receipt.
@param placeHolders list of placeholders, one by room member. Used when url is nil, or during avatar download.
@param alignment (see ReadReceiptsAlignment).
*/
- (void)refreshReceiptSenders:(NSArray<MXRoomMember*>*)roomMembers withPlaceHolders:(NSArray<UIImage*>*)placeHolders andAlignment:(ReadReceiptsAlignment)alignment;
@end
@@ -0,0 +1,174 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2018 New Vector 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 "MXKReceiptSendersContainer.h"
#import "MXKImageView.h"
static UIColor* kMoreLabelDefaultcolor;
@interface MXKReceiptSendersContainer ()
@property (nonatomic, readwrite) NSArray <MXRoomMember *> *roomMembers;
@property (nonatomic, readwrite) NSArray <UIImage *> *placeholders;
@property (nonatomic) MXMediaManager *mediaManager;
@end
@implementation MXKReceiptSendersContainer
+ (void)initialize
{
if (self == [MXKReceiptSendersContainer class])
{
kMoreLabelDefaultcolor = [UIColor blackColor];
}
}
- (instancetype)initWithFrame:(CGRect)frame andMediaManager:(MXMediaManager*)mediaManager
{
self = [super initWithFrame:frame];
if (self)
{
_mediaManager = mediaManager;
_maxDisplayedAvatars = 3;
_avatarMargin = 2.0;
_moreLabel = nil;
_moreLabelTextColor = kMoreLabelDefaultcolor;
}
return self;
}
- (void)refreshReceiptSenders:(NSArray<MXRoomMember*>*)roomMembers withPlaceHolders:(NSArray<UIImage*>*)placeHolders andAlignment:(ReadReceiptsAlignment)alignment
{
// Store the room members and placeholders for showing in the details view controller
self.roomMembers = roomMembers;
self.placeholders = placeHolders;
// Remove all previous content
for (UIView* view in self.subviews)
{
[view removeFromSuperview];
}
if (_moreLabel)
{
[_moreLabel removeFromSuperview];
_moreLabel = nil;
}
CGRect globalFrame = self.frame;
CGFloat side = globalFrame.size.height;
CGFloat defaultMoreLabelWidth = side < 20 ? 20 : side;
unsigned long count;
unsigned long maxDisplayableItems = (int)((globalFrame.size.width - defaultMoreLabelWidth - _avatarMargin) / (side + _avatarMargin));
maxDisplayableItems = MIN(maxDisplayableItems, _maxDisplayedAvatars);
count = MIN(roomMembers.count, maxDisplayableItems);
int index;
CGFloat xOff = 0;
if (alignment == ReadReceiptAlignmentRight)
{
xOff = globalFrame.size.width - (side + _avatarMargin);
}
for (index = 0; index < count; index++)
{
MXRoomMember *roomMember = [roomMembers objectAtIndex:index];
UIImage *preview = index < placeHolders.count ? placeHolders[index] : nil;
MXKImageView *imageView = [[MXKImageView alloc] initWithFrame:CGRectMake(xOff, 0, side, side)];
imageView.defaultBackgroundColor = [UIColor clearColor];
imageView.autoresizingMask = UIViewAutoresizingNone;
if (alignment == ReadReceiptAlignmentRight)
{
xOff -= side + _avatarMargin;
}
else
{
xOff += side + _avatarMargin;
}
[self addSubview:imageView];
imageView.enableInMemoryCache = YES;
[imageView setImageURI:roomMember.avatarUrl
withType:nil
andImageOrientation:UIImageOrientationUp
toFitViewSize:CGSizeMake(side, side)
withMethod:MXThumbnailingMethodCrop
previewImage:preview
mediaManager:_mediaManager];
[imageView.layer setCornerRadius:imageView.frame.size.width / 2];
imageView.clipsToBounds = YES;
}
// Check whether there are more than expected read receipts
if (roomMembers.count > maxDisplayableItems)
{
// Add a more indicator
// In case of right alignment, adjust the current position by considering the default label width
if (alignment == ReadReceiptAlignmentRight && side < defaultMoreLabelWidth)
{
xOff -= (defaultMoreLabelWidth - side);
}
_moreLabel = [[UILabel alloc] initWithFrame:CGRectMake(xOff, 0, defaultMoreLabelWidth, side)];
_moreLabel.text = [NSString stringWithFormat:(alignment == ReadReceiptAlignmentRight) ? @"%tu+" : @"+%tu", roomMembers.count - maxDisplayableItems];
_moreLabel.font = [UIFont systemFontOfSize:11];
_moreLabel.adjustsFontSizeToFitWidth = YES;
_moreLabel.minimumScaleFactor = 0.6;
// In case of right alignment, adjust the horizontal position according to the actual label width
if (alignment == ReadReceiptAlignmentRight)
{
[_moreLabel sizeToFit];
CGRect frame = _moreLabel.frame;
if (frame.size.width < defaultMoreLabelWidth)
{
frame.origin.x += (defaultMoreLabelWidth - frame.size.width);
_moreLabel.frame = frame;
}
}
_moreLabel.textColor = self.moreLabelTextColor ?: kMoreLabelDefaultcolor;
[self addSubview:_moreLabel];
}
}
- (void)dealloc
{
NSArray* subviews = self.subviews;
for (UIView* view in subviews)
{
[view removeFromSuperview];
}
if (_moreLabel)
{
[_moreLabel removeFromSuperview];
_moreLabel = nil;
}
}
@end
@@ -0,0 +1,71 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations 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 "MXKView.h"
@protocol MXKRoomActivitiesViewDelegate;
/**
Customize UIView to display some extra info above the RoomInputToolBar
*/
@interface MXKRoomActivitiesView : MXKView
@property (nonatomic) CGFloat height;
@property (weak, nonatomic) id<MXKRoomActivitiesViewDelegate> delegate;
/**
Returns the `UINib` object initialized for a `MXKRoomActivitiesView`.
@return The initialized `UINib` object or `nil` if there were errors during initialization
or the nib file could not be located.
@discussion You may override this method to provide a customized nib. If you do,
you should also override `roomActivitiesView` to return your
view controller loaded from your custom nib.
*/
+ (UINib *)nib;
/**
Creates and returns a new `MXKRoomActivitiesView-inherited` object.
@discussion This is the designated initializer for programmatic instantiation.
@return An initialized `MXKRoomActivitiesView-inherited` object if successful, `nil` otherwise.
*/
+ (instancetype)roomActivitiesView;
/**
Dispose any resources and listener.
*/
- (void)destroy;
@end
@protocol MXKRoomActivitiesViewDelegate <NSObject>
/**
Called when the activities view height changes.
@param roomActivitiesView the MXKRoomActivitiesView instance.
@param oldHeight its previous height.
@param newHeight its new height.
*/
- (void)didChangeHeight:(MXKRoomActivitiesView*)roomActivitiesView oldHeight:(CGFloat)oldHeight newHeight:(CGFloat)newHeight;
@end
@@ -0,0 +1,58 @@
/*
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 "MXKRoomActivitiesView.h"
@implementation MXKRoomActivitiesView
+ (UINib *)nib
{
// No 'MXKRoomActivitiesView.xib' has been defined yet
return nil;
}
+ (instancetype)roomActivitiesView
{
id instance = nil;
if ([[self class] nib])
{
@try {
instance = [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject;
}
@catch (NSException *exception) {
}
}
if (!instance)
{
instance = [[self alloc] initWithFrame:CGRectZero];
}
return instance;
}
- (void)destroy
{
_delegate = nil;
}
- (CGFloat)height
{
return self.frame.size.height;
}
@end
@@ -0,0 +1,140 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations 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 <MatrixSDK/MatrixSDK.h>
#import "MXKView.h"
@class MXKRoomCreationView;
@protocol MXKRoomCreationViewDelegate <NSObject>
/**
Tells the delegate that an alert must be presented.
@param creationView the view.
@param alertController the alert to present.
*/
- (void)roomCreationView:(MXKRoomCreationView*)creationView presentAlertController:(UIAlertController*)alertController;
/**
Tells the delegate to open the room with the provided identifier in a specific matrix session.
@param creationView the view.
@param roomId the room identifier.
@param mxSession the matrix session in which the room should be available.
*/
- (void)roomCreationView:(MXKRoomCreationView*)creationView showRoom:(NSString*)roomId withMatrixSession:(MXSession*)mxSession;
@end
/**
MXKRoomCreationView instance is a cell dedicated to room creation.
Add this view in your app to offer room creation option.
*/
@interface MXKRoomCreationView : MXKView <UITextFieldDelegate> {
@protected
UIView *inputAccessoryView;
}
/**
* Returns the `UINib` object initialized for the tool bar view.
*
* @return The initialized `UINib` object or `nil` if there were errors during
* initialization or the nib file could not be located.
*/
+ (UINib *)nib;
/**
Creates and returns a new `MXKRoomCreationView-inherited` object.
@discussion This is the designated initializer for programmatic instantiation.
@return An initialized `MXKRoomCreationView-inherited` object if successful, `nil` otherwise.
*/
+ (instancetype)roomCreationView;
/**
The delegate.
*/
@property (nonatomic, weak) id<MXKRoomCreationViewDelegate> delegate;
/**
Hide room name field (NO by default).
Set YES this property to disable room name edition and hide the related items.
*/
@property (nonatomic, getter=isRoomNameFieldHidden) BOOL roomNameFieldHidden;
/**
Hide room alias field (NO by default).
Set YES this property to disable room alias edition and hide the related items.
*/
@property (nonatomic, getter=isRoomAliasFieldHidden) BOOL roomAliasFieldHidden;
/**
Hide room participants field (NO by default).
Set YES this property to disable room participants edition and hide the related items.
*/
@property (nonatomic, getter=isParticipantsFieldHidden) BOOL participantsFieldHidden;
/**
The view height which takes into account potential hidden fields
*/
@property (nonatomic) CGFloat actualFrameHeight;
/**
*/
@property (nonatomic) NSArray* mxSessions;
/**
The custom accessory view associated to all text field of this 'MXKRoomCreationView' instance.
This view is actually used to retrieve the keyboard view. Indeed the keyboard view is the superview of
this accessory view when a text field become the first responder.
*/
@property (readonly) UIView *inputAccessoryView;
/**
UI items
*/
@property (weak, nonatomic) IBOutlet UILabel *roomNameLabel;
@property (weak, nonatomic) IBOutlet UILabel *roomAliasLabel;
@property (weak, nonatomic) IBOutlet UILabel *participantsLabel;
@property (weak, nonatomic) IBOutlet UITextField *roomNameTextField;
@property (weak, nonatomic) IBOutlet UITextField *roomAliasTextField;
@property (weak, nonatomic) IBOutlet UITextField *participantsTextField;
@property (weak, nonatomic) IBOutlet UISegmentedControl *roomVisibilityControl;
@property (weak, nonatomic) IBOutlet UIButton *createRoomBtn;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *roomNameFieldTopConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *roomAliasFieldTopConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *participantsFieldTopConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *textFieldLeftConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *createRoomBtnTopConstraint;
/**
Action registered to handle text field editing change (UIControlEventEditingChanged).
*/
- (IBAction)textFieldEditingChanged:(id)sender;
/**
Force dismiss keyboard.
*/
- (void)dismissKeyboard;
/**
Dispose any resources and listener.
*/
- (void)destroy;
@end
@@ -0,0 +1,578 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector 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 "MXKRoomCreationView.h"
#import "MXKConstants.h"
#import "NSBundle+MatrixKit.h"
#import "MXKSwiftHeader.h"
@interface MXKRoomCreationView ()
{
UIAlertController *mxSessionPicker;
// Array of homeserver suffix (NSString instance)
NSMutableArray *homeServerSuffixArray;
}
@end
@implementation MXKRoomCreationView
@synthesize inputAccessoryView;
+ (UINib *)nib
{
return [UINib nibWithNibName:NSStringFromClass([MXKRoomCreationView class])
bundle:[NSBundle bundleForClass:[MXKRoomCreationView class]]];
}
+ (instancetype)roomCreationView
{
if ([[self class] nib])
{
return [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject;
}
else
{
return [[self alloc] init];
}
}
- (void)awakeFromNib
{
[super awakeFromNib];
// Add observer to keep align text fields
[_roomNameLabel addObserver:self forKeyPath:@"text" options:0 context:nil];
[_roomAliasLabel addObserver:self forKeyPath:@"text" options:0 context:nil];
[_participantsLabel addObserver:self forKeyPath:@"text" options:0 context:nil];
[self alignTextFields];
// Finalize setup
[self setTranslatesAutoresizingMaskIntoConstraints: NO];
// Add an accessory view to the text views in order to retrieve keyboard view.
inputAccessoryView = [[UIView alloc] initWithFrame:CGRectZero];
_roomNameTextField.inputAccessoryView = inputAccessoryView;
_roomAliasTextField.inputAccessoryView = inputAccessoryView;
_participantsTextField.inputAccessoryView = inputAccessoryView;
// Localize strings
_roomNameLabel.text = [MatrixKitL10n roomCreationNameTitle];
_roomNameTextField.placeholder = [MatrixKitL10n roomCreationNamePlaceholder];
_roomAliasLabel.text = [MatrixKitL10n roomCreationAliasTitle];
_roomAliasTextField.placeholder = [MatrixKitL10n roomCreationAliasPlaceholder];
_participantsLabel.text = [MatrixKitL10n roomCreationParticipantsTitle];
_participantsTextField.placeholder = [MatrixKitL10n roomCreationParticipantsPlaceholder];
[_roomVisibilityControl setTitle:[MatrixKitL10n public] forSegmentAtIndex:0];
[_roomVisibilityControl setTitle:[MatrixKitL10n private] forSegmentAtIndex:1];
[_createRoomBtn setTitle:[MatrixKitL10n createRoom] forState:UIControlStateNormal];
[_createRoomBtn setTitle:[MatrixKitL10n createRoom] forState:UIControlStateHighlighted];
}
- (void)dealloc
{
[self destroy];
inputAccessoryView = nil;
}
- (void)setRoomNameFieldHidden:(BOOL)roomNameFieldHidden
{
_roomNameFieldHidden = _roomNameTextField.hidden = _roomNameLabel.hidden = roomNameFieldHidden;
if (roomNameFieldHidden)
{
_roomAliasFieldTopConstraint.constant -= _roomNameTextField.frame.size.height + 8;
_participantsFieldTopConstraint.constant -= _roomNameTextField.frame.size.height + 8;
_createRoomBtnTopConstraint.constant -= _roomNameTextField.frame.size.height + 8;
}
else
{
_roomAliasFieldTopConstraint.constant += _roomNameTextField.frame.size.height + 8;
_participantsFieldTopConstraint.constant += _roomNameTextField.frame.size.height + 8;
_createRoomBtnTopConstraint.constant += _roomNameTextField.frame.size.height + 8;
}
[self alignTextFields];
}
- (void)setRoomAliasFieldHidden:(BOOL)roomAliasFieldHidden
{
_roomAliasFieldHidden = _roomAliasTextField.hidden = _roomAliasLabel.hidden = roomAliasFieldHidden;
if (roomAliasFieldHidden)
{
_participantsFieldTopConstraint.constant -= _roomAliasTextField.frame.size.height + 8;
_createRoomBtnTopConstraint.constant -= _roomAliasTextField.frame.size.height + 8;
}
else
{
_participantsFieldTopConstraint.constant += _roomAliasTextField.frame.size.height + 8;
_createRoomBtnTopConstraint.constant += _roomAliasTextField.frame.size.height + 8;
}
[self alignTextFields];
}
- (void)setParticipantsFieldHidden:(BOOL)participantsFieldHidden
{
_participantsFieldHidden = _participantsTextField.hidden = _participantsLabel.hidden = participantsFieldHidden;
if (participantsFieldHidden)
{
_createRoomBtnTopConstraint.constant -= _participantsTextField.frame.size.height + 8;
}
else
{
_createRoomBtnTopConstraint.constant += _participantsTextField.frame.size.height + 8;
}
[self alignTextFields];
}
- (CGFloat)actualFrameHeight
{
return (_createRoomBtnTopConstraint.constant + _createRoomBtn.frame.size.height + 8);
}
- (void)setMxSessions:(NSArray *)mxSessions
{
_mxSessions = mxSessions;
if (mxSessions.count)
{
homeServerSuffixArray = [NSMutableArray array];
for (MXSession *mxSession in mxSessions)
{
NSString *homeserverSuffix = mxSession.matrixRestClient.homeserverSuffix;
if (homeserverSuffix && [homeServerSuffixArray indexOfObject:homeserverSuffix] == NSNotFound)
{
[homeServerSuffixArray addObject:homeserverSuffix];
}
}
}
else
{
homeServerSuffixArray = nil;
}
// Update alias placeholder in room creation section
if (homeServerSuffixArray.count == 1)
{
_roomAliasTextField.placeholder = [MatrixKitL10n roomCreationAliasPlaceholderWithHomeserver:homeServerSuffixArray.firstObject];
}
else
{
_roomAliasTextField.placeholder = [MatrixKitL10n roomCreationAliasPlaceholder];
}
}
- (void)dismissKeyboard
{
// Hide the keyboard
[_roomNameTextField resignFirstResponder];
[_roomAliasTextField resignFirstResponder];
[_participantsTextField resignFirstResponder];
}
- (void)destroy
{
self.mxSessions = nil;
// Remove observers
[_roomNameLabel removeObserver:self forKeyPath:@"text"];
[_roomAliasLabel removeObserver:self forKeyPath:@"text"];
[_participantsLabel removeObserver:self forKeyPath:@"text"];
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
// UIView will be "transparent" for touch events if we return NO
return YES;
}
#pragma mark - Internal methods
- (void)alignTextFields
{
CGFloat maxLabelLenght = 0;
if (!_roomNameLabel.hidden)
{
maxLabelLenght = _roomNameLabel.frame.size.width;
}
if (!_roomAliasLabel.hidden && maxLabelLenght < _roomAliasLabel.frame.size.width)
{
maxLabelLenght = _roomAliasLabel.frame.size.width;
}
if (!_participantsLabel.hidden && maxLabelLenght < _participantsLabel.frame.size.width)
{
maxLabelLenght = _participantsLabel.frame.size.width;
}
// Update textField left constraint by adding marging
_textFieldLeftConstraint.constant = maxLabelLenght + (2 * 8);
[self layoutIfNeeded];
}
- (NSString*)alias
{
// Extract alias name from alias text field
NSString *alias = _roomAliasTextField.text;
if (alias.length)
{
// Remove '#' character
alias = [alias substringFromIndex:1];
NSString *actualAlias = nil;
for (NSString *homeServerSuffix in homeServerSuffixArray)
{
// Remove homeserver suffix
NSRange range = [alias rangeOfString:homeServerSuffix];
if (range.location != NSNotFound)
{
actualAlias = [alias stringByReplacingCharactersInRange:range withString:@""];
break;
}
}
if (actualAlias)
{
alias = actualAlias;
}
else
{
MXLogDebug(@"[MXKRoomCreationView] Wrong room alias has been set (%@)", _roomAliasTextField.text);
alias = nil;
}
}
if (! alias.length)
{
alias = nil;
}
return alias;
}
- (NSArray*)participantsList
{
NSMutableArray *participants = [NSMutableArray array];
if (_participantsTextField.text.length)
{
NSArray *components = [_participantsTextField.text componentsSeparatedByString:@";"];
for (NSString *component in components)
{
// Remove white space from both ends
NSString *user = [component stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
if (user.length > 1 && [user hasPrefix:@"@"])
{
[participants addObject:user];
}
}
}
if (participants.count == 0)
{
participants = nil;
}
return participants;
}
- (void)selectMatrixSession:(void (^)(MXSession *selectedSession))onSelection
{
if (_mxSessions.count == 1)
{
if (onSelection)
{
onSelection(_mxSessions.firstObject);
}
}
else if (_mxSessions.count > 1)
{
if (mxSessionPicker)
{
[mxSessionPicker dismissViewControllerAnimated:NO completion:nil];
}
mxSessionPicker = [UIAlertController alertControllerWithTitle:[MatrixKitL10n selectAccount] message:nil preferredStyle:UIAlertControllerStyleActionSheet];
__weak typeof(self) weakSelf = self;
for(MXSession *mxSession in _mxSessions)
{
[mxSessionPicker addAction:[UIAlertAction actionWithTitle:mxSession.myUser.userId
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->mxSessionPicker = nil;
if (onSelection)
{
onSelection(mxSession);
}
}
}]];
}
[mxSessionPicker addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n cancel]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->mxSessionPicker = nil;
}
}]];
[mxSessionPicker popoverPresentationController].sourceView = self;
[mxSessionPicker popoverPresentationController].sourceRect = self.bounds;
if (self.delegate)
{
[self.delegate roomCreationView:self presentAlertController:mxSessionPicker];
}
}
}
#pragma mark - UITextField delegate
- (IBAction)textFieldEditingChanged:(id)sender
{
// Update Create Room button
NSString *roomName = _roomNameTextField.text;
NSString *roomAlias = _roomAliasTextField.text;
NSString *participants = _participantsTextField.text;
// Room alias is required to create public room
_createRoomBtn.enabled = ((_roomVisibilityControl.selectedSegmentIndex == 0) ? roomAlias.length : (roomName.length || roomAlias.length || participants.length));
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
if (textField == _participantsTextField)
{
if (textField.text.length == 0)
{
textField.text = @"@";
}
}
else if (textField == _roomAliasTextField)
{
if (textField.text.length == 0)
{
textField.text = @"#";
}
}
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
if (textField == _roomAliasTextField)
{
if (homeServerSuffixArray.count == 1)
{
// Check whether homeserver suffix should be added
NSRange range = [textField.text rangeOfString:@":"];
if (range.location == NSNotFound)
{
textField.text = [textField.text stringByAppendingString:homeServerSuffixArray.firstObject];
}
}
// Check whether the alias is valid
if (!self.alias)
{
// reset text field
textField.text = nil;
// Update Create button status
_createRoomBtn.enabled = ((_roomVisibilityControl.selectedSegmentIndex == 1) && (_roomNameTextField.text.length || _participantsTextField.text.length));
}
}
else if (textField == _participantsTextField)
{
NSArray *participants = self.participantsList;
textField.text = [participants componentsJoinedByString:@"; "];
// Update Create button status
_createRoomBtn.enabled = ((_roomVisibilityControl.selectedSegmentIndex == 0) ? _roomAliasTextField.text.length : (_roomNameTextField.text.length || _roomAliasTextField.text.length || _participantsTextField.text.length));
}
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
// Auto complete participant IDs
if (textField == _participantsTextField)
{
// Add @ if none
if (!textField.text.length || textField.text.length == range.length)
{
if ([string hasPrefix:@"@"] == NO)
{
textField.text = [NSString stringWithFormat:@"@%@",string];
return NO;
}
}
else if (range.location == textField.text.length)
{
if ([string isEqualToString:@";"])
{
// Add '@' character
textField.text = [textField.text stringByAppendingString:@"; @"];
return NO;
}
}
}
else if (textField == _roomAliasTextField)
{
// Add # if none
if (!textField.text.length || textField.text.length == range.length)
{
if ([string hasPrefix:@"#"] == NO)
{
if ([string isEqualToString:@":"] && homeServerSuffixArray.count == 1)
{
textField.text = [NSString stringWithFormat:@"#%@",homeServerSuffixArray.firstObject];
}
else
{
textField.text = [NSString stringWithFormat:@"#%@",string];
}
return NO;
}
}
else if (homeServerSuffixArray.count == 1)
{
// Add homeserver automatically when user adds ':' at the end
if (range.location == textField.text.length && [string isEqualToString:@":"])
{
textField.text = [textField.text stringByAppendingString:homeServerSuffixArray.firstObject];
return NO;
}
}
}
return YES;
}
- (BOOL)textFieldShouldReturn:(UITextField*) textField
{
// "Done" key has been pressed
[textField resignFirstResponder];
return YES;
}
#pragma mark - Actions
- (IBAction)onButtonPressed:(id)sender
{
[self dismissKeyboard];
// Handle multi-sessions here
[self selectMatrixSession:^(MXSession *selectedSession)
{
if (sender == self->_createRoomBtn)
{
// Disable button to prevent multiple request
self->_createRoomBtn.enabled = NO;
MXRoomCreationParameters *roomCreationParameters = [MXRoomCreationParameters new];
roomCreationParameters.name = self->_roomNameTextField.text;
if (!roomCreationParameters.name.length)
{
roomCreationParameters.name = nil;
}
// Check whether some users must be invited
roomCreationParameters.inviteArray = self.participantsList;
// Prepare room settings
if (self->_roomVisibilityControl.selectedSegmentIndex == 0)
{
roomCreationParameters.visibility = kMXRoomDirectoryVisibilityPublic;
}
else
{
roomCreationParameters.visibility = kMXRoomDirectoryVisibilityPrivate;
roomCreationParameters.isDirect = (roomCreationParameters.inviteArray.count == 1);
}
// Ensure direct chat are created with equal ops on both sides (the trusted_private_chat preset)
roomCreationParameters.preset = (roomCreationParameters.isDirect ? kMXRoomPresetTrustedPrivateChat : nil);
// Create new room
[selectedSession createRoomWithParameters:roomCreationParameters success:^(MXRoom *room) {
// Reset text fields
self->_roomNameTextField.text = nil;
self->_roomAliasTextField.text = nil;
self->_participantsTextField.text = nil;
if (self.delegate)
{
// Open created room
[self.delegate roomCreationView:self showRoom:room.roomId withMatrixSession:selectedSession];
}
} failure:^(NSError *error) {
self->_createRoomBtn.enabled = YES;
MXLogDebug(@"[MXKRoomCreationView] Create room (%@ %@ (%@)) failed", self->_roomNameTextField.text, self.alias, (self->_roomVisibilityControl.selectedSegmentIndex == 0) ? @"Public":@"Private");
// Notify MatrixKit user
NSString *myUserId = selectedSession.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
}];
}
}];
}
#pragma mark - KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
// Check whether one label has been updated
if ([@"text" isEqualToString:keyPath] && (object == _roomNameLabel || object == _roomAliasLabel || object == _participantsLabel))
{
// Update left constraint of the text fields
[object sizeToFit];
[self alignTextFields];
}
}
@end
@@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="pWR-M4-k2c" customClass="MXKRoomCreationView">
<rect key="frame" x="0.0" y="0.0" width="600" height="164"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="(e.g. lunchGroup)" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="IVl-ZW-gpm">
<rect key="frame" x="109" y="8" width="483" height="30"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" returnKeyType="done"/>
<connections>
<action selector="textFieldEditingChanged:" destination="pWR-M4-k2c" eventType="editingChanged" id="s8S-yT-1Me"/>
<outlet property="delegate" destination="pWR-M4-k2c" id="xKT-ld-5W2"/>
</connections>
</textField>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Room name:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VgY-Xe-EWu">
<rect key="frame" x="8" y="12" width="91" height="20"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="(e.g. #foo:homeserver)" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="cQR-iw-qK7">
<rect key="frame" x="109" y="46" width="483" height="30"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="done"/>
<connections>
<action selector="textFieldEditingChanged:" destination="pWR-M4-k2c" eventType="editingChanged" id="wug-Ut-Gbb"/>
<outlet property="delegate" destination="pWR-M4-k2c" id="NLj-fO-7S1"/>
</connections>
</textField>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Room alias:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Yxi-SY-9AS">
<rect key="frame" x="8" y="50" width="83" height="20"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="(e.g. @bob:homeserver1; @john:homeserver2...)" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="ntR-iR-LWI">
<rect key="frame" x="108" y="84" width="484" height="30"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="done"/>
<connections>
<action selector="textFieldEditingChanged:" destination="pWR-M4-k2c" eventType="editingChanged" id="yLf-bu-2oI"/>
<outlet property="delegate" destination="pWR-M4-k2c" id="YMU-bj-YZf"/>
</connections>
</textField>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Participants:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AXJ-7h-Su1">
<rect key="frame" x="8" y="87" width="91" height="20"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="8rs-if-WjB">
<rect key="frame" x="94" y="125" width="113" height="29"/>
<constraints>
<constraint firstAttribute="width" constant="113" id="586-hY-v29"/>
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="113" id="7dM-UM-eNl"/>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="113" id="Bjv-y7-dA7"/>
</constraints>
<segments>
<segment title="Public"/>
<segment title="Private"/>
</segments>
<connections>
<action selector="textFieldEditingChanged:" destination="pWR-M4-k2c" eventType="valueChanged" id="47w-qB-srf"/>
</connections>
</segmentedControl>
<button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ega-pA-bej">
<rect key="frame" x="411" y="125" width="89" height="30"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<state key="normal" title="Create Room">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="onButtonPressed:" destination="pWR-M4-k2c" eventType="touchUpInside" id="gXl-ZF-r1N"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="cQR-iw-qK7" secondAttribute="trailing" constant="8" id="0cB-U1-VZA"/>
<constraint firstItem="IVl-ZW-gpm" firstAttribute="top" secondItem="pWR-M4-k2c" secondAttribute="top" constant="8" id="32v-lV-s4o"/>
<constraint firstAttribute="centerX" secondItem="8rs-if-WjB" secondAttribute="centerX" multiplier="2" id="7ZV-PE-7jb"/>
<constraint firstAttribute="trailing" secondItem="ntR-iR-LWI" secondAttribute="trailing" constant="8" id="8nG-wm-5IR"/>
<constraint firstItem="VgY-Xe-EWu" firstAttribute="leading" secondItem="pWR-M4-k2c" secondAttribute="leading" constant="8" id="DdS-cu-Y25"/>
<constraint firstItem="IVl-ZW-gpm" firstAttribute="leading" secondItem="pWR-M4-k2c" secondAttribute="leading" constant="109" id="J5j-Fw-qqc"/>
<constraint firstItem="ega-pA-bej" firstAttribute="centerY" secondItem="8rs-if-WjB" secondAttribute="centerY" constant="1" id="Kbc-Bj-YBf"/>
<constraint firstItem="cQR-iw-qK7" firstAttribute="centerX" secondItem="IVl-ZW-gpm" secondAttribute="centerX" id="Lai-pI-DCS"/>
<constraint firstItem="VgY-Xe-EWu" firstAttribute="centerY" secondItem="IVl-ZW-gpm" secondAttribute="centerY" constant="-1" id="Mls-23-azV"/>
<constraint firstAttribute="centerX" secondItem="ega-pA-bej" secondAttribute="centerX" multiplier="0.66" id="Nu0-mz-Cvz"/>
<constraint firstItem="ntR-iR-LWI" firstAttribute="centerX" secondItem="IVl-ZW-gpm" secondAttribute="centerX" constant="-0.5" id="OPT-zM-9ST"/>
<constraint firstItem="ega-pA-bej" firstAttribute="top" secondItem="pWR-M4-k2c" secondAttribute="top" constant="125" id="SYc-Ge-iWE"/>
<constraint firstAttribute="trailing" secondItem="IVl-ZW-gpm" secondAttribute="trailing" constant="8" id="Scw-Nj-Uef"/>
<constraint firstItem="ntR-iR-LWI" firstAttribute="top" secondItem="pWR-M4-k2c" secondAttribute="top" constant="84" id="ZK5-Z1-Fgq"/>
<constraint firstItem="Yxi-SY-9AS" firstAttribute="leading" secondItem="pWR-M4-k2c" secondAttribute="leading" constant="8" id="cax-9B-Qgo"/>
<constraint firstItem="cQR-iw-qK7" firstAttribute="centerY" secondItem="Yxi-SY-9AS" secondAttribute="centerY" constant="1" id="g3m-Mp-LHU"/>
<constraint firstItem="ntR-iR-LWI" firstAttribute="centerY" secondItem="AXJ-7h-Su1" secondAttribute="centerY" constant="2" id="suf-qu-Xzx"/>
<constraint firstItem="AXJ-7h-Su1" firstAttribute="leading" secondItem="pWR-M4-k2c" secondAttribute="leading" constant="8" id="wwP-0Z-cCe"/>
<constraint firstItem="cQR-iw-qK7" firstAttribute="top" secondItem="pWR-M4-k2c" secondAttribute="top" constant="46" id="xMf-E2-suf"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
<outlet property="createRoomBtn" destination="ega-pA-bej" id="psO-nt-6EI"/>
<outlet property="createRoomBtnTopConstraint" destination="SYc-Ge-iWE" id="iqw-pV-pK5"/>
<outlet property="participantsFieldTopConstraint" destination="ZK5-Z1-Fgq" id="FKM-Yu-PZ0"/>
<outlet property="participantsLabel" destination="AXJ-7h-Su1" id="Obs-td-uG8"/>
<outlet property="participantsTextField" destination="ntR-iR-LWI" id="vfZ-AE-sPH"/>
<outlet property="roomAliasFieldTopConstraint" destination="xMf-E2-suf" id="bgv-5X-sAL"/>
<outlet property="roomAliasLabel" destination="Yxi-SY-9AS" id="y2a-fF-YO7"/>
<outlet property="roomAliasTextField" destination="cQR-iw-qK7" id="Xmy-Gb-A8S"/>
<outlet property="roomNameFieldTopConstraint" destination="32v-lV-s4o" id="Uee-1k-lhQ"/>
<outlet property="roomNameLabel" destination="VgY-Xe-EWu" id="36a-Vw-KBs"/>
<outlet property="roomNameTextField" destination="IVl-ZW-gpm" id="Q0R-2X-NPw"/>
<outlet property="roomVisibilityControl" destination="8rs-if-WjB" id="0bS-Xd-YKE"/>
<outlet property="textFieldLeftConstraint" destination="J5j-Fw-qqc" id="5lB-MS-5G6"/>
</connections>
<point key="canvasLocation" x="215" y="464"/>
</view>
</objects>
</document>
@@ -0,0 +1,82 @@
/*
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 "MXKCellData.h"
/**
List the display box types for the cell subviews.
*/
typedef enum : NSUInteger {
/**
By default the view display box is unchanged.
*/
MXKTableViewCellDisplayBoxTypeDefault,
/**
Define a circle box based on the smaller size of the view frame, some portion of content may be clipped.
*/
MXKTableViewCellDisplayBoxTypeCircle,
/**
Round the corner of the display box of the view.
*/
MXKTableViewCellDisplayBoxTypeRoundedCorner
} MXKTableViewCellDisplayBoxType;
/**
'MXKTableViewCell' class is used to define custom UITableViewCell.
Each 'MXKTableViewCell-inherited' class has its own 'reuseIdentifier'.
*/
@interface MXKTableViewCell : UITableViewCell
{
@protected
NSString *mxkReuseIdentifier;
}
/**
Returns the `UINib` object initialized for the cell.
@return The initialized `UINib` object or `nil` if there were errors during
initialization or the nib file could not be located.
*/
+ (UINib *)nib;
/**
The default reuseIdentifier of the 'MXKTableViewCell-inherited' class.
*/
+ (NSString*)defaultReuseIdentifier;
/**
Override [UITableViewCell initWithStyle:reuseIdentifier:] to load cell content from nib file (if any),
and handle reuse identifier.
*/
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier;
/**
Customize the rendering of the table view cell and its subviews (Do nothing by default).
This method is called when the view is initialized or prepared for reuse.
Override this method to customize the table view cell at the application level.
*/
- (void)customizeTableViewCellRendering;
/**
The current cell data displayed by the table view cell
*/
@property (weak, nonatomic, readonly) MXKCellData *mxkCellData;
@end
@@ -0,0 +1,100 @@
/*
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 "MXKTableViewCell.h"
#import "NSBundle+MatrixKit.h"
@implementation MXKTableViewCell
+ (UINib *)nib
{
// Check whether a nib file is available
NSBundle *mainBundle = [NSBundle mxk_bundleForClass:self.class];
NSString *path = [mainBundle pathForResource:NSStringFromClass([self class]) ofType:@"nib"];
if (path)
{
return [UINib nibWithNibName:NSStringFromClass([self class]) bundle:mainBundle];
}
return nil;
}
+ (NSString*)defaultReuseIdentifier
{
return NSStringFromClass([self class]);
}
- (void)awakeFromNib
{
[super awakeFromNib];
[self customizeTableViewCellRendering];
}
- (void)prepareForReuse
{
[super prepareForReuse];
[self customizeTableViewCellRendering];
}
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
// Check whether a xib is defined
if ([[self class] nib])
{
self = [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject;
}
else
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
[self customizeTableViewCellRendering];
}
if (reuseIdentifier.length)
{
// The provided identifier is not always conserved in the new created cell.
// This depends how the method [initWithStyle:reuseIdentifier:] is trigerred.
// Trick: we store a copy of this identifier.
mxkReuseIdentifier = reuseIdentifier;
}
else
{
mxkReuseIdentifier = [[self class] defaultReuseIdentifier];
}
return self;
}
- (NSString*)reuseIdentifier
{
NSString *identifier = super.reuseIdentifier;
if (!identifier.length)
{
identifier = mxkReuseIdentifier;
}
return identifier;
}
- (void)customizeTableViewCellRendering
{
// Do nothing by default.
}
@end
@@ -0,0 +1,27 @@
/*
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 "MXKTableViewCell.h"
/**
'MXKTableViewCellWithButton' inherits 'MXKTableViewCell' class.
It constains a 'UIButton' centered in cell content view.
*/
@interface MXKTableViewCellWithButton : MXKTableViewCell
@property (strong, nonatomic) IBOutlet UIButton *mxkButton;
@end
@@ -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 "MXKTableViewCellWithButton.h"
@implementation MXKTableViewCellWithButton
- (void)prepareForReuse
{
[super prepareForReuse];
// TODO: Code commented for a quick fix for https://github.com/vector-im/riot-ios/issues/1323
// This line was a fix for https://github.com/vector-im/riot-ios/issues/1354
// but it creates a regression that is worse than the bug it fixes.
// self.mxkButton.titleLabel.text = nil;
[self.mxkButton removeTarget:nil action:nil forControlEvents:UIControlEventAllEvents];
self.mxkButton.accessibilityIdentifier = nil;
}
@end
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="20U-7H-xmi" customClass="MXKTableViewCellWithButton">
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="20U-7H-xmi" id="cFw-g7-Cgn">
<rect key="frame" x="0.0" y="0.0" width="600" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ycy-33-7Zq">
<rect key="frame" x="276" y="7" width="49" height="30"/>
<state key="normal" title="Submit">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
</button>
</subviews>
<constraints>
<constraint firstAttribute="centerY" secondItem="Ycy-33-7Zq" secondAttribute="centerY" id="6kh-Pa-47V"/>
<constraint firstAttribute="centerX" secondItem="Ycy-33-7Zq" secondAttribute="centerX" id="uAj-IK-F0I"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="mxkButton" destination="Ycy-33-7Zq" id="rQt-Th-xOb"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -0,0 +1,36 @@
/*
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 "MXKTableViewCell.h"
/**
'MXKTableViewCellWithButtons' inherits 'MXKTableViewCell' class.
It displays several buttons with the system style in a UITableViewCell. All buttons have the same width and they are horizontally aligned.
They are vertically centered.
*/
@interface MXKTableViewCellWithButtons : MXKTableViewCell
/**
The number of buttons
*/
@property (nonatomic) NSUInteger mxkButtonNumber;
/**
The current array of buttons
*/
@property (nonatomic) NSArray *mxkButtons;
@end
@@ -0,0 +1,156 @@
/*
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 "MXKTableViewCellWithButtons.h"
@interface MXKTableViewCellWithButtons ()
{
NSMutableArray *buttonArray;
}
@end
@implementation MXKTableViewCellWithButtons
- (void)setMxkButtonNumber:(NSUInteger)buttonNumber
{
if (_mxkButtonNumber == buttonNumber)
{
return;
}
_mxkButtonNumber = buttonNumber;
buttonArray = [NSMutableArray arrayWithCapacity:buttonNumber];
CGFloat containerWidth = self.contentView.frame.size.width / buttonNumber;
UIView *previousContainer = nil;
NSLayoutConstraint *leftConstraint;
NSLayoutConstraint *rightConstraint;
NSLayoutConstraint *widthConstraint;
NSLayoutConstraint *topConstraint;
NSLayoutConstraint *bottomConstraint;
for (NSInteger index = 0; index < buttonNumber; index++)
{
UIView *buttonContainer = [[UIView alloc] initWithFrame:CGRectMake(index * containerWidth, 0, containerWidth, self.contentView.frame.size.height)];
buttonContainer.backgroundColor = [UIColor clearColor];
[self.contentView addSubview:buttonContainer];
// Add container constraints
buttonContainer.translatesAutoresizingMaskIntoConstraints = NO;
if (!previousContainer)
{
leftConstraint = [NSLayoutConstraint constraintWithItem:buttonContainer
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.contentView
attribute:NSLayoutAttributeLeading
multiplier:1
constant:0];
widthConstraint = [NSLayoutConstraint constraintWithItem:buttonContainer
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.contentView
attribute:NSLayoutAttributeWidth
multiplier:(1.0 / buttonNumber)
constant:0];
}
else
{
leftConstraint = [NSLayoutConstraint constraintWithItem:buttonContainer
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:previousContainer
attribute:NSLayoutAttributeTrailing
multiplier:1
constant:0];
widthConstraint = [NSLayoutConstraint constraintWithItem:buttonContainer
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:previousContainer
attribute:NSLayoutAttributeWidth
multiplier:1
constant:0];
}
topConstraint = [NSLayoutConstraint constraintWithItem:buttonContainer
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.contentView
attribute:NSLayoutAttributeTop
multiplier:1
constant:0];
bottomConstraint = [NSLayoutConstraint constraintWithItem:buttonContainer
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.contentView
attribute:NSLayoutAttributeBottom
multiplier:1
constant:0];
[NSLayoutConstraint activateConstraints:@[leftConstraint, widthConstraint, topConstraint, bottomConstraint]];
previousContainer = buttonContainer;
// Add Button
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
button.frame = CGRectMake(10, 8, containerWidth - 20, buttonContainer.frame.size.height - 16);
[buttonContainer addSubview:button];
[buttonArray addObject:button];
// Add button constraints
button.translatesAutoresizingMaskIntoConstraints = NO;
leftConstraint = [NSLayoutConstraint constraintWithItem:button
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:buttonContainer
attribute:NSLayoutAttributeLeading
multiplier:1
constant:10];
rightConstraint = [NSLayoutConstraint constraintWithItem:button
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:buttonContainer
attribute:NSLayoutAttributeTrailing
multiplier:1
constant:-10];
topConstraint = [NSLayoutConstraint constraintWithItem:button
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:buttonContainer
attribute:NSLayoutAttributeTop
multiplier:1
constant:8];
bottomConstraint = [NSLayoutConstraint constraintWithItem:button
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:buttonContainer
attribute:NSLayoutAttributeBottom
multiplier:1
constant:-8];
[NSLayoutConstraint activateConstraints:@[leftConstraint, rightConstraint, topConstraint, bottomConstraint]];
}
}
- (NSArray*)mxkButtons
{
return [NSArray arrayWithArray:buttonArray];
}
@end
@@ -0,0 +1,38 @@
/*
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 "MXKTableViewCell.h"
/**
'MXKTableViewCellWithLabelAndButton' inherits 'MXKTableViewCell' class.
It constains a 'UILabel' and a 'UIButton' vertically centered.
*/
@interface MXKTableViewCellWithLabelAndButton : MXKTableViewCell
@property (strong, nonatomic) IBOutlet UILabel *mxkLabel;
@property (strong, nonatomic) IBOutlet UIButton *mxkButton;
/**
Leading/Trailing constraints define here spacing to nearest neighbor (no relative to margin)
*/
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelLeadingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkButtonLeadingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkButtonTrailingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkButtonProportionalWidthToMxkButtonConstraint;
@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 "MXKTableViewCellWithLabelAndButton.h"
@implementation MXKTableViewCellWithLabelAndButton
@end
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="aCf-VI-ocb" customClass="MXKTableViewCellWithLabelAndButton">
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="aCf-VI-ocb" id="Eg5-vl-rni">
<rect key="frame" x="0.0" y="0.0" width="600" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Xx4-AP-OQs">
<rect key="frame" x="8" y="11" width="411" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="nTw-zr-xt2">
<rect key="frame" x="427" y="7" width="165" height="30"/>
<state key="normal" title="Button">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
</button>
</subviews>
<constraints>
<constraint firstAttribute="centerY" secondItem="Xx4-AP-OQs" secondAttribute="centerY" id="A2R-gN-0sW"/>
<constraint firstItem="Xx4-AP-OQs" firstAttribute="leading" secondItem="Eg5-vl-rni" secondAttribute="leading" constant="8" id="Wih-ke-1dd"/>
<constraint firstAttribute="trailing" secondItem="nTw-zr-xt2" secondAttribute="trailing" constant="8" id="f4b-wS-uFy"/>
<constraint firstAttribute="centerY" secondItem="nTw-zr-xt2" secondAttribute="centerY" id="gyq-nC-R4z"/>
<constraint firstItem="nTw-zr-xt2" firstAttribute="width" secondItem="Xx4-AP-OQs" secondAttribute="width" multiplier="0.4" id="ill-js-N8j"/>
<constraint firstItem="nTw-zr-xt2" firstAttribute="leading" secondItem="Xx4-AP-OQs" secondAttribute="trailing" constant="8" id="s8d-Y5-xZR"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="mxkButton" destination="nTw-zr-xt2" id="I8D-FA-NHu"/>
<outlet property="mxkButtonLeadingConstraint" destination="s8d-Y5-xZR" id="yFz-3Q-x4B"/>
<outlet property="mxkButtonProportionalWidthToMxkButtonConstraint" destination="ill-js-N8j" id="XNz-6f-8V1"/>
<outlet property="mxkButtonTrailingConstraint" destination="f4b-wS-uFy" id="W7t-7z-PeJ"/>
<outlet property="mxkLabel" destination="Xx4-AP-OQs" id="LI1-Yq-PW8"/>
<outlet property="mxkLabelLeadingConstraint" destination="Wih-ke-1dd" id="2dO-ba-i74"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -0,0 +1,44 @@
/*
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 "MXKTableViewCell.h"
/**
'MXKTableViewCellWithLabelAndImageView' inherits 'MXKTableViewCell' class.
It constains a 'UILabel' and a 'UIImageView' vertically centered.
*/
@interface MXKTableViewCellWithLabelAndImageView : MXKTableViewCell
@property (strong, nonatomic) IBOutlet UILabel *mxkLabel;
@property (strong, nonatomic) IBOutlet UIImageView *mxkImageView;
/**
Leading/Trailing constraints define here spacing to nearest neighbor (no relative to margin)
*/
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelLeadingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkImageViewLeadingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkImageViewTrailingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkImageViewWidthConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkImageViewHeightConstraint;
/**
The image view display box type ('MXKTableViewCellDisplayBoxTypeDefault' by default)
*/
@property (nonatomic) MXKTableViewCellDisplayBoxType mxkImageViewDisplayBoxType;
@end
@@ -0,0 +1,44 @@
/*
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 "MXKTableViewCellWithLabelAndImageView.h"
@implementation MXKTableViewCellWithLabelAndImageView
- (void)layoutSubviews
{
[super layoutSubviews];
if (self.mxkImageViewDisplayBoxType == MXKTableViewCellDisplayBoxTypeCircle)
{
// Round image view for thumbnail
_mxkImageView.layer.cornerRadius = _mxkImageView.frame.size.width / 2;
_mxkImageView.clipsToBounds = YES;
}
else if (self.mxkImageViewDisplayBoxType == MXKTableViewCellDisplayBoxTypeRoundedCorner)
{
_mxkImageView.layer.cornerRadius = 5;
_mxkImageView.clipsToBounds = YES;
}
else
{
_mxkImageView.layer.cornerRadius = 0;
_mxkImageView.clipsToBounds = NO;
}
}
@end
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="aCf-VI-ocb" customClass="MXKTableViewCellWithLabelAndImageView">
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="aCf-VI-ocb" id="Eg5-vl-rni">
<rect key="frame" x="0.0" y="0.0" width="600" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Xx4-AP-OQs">
<rect key="frame" x="8" y="11" width="543" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="F9Q-h1-mTt">
<rect key="frame" x="559" y="5" width="33" height="33"/>
<constraints>
<constraint firstAttribute="width" constant="33" id="Mbb-3z-EJx"/>
<constraint firstAttribute="height" constant="33" id="SN2-B2-NSE"/>
</constraints>
</imageView>
</subviews>
<constraints>
<constraint firstItem="F9Q-h1-mTt" firstAttribute="centerY" secondItem="Eg5-vl-rni" secondAttribute="centerY" id="2I6-r3-K1d"/>
<constraint firstItem="Xx4-AP-OQs" firstAttribute="leading" secondItem="Eg5-vl-rni" secondAttribute="leading" constant="8" id="Wih-ke-1dd"/>
<constraint firstItem="F9Q-h1-mTt" firstAttribute="leading" secondItem="Xx4-AP-OQs" secondAttribute="trailing" priority="750" constant="8" id="mXu-V2-6Sm"/>
<constraint firstAttribute="trailing" secondItem="F9Q-h1-mTt" secondAttribute="trailing" constant="8" id="v4T-uD-LAm"/>
<constraint firstAttribute="centerY" secondItem="Xx4-AP-OQs" secondAttribute="centerY" id="v6O-QC-moW"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="mxkImageView" destination="F9Q-h1-mTt" id="4Lx-au-5RN"/>
<outlet property="mxkImageViewHeightConstraint" destination="SN2-B2-NSE" id="Zql-LZ-nsj"/>
<outlet property="mxkImageViewLeadingConstraint" destination="mXu-V2-6Sm" id="Ecf-aj-MJv"/>
<outlet property="mxkImageViewTrailingConstraint" destination="v4T-uD-LAm" id="HLK-AJ-Gj9"/>
<outlet property="mxkImageViewWidthConstraint" destination="Mbb-3z-EJx" id="10m-q5-Yok"/>
<outlet property="mxkLabel" destination="Xx4-AP-OQs" id="PHz-Lv-lrf"/>
<outlet property="mxkLabelLeadingConstraint" destination="Wih-ke-1dd" id="tyK-6D-jiQ"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -0,0 +1,46 @@
/*
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 "MXKTableViewCell.h"
#import "MXKImageView.h"
/**
'MXKTableViewCellWithLabelAndMXKImageView' inherits 'MXKTableViewCell' class.
It constains a 'UILabel' and a 'MXKImageView' vertically centered.
*/
@interface MXKTableViewCellWithLabelAndMXKImageView : MXKTableViewCell
@property (strong, nonatomic) IBOutlet UILabel *mxkLabel;
@property (strong, nonatomic) IBOutlet MXKImageView *mxkImageView;
/**
Leading/Trailing constraints define here spacing to nearest neighbor (no relative to margin)
*/
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelLeadingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkImageViewLeadingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkImageViewTrailingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkImageViewWidthConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkImageViewHeightConstraint;
/**
The MXKImageView display box type ('MXKTableViewCellDisplayBoxTypeDefault' by default)
*/
@property (nonatomic) MXKTableViewCellDisplayBoxType mxkImageViewDisplayBoxType;
@end
@@ -0,0 +1,44 @@
/*
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 "MXKTableViewCellWithLabelAndMXKImageView.h"
@implementation MXKTableViewCellWithLabelAndMXKImageView
- (void)layoutSubviews
{
[super layoutSubviews];
if (self.mxkImageViewDisplayBoxType == MXKTableViewCellDisplayBoxTypeCircle)
{
// Round image view for thumbnail
_mxkImageView.layer.cornerRadius = _mxkImageView.frame.size.width / 2;
_mxkImageView.clipsToBounds = YES;
}
else if (self.mxkImageViewDisplayBoxType == MXKTableViewCellDisplayBoxTypeRoundedCorner)
{
_mxkImageView.layer.cornerRadius = 5;
_mxkImageView.clipsToBounds = YES;
}
else
{
_mxkImageView.layer.cornerRadius = 0;
_mxkImageView.clipsToBounds = NO;
}
}
@end
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="aCf-VI-ocb" customClass="MXKTableViewCellWithLabelAndMXKImageView">
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="aCf-VI-ocb" id="Eg5-vl-rni">
<rect key="frame" x="0.0" y="0.0" width="600" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Xx4-AP-OQs">
<rect key="frame" x="8" y="11" width="538" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7Ts-eR-MZV" userLabel="Mxk ImageView" customClass="MXKImageView">
<rect key="frame" x="554" y="5" width="33" height="33"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="33" id="Ejk-bJ-0MB"/>
<constraint firstAttribute="height" constant="33" id="m9S-XK-OTa"/>
</constraints>
</view>
</subviews>
<constraints>
<constraint firstItem="Xx4-AP-OQs" firstAttribute="leading" secondItem="Eg5-vl-rni" secondAttribute="leading" constant="8" id="Wih-ke-1dd"/>
<constraint firstAttribute="trailing" secondItem="7Ts-eR-MZV" secondAttribute="trailing" constant="13" id="atK-Pp-aD8"/>
<constraint firstItem="7Ts-eR-MZV" firstAttribute="leading" secondItem="Xx4-AP-OQs" secondAttribute="trailing" constant="8" id="mm9-sU-77x"/>
<constraint firstAttribute="centerY" secondItem="Xx4-AP-OQs" secondAttribute="centerY" id="v6O-QC-moW"/>
</constraints>
</tableViewCellContentView>
<constraints>
<constraint firstItem="7Ts-eR-MZV" firstAttribute="centerY" secondItem="aCf-VI-ocb" secondAttribute="centerY" id="lgB-TU-fX3"/>
</constraints>
<connections>
<outlet property="mxkImageView" destination="7Ts-eR-MZV" id="ALS-2d-HUG"/>
<outlet property="mxkImageViewHeightConstraint" destination="m9S-XK-OTa" id="hRs-A7-Zrx"/>
<outlet property="mxkImageViewLeadingConstraint" destination="mm9-sU-77x" id="nNv-Ns-zdz"/>
<outlet property="mxkImageViewTrailingConstraint" destination="atK-Pp-aD8" id="kr5-ot-BDV"/>
<outlet property="mxkImageViewWidthConstraint" destination="Ejk-bJ-0MB" id="fgn-XC-e4o"/>
<outlet property="mxkLabel" destination="Xx4-AP-OQs" id="PHz-Lv-lrf"/>
<outlet property="mxkLabelLeadingConstraint" destination="Wih-ke-1dd" id="ONt-IX-CEx"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -0,0 +1,42 @@
/*
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 "MXKTableViewCell.h"
/**
'MXKTableViewCellWithLabelAndSlider' inherits 'MXKTableViewCell' class.
It constains a 'UILabel' and a 'UISlider'.
*/
@interface MXKTableViewCellWithLabelAndSlider : MXKTableViewCell
@property (nonatomic) IBOutlet UILabel *mxkLabel;
@property (nonatomic) IBOutlet UISlider *mxkSlider;
/**
Leading/Trailing constraints define here spacing to nearest neighbor (no relative to margin)
*/
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelTopConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelLeadingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelTrailingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSliderTopConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSliderLeadingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSliderTrailingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSliderBottomConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSliderHeightConstraint;
@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 "MXKTableViewCellWithLabelAndSlider.h"
@implementation MXKTableViewCellWithLabelAndSlider
@end
@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" rowHeight="88" id="lA9-3N-IIx" customClass="MXKTableViewCellWithLabelAndSlider">
<rect key="frame" x="0.0" y="0.0" width="600" height="88"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="lA9-3N-IIx" id="Vc0-lB-zV6">
<rect key="frame" x="0.0" y="0.0" width="600" height="87"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" preferredMaxLayoutWidth="584" translatesAutoresizingMaskIntoConstraints="NO" id="k24-l4-6rr">
<rect key="frame" x="8" y="8" width="584" height="34"/>
<constraints>
<constraint firstAttribute="height" constant="34" id="FNq-mS-rjp"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="gcY-FQ-8r8">
<rect key="frame" x="6" y="49" width="588" height="31"/>
<color key="minimumTrackTintColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="maximumTrackTintColor" red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</slider>
</subviews>
<constraints>
<constraint firstItem="k24-l4-6rr" firstAttribute="top" secondItem="Vc0-lB-zV6" secondAttribute="top" constant="8" id="2hw-Fl-Xrg"/>
<constraint firstAttribute="bottom" secondItem="gcY-FQ-8r8" secondAttribute="bottom" constant="8" id="9Om-Ak-gtM"/>
<constraint firstItem="gcY-FQ-8r8" firstAttribute="leading" secondItem="Vc0-lB-zV6" secondAttribute="leading" constant="8" id="FkG-hh-eyX"/>
<constraint firstItem="k24-l4-6rr" firstAttribute="leading" secondItem="Vc0-lB-zV6" secondAttribute="leading" constant="8" id="OLf-mB-0l0"/>
<constraint firstAttribute="trailing" secondItem="k24-l4-6rr" secondAttribute="trailing" constant="8" id="PFM-WC-USa"/>
<constraint firstItem="gcY-FQ-8r8" firstAttribute="top" secondItem="k24-l4-6rr" secondAttribute="bottom" constant="7" id="aEh-bN-jLX"/>
<constraint firstAttribute="trailing" secondItem="gcY-FQ-8r8" secondAttribute="trailing" constant="8" id="mo8-a8-N8Q"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="mxkLabel" destination="k24-l4-6rr" id="y0D-Mw-0um"/>
<outlet property="mxkLabelLeadingConstraint" destination="OLf-mB-0l0" id="ET4-n2-A2u"/>
<outlet property="mxkLabelTopConstraint" destination="2hw-Fl-Xrg" id="t7n-aA-lql"/>
<outlet property="mxkLabelTrailingConstraint" destination="PFM-WC-USa" id="MPu-eZ-Utj"/>
<outlet property="mxkSlider" destination="gcY-FQ-8r8" id="uhT-97-E4d"/>
<outlet property="mxkSliderBottomConstraint" destination="9Om-Ak-gtM" id="A7D-Tr-5Xg"/>
<outlet property="mxkSliderHeightConstraint" destination="FNq-mS-rjp" id="0vO-0U-iqS"/>
<outlet property="mxkSliderLeadingConstraint" destination="FkG-hh-eyX" id="gCG-SE-H49"/>
<outlet property="mxkSliderTopConstraint" destination="aEh-bN-jLX" id="6Y5-0U-7Ma"/>
<outlet property="mxkSliderTrailingConstraint" destination="mo8-a8-N8Q" id="G8j-DF-rZr"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -0,0 +1,39 @@
/*
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 "MXKTableViewCell.h"
/**
'MXKTableViewCellWithLabelAndSubLabel' inherits 'MXKTableViewCell' class.
It constains two 'UILabel' instances.
*/
@interface MXKTableViewCellWithLabelAndSubLabel : MXKTableViewCell
@property (strong, nonatomic) IBOutlet UILabel* mxkLabel;
@property (strong, nonatomic) IBOutlet UILabel* mxkSublabel;
/**
Leading/Trailing constraints define here spacing to nearest neighbor (no relative to margin)
*/
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelTopConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelLeadingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelTrailingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSublabelLeadingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSublabelTrailingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSublabelBottomConstraint;
@end
@@ -0,0 +1,21 @@
/*
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 "MXKTableViewCellWithLabelAndSubLabel.h"
@implementation MXKTableViewCellWithLabelAndSubLabel
@end
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="odA-la-SwE" customClass="MXKTableViewCellWithLabelAndSubLabel">
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="odA-la-SwE" id="Ice-YN-Ffy">
<rect key="frame" x="0.0" y="0.0" width="600" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rfi-Fa-7Uq">
<rect key="frame" x="8" y="6" width="584" height="18"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Sublabel" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bFk-PP-KQ8">
<rect key="frame" x="8" y="22" width="584" height="15"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="bFk-PP-KQ8" firstAttribute="leading" secondItem="Ice-YN-Ffy" secondAttribute="leading" constant="8" id="EIU-NZ-lLo"/>
<constraint firstItem="rfi-Fa-7Uq" firstAttribute="top" secondItem="Ice-YN-Ffy" secondAttribute="top" constant="6" id="RhT-OU-fAa"/>
<constraint firstAttribute="bottom" secondItem="bFk-PP-KQ8" secondAttribute="bottom" constant="6" id="XUy-p8-Cn5"/>
<constraint firstItem="rfi-Fa-7Uq" firstAttribute="leading" secondItem="Ice-YN-Ffy" secondAttribute="leading" constant="8" id="czr-je-LwV"/>
<constraint firstAttribute="trailing" secondItem="rfi-Fa-7Uq" secondAttribute="trailing" constant="8" id="pnn-m6-Idk"/>
<constraint firstAttribute="trailing" secondItem="bFk-PP-KQ8" secondAttribute="trailing" constant="8" id="tlW-4P-80L"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="mxkLabel" destination="rfi-Fa-7Uq" id="F0V-zy-oHR"/>
<outlet property="mxkLabelLeadingConstraint" destination="czr-je-LwV" id="RiL-Rk-lx8"/>
<outlet property="mxkLabelTopConstraint" destination="RhT-OU-fAa" id="tDf-zZ-FDk"/>
<outlet property="mxkLabelTrailingConstraint" destination="pnn-m6-Idk" id="fXH-jC-dc5"/>
<outlet property="mxkSublabel" destination="bFk-PP-KQ8" id="w63-Pz-7Fc"/>
<outlet property="mxkSublabelBottomConstraint" destination="XUy-p8-Cn5" id="ula-Yb-yTz"/>
<outlet property="mxkSublabelLeadingConstraint" destination="EIU-NZ-lLo" id="KF2-oE-8yn"/>
<outlet property="mxkSublabelTrailingConstraint" destination="tlW-4P-80L" id="uP5-iq-D1f"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -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 "MXKTableViewCell.h"
/**
'MXKTableViewCellWithLabelAndSwitch' inherits 'MXKTableViewCell' class.
It constains a 'UILabel' and a 'UISwitch' vertically centered.
*/
@interface MXKTableViewCellWithLabelAndSwitch : MXKTableViewCell
@property (strong, nonatomic) IBOutlet UILabel *mxkLabel;
@property (strong, nonatomic) IBOutlet UISwitch *mxkSwitch;
/**
Leading/Trailing constraints define here spacing to nearest neighbor (no relative to margin)
*/
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelLeadingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSwitchLeadingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSwitchTrailingConstraint;
@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 "MXKTableViewCellWithLabelAndSwitch.h"
@implementation MXKTableViewCellWithLabelAndSwitch
@end
@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12121" systemVersion="16F73" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="aCf-VI-ocb" customClass="MXKTableViewCellWithLabelAndSwitch">
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="aCf-VI-ocb" id="Eg5-vl-rni">
<rect key="frame" x="0.0" y="0.0" width="600" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9dA-E8-w3v">
<rect key="frame" x="543" y="6" width="51" height="31"/>
</switch>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Xx4-AP-OQs">
<rect key="frame" x="8" y="11" width="42" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="9dA-E8-w3v" firstAttribute="centerY" secondItem="Eg5-vl-rni" secondAttribute="centerY" id="HHv-PY-RDK"/>
<constraint firstAttribute="bottomMargin" secondItem="Xx4-AP-OQs" secondAttribute="bottom" constant="3" id="Ikz-iJ-l4h"/>
<constraint firstItem="Xx4-AP-OQs" firstAttribute="top" secondItem="Eg5-vl-rni" secondAttribute="topMargin" constant="3" id="Vem-3Z-NyP"/>
<constraint firstItem="Xx4-AP-OQs" firstAttribute="leading" secondItem="Eg5-vl-rni" secondAttribute="leading" constant="8" id="Wih-ke-1dd"/>
<constraint firstItem="9dA-E8-w3v" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Xx4-AP-OQs" secondAttribute="trailing" constant="8" id="hms-8a-BYd"/>
<constraint firstAttribute="trailing" secondItem="9dA-E8-w3v" secondAttribute="trailing" constant="8" id="oN3-Ph-hS5"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="mxkLabel" destination="Xx4-AP-OQs" id="jTM-zE-gtP"/>
<outlet property="mxkLabelLeadingConstraint" destination="Wih-ke-1dd" id="b2U-OW-efL"/>
<outlet property="mxkSwitch" destination="9dA-E8-w3v" id="sko-Dh-EsF"/>
<outlet property="mxkSwitchLeadingConstraint" destination="hms-8a-BYd" id="ACm-RB-bE8"/>
<outlet property="mxkSwitchTrailingConstraint" destination="oN3-Ph-hS5" id="Y17-Ii-c3Y"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -0,0 +1,47 @@
/*
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 "MXKTableViewCell.h"
/**
'MXKTableViewCellWithLabelAndTextField' inherits 'MXKTableViewCell' class.
It constains a 'UILabel' and a 'UITextField' vertically centered.
*/
@interface MXKTableViewCellWithLabelAndTextField : MXKTableViewCell <UITextFieldDelegate>
{
@protected
UIView *inputAccessoryView;
}
@property (strong, nonatomic) IBOutlet UILabel *mxkLabel;
@property (strong, nonatomic) IBOutlet UITextField *mxkTextField;
/**
The custom accessory view associated with the text field. This view is
actually used to retrieve the keyboard view. Indeed the keyboard view is the superview of
the accessory view when the text field become the first responder.
*/
@property (readonly) UIView *inputAccessoryView;
/**
Leading/Trailing constraints define here spacing to nearest neighbor (no relative to margin)
*/
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelLeadingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelMinWidthConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkTextFieldLeadingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkTextFieldTrailingConstraint;
@end
@@ -0,0 +1,58 @@
/*
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 "MXKTableViewCellWithLabelAndTextField.h"
@implementation MXKTableViewCellWithLabelAndTextField
@synthesize inputAccessoryView;
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
{
// Add an accessory view to the text view in order to retrieve keyboard view.
inputAccessoryView = [[UIView alloc] initWithFrame:CGRectZero];
_mxkTextField.inputAccessoryView = inputAccessoryView;
}
return self;
}
- (void)dealloc
{
inputAccessoryView = nil;
}
- (void)layoutSubviews
{
[super layoutSubviews];
// Fix the minimum width of the label in order to keep it visible when the textfield width is increasing.
[_mxkLabel sizeToFit];
_mxkLabelMinWidthConstraint.constant = _mxkLabel.frame.size.width;
}
#pragma mark - UITextField delegate
- (BOOL)textFieldShouldReturn:(UITextField*)textField
{
// "Done" key has been pressed
[self.mxkTextField resignFirstResponder];
return YES;
}
@end
@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="5Je-1y-foH" customClass="MXKTableViewCellWithLabelAndTextField">
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="5Je-1y-foH" id="hCm-wd-Yup">
<rect key="frame" x="0.0" y="0.0" width="600" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" minimumFontSize="14" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="SBE-PS-i6p">
<rect key="frame" x="58" y="7" width="534" height="30"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" returnKeyType="done"/>
<connections>
<outlet property="delegate" destination="5Je-1y-foH" id="Nie-b5-gB9"/>
</connections>
</textField>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" preferredMaxLayoutWidth="584" translatesAutoresizingMaskIntoConstraints="NO" id="DE7-uC-Oz2">
<rect key="frame" x="8" y="11" width="42" height="21"/>
<color key="tintColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="10" id="Qzq-Ej-Wdb"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="SBE-PS-i6p" secondAttribute="trailing" constant="8" id="7NM-6p-4fc"/>
<constraint firstItem="SBE-PS-i6p" firstAttribute="centerY" secondItem="DE7-uC-Oz2" secondAttribute="centerY" id="LEG-PI-Zmz"/>
<constraint firstAttribute="centerY" secondItem="DE7-uC-Oz2" secondAttribute="centerY" id="PU3-ZA-23J"/>
<constraint firstItem="SBE-PS-i6p" firstAttribute="leading" secondItem="DE7-uC-Oz2" secondAttribute="trailing" constant="8" id="h1v-HS-kzp"/>
<constraint firstItem="DE7-uC-Oz2" firstAttribute="leading" secondItem="hCm-wd-Yup" secondAttribute="leading" constant="8" id="qse-r7-x6e"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="mxkLabel" destination="DE7-uC-Oz2" id="CTb-my-hkV"/>
<outlet property="mxkLabelLeadingConstraint" destination="qse-r7-x6e" id="WiE-p0-3PB"/>
<outlet property="mxkLabelMinWidthConstraint" destination="Qzq-Ej-Wdb" id="SJc-IC-N5T"/>
<outlet property="mxkTextField" destination="SBE-PS-i6p" id="jLi-27-mbO"/>
<outlet property="mxkTextFieldLeadingConstraint" destination="h1v-HS-kzp" id="Sx7-JU-SeG"/>
<outlet property="mxkTextFieldTrailingConstraint" destination="7NM-6p-4fc" id="tBw-Gt-9pm"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -0,0 +1,58 @@
/*
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 "MXKTableViewCell.h"
/**
'MXKTableViewCellWithLabelTextFieldAndButton' inherits 'MXKTableViewCell' class.
It constains a 'UILabel' on the first line. The second line is composed with a 'UITextField' and a 'UIButton'
vertically aligned.
*/
@interface MXKTableViewCellWithLabelTextFieldAndButton : MXKTableViewCell <UITextFieldDelegate>
{
@protected
UIView *inputAccessoryView;
}
@property (strong, nonatomic) IBOutlet UILabel *mxkLabel;
@property (strong, nonatomic) IBOutlet UITextField *mxkTextField;
@property (strong, nonatomic) IBOutlet UIButton *mxkButton;
/**
The custom accessory view associated with the text field. This view is
actually used to retrieve the keyboard view. Indeed the keyboard view is the superview of
the accessory view when the text field become the first responder.
*/
@property (readonly) UIView *inputAccessoryView;
/**
Leading/Trailing constraints define here spacing to nearest neighbor (no relative to margin)
*/
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelTopConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelLeadingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelTrailingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkTextFieldLeadingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkTextFieldTopConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkTextFieldBottomConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkButtonLeadingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkButtonTrailingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkButtonMinWidthConstraint;
- (IBAction)textFieldEditingChanged:(id)sender;
@end
@@ -0,0 +1,57 @@
/*
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 "MXKTableViewCellWithLabelTextFieldAndButton.h"
@implementation MXKTableViewCellWithLabelTextFieldAndButton
@synthesize inputAccessoryView;
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
{
// Add an accessory view to the text view in order to retrieve keyboard view.
inputAccessoryView = [[UIView alloc] initWithFrame:CGRectZero];
_mxkTextField.inputAccessoryView = inputAccessoryView;
}
return self;
}
- (void)dealloc
{
inputAccessoryView = nil;
}
#pragma mark - UITextField delegate
- (BOOL)textFieldShouldReturn:(UITextField*)textField
{
// "Done" key has been pressed
[self.mxkTextField resignFirstResponder];
return YES;
}
#pragma mark - Action
- (IBAction)textFieldEditingChanged:(id)sender
{
self.mxkButton.enabled = (self.mxkTextField.text.length != 0);
}
@end
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" rowHeight="70" id="R1k-d9-UZq" customClass="MXKTableViewCellWithLabelTextFieldAndButton">
<rect key="frame" x="0.0" y="0.0" width="600" height="70"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="R1k-d9-UZq" id="TrF-Sd-htc">
<rect key="frame" x="0.0" y="0.0" width="600" height="69"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="HwF-3u-e5Q">
<rect key="frame" x="8" y="33" width="527" height="30"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" returnKeyType="done"/>
<connections>
<action selector="textFieldEditingChanged:" destination="R1k-d9-UZq" eventType="editingChanged" id="LeA-rJ-pcV"/>
<outlet property="delegate" destination="R1k-d9-UZq" id="X6b-0x-2HJ"/>
</connections>
</textField>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="KoB-y6-MAj">
<rect key="frame" x="543" y="33" width="49" height="30"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="40" id="8Yl-vg-asf"/>
</constraints>
<state key="normal" title="Submit">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="14" translatesAutoresizingMaskIntoConstraints="NO" id="9Ez-M7-s8I">
<rect key="frame" x="8" y="8" width="584" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="KoB-y6-MAj" firstAttribute="centerY" secondItem="HwF-3u-e5Q" secondAttribute="centerY" id="0j3-Pg-l0f"/>
<constraint firstAttribute="trailing" secondItem="KoB-y6-MAj" secondAttribute="trailing" constant="8" id="Bed-gz-IoV"/>
<constraint firstItem="9Ez-M7-s8I" firstAttribute="top" secondItem="TrF-Sd-htc" secondAttribute="top" constant="8" id="UiT-hp-y64"/>
<constraint firstAttribute="bottom" secondItem="HwF-3u-e5Q" secondAttribute="bottom" constant="6" id="VBI-N7-ubO"/>
<constraint firstAttribute="trailing" secondItem="9Ez-M7-s8I" secondAttribute="trailing" constant="8" id="cKm-EC-j6C"/>
<constraint firstItem="HwF-3u-e5Q" firstAttribute="top" secondItem="9Ez-M7-s8I" secondAttribute="bottom" constant="4" id="hsT-zv-aFU"/>
<constraint firstItem="HwF-3u-e5Q" firstAttribute="leading" secondItem="TrF-Sd-htc" secondAttribute="leading" constant="8" id="uYR-ZY-FTK"/>
<constraint firstItem="KoB-y6-MAj" firstAttribute="leading" secondItem="HwF-3u-e5Q" secondAttribute="trailing" constant="8" id="vZw-2I-IYg"/>
<constraint firstItem="9Ez-M7-s8I" firstAttribute="leading" secondItem="TrF-Sd-htc" secondAttribute="leading" constant="8" id="z1S-ho-Niu"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="mxkButton" destination="KoB-y6-MAj" id="HtV-Hu-sJB"/>
<outlet property="mxkButtonLeadingConstraint" destination="vZw-2I-IYg" id="tSD-e8-387"/>
<outlet property="mxkButtonMinWidthConstraint" destination="8Yl-vg-asf" id="Ysh-Vs-QYO"/>
<outlet property="mxkButtonTrailingConstraint" destination="Bed-gz-IoV" id="3KC-1v-buQ"/>
<outlet property="mxkLabel" destination="9Ez-M7-s8I" id="RAD-Qz-GHK"/>
<outlet property="mxkLabelLeadingConstraint" destination="z1S-ho-Niu" id="nPj-Hs-eA6"/>
<outlet property="mxkLabelTopConstraint" destination="UiT-hp-y64" id="Fq9-DJ-zQN"/>
<outlet property="mxkLabelTrailingConstraint" destination="cKm-EC-j6C" id="nA3-he-Wll"/>
<outlet property="mxkTextField" destination="HwF-3u-e5Q" id="aTc-9G-9BE"/>
<outlet property="mxkTextFieldBottomConstraint" destination="VBI-N7-ubO" id="iwS-Hp-9EQ"/>
<outlet property="mxkTextFieldLeadingConstraint" destination="uYR-ZY-FTK" id="kOH-F3-xGk"/>
<outlet property="mxkTextFieldTopConstraint" destination="hsT-zv-aFU" id="n5u-Lo-TOd"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -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 "MXKTableViewCell.h"
/**
'MXKTableViewCellWithPicker' inherits 'MXKTableViewCell' class.
It constains a 'UIPickerView' vertically centered.
*/
@interface MXKTableViewCellWithPicker : MXKTableViewCell
@property (strong, nonatomic) IBOutlet UIPickerView* mxkPickerView;
/**
Leading/Trailing constraints define here spacing to nearest neighbor (no relative to margin)
*/
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkPickerViewLeadingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkPickerViewTopConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkPickerViewBottomConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkPickerViewTrailingConstraint;
@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 "MXKTableViewCellWithPicker.h"
@implementation MXKTableViewCellWithPicker
@end
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="164" id="q82-Um-cU7" customClass="MXKTableViewCellWithPicker">
<rect key="frame" x="0.0" y="0.0" width="600" height="164"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="q82-Um-cU7" id="aBM-uR-U5E">
<rect key="frame" x="0.0" y="0.0" width="600" height="163"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<pickerView contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="hrr-rd-eoA">
<rect key="frame" x="8" y="8" width="584" height="147"/>
</pickerView>
</subviews>
<constraints>
<constraint firstItem="hrr-rd-eoA" firstAttribute="top" secondItem="aBM-uR-U5E" secondAttribute="top" constant="8" id="J6o-ID-bDs"/>
<constraint firstAttribute="trailing" secondItem="hrr-rd-eoA" secondAttribute="trailing" constant="8" id="SSs-GG-jvP"/>
<constraint firstItem="hrr-rd-eoA" firstAttribute="leading" secondItem="aBM-uR-U5E" secondAttribute="leading" constant="8" id="V0R-Eb-PWo"/>
<constraint firstAttribute="bottom" secondItem="hrr-rd-eoA" secondAttribute="bottom" constant="8" id="h2E-4o-qec"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="mxkPickerView" destination="hrr-rd-eoA" id="HBG-aN-sZg"/>
<outlet property="mxkPickerViewBottomConstraint" destination="h2E-4o-qec" id="2eo-oT-tUZ"/>
<outlet property="mxkPickerViewLeadingConstraint" destination="V0R-Eb-PWo" id="uBh-Jo-oOn"/>
<outlet property="mxkPickerViewTopConstraint" destination="J6o-ID-bDs" id="pEK-EQ-1IJ"/>
<outlet property="mxkPickerViewTrailingConstraint" destination="SSs-GG-jvP" id="8JI-Zy-LfB"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -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 "MXKTableViewCell.h"
/**
'MXKTableViewCellWithSearchBar' inherits 'MXKTableViewCell' class.
It constains a 'UISearchBar' vertically centered.
*/
@interface MXKTableViewCellWithSearchBar : MXKTableViewCell
@property (strong, nonatomic) IBOutlet UISearchBar *mxkSearchBar;
/**
Leading/Trailing constraints define here spacing to nearest neighbor (no relative to margin)
*/
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSearchBarLeadingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSearchBarTopConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSearchBarBottomConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSearchBarTrailingConstraint;
@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 "MXKTableViewCellWithSearchBar.h"
@implementation MXKTableViewCellWithSearchBar
@end
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="20U-7H-xmi" customClass="MXKTableViewCellWithSearchBar">
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="20U-7H-xmi" id="cFw-g7-Cgn">
<rect key="frame" x="0.0" y="0.0" width="600" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<searchBar contentMode="redraw" translatesAutoresizingMaskIntoConstraints="NO" id="HgP-TQ-bd1">
<rect key="frame" x="0.0" y="0.0" width="600" height="43"/>
<textInputTraits key="textInputTraits"/>
</searchBar>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="HgP-TQ-bd1" secondAttribute="bottom" id="9RG-MY-amb"/>
<constraint firstItem="HgP-TQ-bd1" firstAttribute="top" secondItem="cFw-g7-Cgn" secondAttribute="top" id="csH-4b-32a"/>
<constraint firstItem="HgP-TQ-bd1" firstAttribute="leading" secondItem="cFw-g7-Cgn" secondAttribute="leading" id="duA-9h-WEA"/>
<constraint firstAttribute="trailing" secondItem="HgP-TQ-bd1" secondAttribute="trailing" id="y0I-NU-96o"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="mxkSearchBar" destination="HgP-TQ-bd1" id="2YT-dI-X4U"/>
<outlet property="mxkSearchBarBottomConstraint" destination="9RG-MY-amb" id="CFM-k7-uGe"/>
<outlet property="mxkSearchBarLeadingConstraint" destination="duA-9h-WEA" id="5vm-V4-Qsu"/>
<outlet property="mxkSearchBarTopConstraint" destination="csH-4b-32a" id="NHJ-WR-9Hm"/>
<outlet property="mxkSearchBarTrailingConstraint" destination="y0I-NU-96o" id="lb1-cE-sE0"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -0,0 +1,50 @@
/*
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 "MXKTableViewCell.h"
/**
'MXKTableViewCellWithTextFieldAndButton' inherits 'MXKTableViewCell' class.
It constains a 'UITextField' and a 'UIButton' vertically centered.
*/
@interface MXKTableViewCellWithTextFieldAndButton : MXKTableViewCell <UITextFieldDelegate>
{
@protected
UIView *inputAccessoryView;
}
@property (strong, nonatomic) IBOutlet UITextField *mxkTextField;
@property (strong, nonatomic) IBOutlet UIButton *mxkButton;
/**
The custom accessory view associated with the text field. This view is
actually used to retrieve the keyboard view. Indeed the keyboard view is the superview of
the accessory view when the text field become the first responder.
*/
@property (readonly) UIView *inputAccessoryView;
/**
Leading/Trailing constraints define here spacing to nearest neighbor (no relative to margin)
*/
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkTextFieldLeadingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkButtonLeadingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkButtonTrailingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkButtonMinWidthConstraint;
- (IBAction)textFieldEditingChanged:(id)sender;
@end
@@ -0,0 +1,57 @@
/*
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 "MXKTableViewCellWithTextFieldAndButton.h"
@implementation MXKTableViewCellWithTextFieldAndButton
@synthesize inputAccessoryView;
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
{
// Add an accessory view to the text view in order to retrieve keyboard view.
inputAccessoryView = [[UIView alloc] initWithFrame:CGRectZero];
_mxkTextField.inputAccessoryView = inputAccessoryView;
}
return self;
}
- (void)dealloc
{
inputAccessoryView = nil;
}
#pragma mark - UITextField delegate
- (BOOL)textFieldShouldReturn:(UITextField*)textField
{
// "Done" key has been pressed
[self.mxkTextField resignFirstResponder];
return YES;
}
#pragma mark - Action
- (IBAction)textFieldEditingChanged:(id)sender
{
self.mxkButton.enabled = (self.mxkTextField.text.length != 0);
}
@end
@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="20U-7H-xmi" customClass="MXKTableViewCellWithTextFieldAndButton">
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="20U-7H-xmi" id="cFw-g7-Cgn">
<rect key="frame" x="0.0" y="0.0" width="600" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="XWh-rM-iqf">
<rect key="frame" x="8" y="7" width="527" height="30"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" returnKeyType="done"/>
<connections>
<action selector="textFieldEditingChanged:" destination="20U-7H-xmi" eventType="editingChanged" id="XUA-5O-AC8"/>
<outlet property="delegate" destination="20U-7H-xmi" id="IIR-M2-klm"/>
</connections>
</textField>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ycy-33-7Zq">
<rect key="frame" x="543" y="7" width="49" height="30"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="40" id="bvf-cL-4kz"/>
</constraints>
<state key="normal" title="Submit">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
</button>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="Ycy-33-7Zq" secondAttribute="trailing" constant="8" id="PaG-vi-8XK"/>
<constraint firstAttribute="centerY" secondItem="XWh-rM-iqf" secondAttribute="centerY" id="TMe-P5-cPy"/>
<constraint firstAttribute="centerY" secondItem="Ycy-33-7Zq" secondAttribute="centerY" id="WRy-Ga-ivi"/>
<constraint firstItem="XWh-rM-iqf" firstAttribute="leading" secondItem="cFw-g7-Cgn" secondAttribute="leading" constant="8" id="Ydc-ON-VIq"/>
<constraint firstItem="Ycy-33-7Zq" firstAttribute="leading" secondItem="XWh-rM-iqf" secondAttribute="trailing" constant="8" id="vw8-zU-VJt"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="mxkButton" destination="Ycy-33-7Zq" id="fwp-cx-faQ"/>
<outlet property="mxkButtonLeadingConstraint" destination="vw8-zU-VJt" id="Lep-jf-4Sa"/>
<outlet property="mxkButtonMinWidthConstraint" destination="bvf-cL-4kz" id="Q0x-ro-SjH"/>
<outlet property="mxkButtonTrailingConstraint" destination="PaG-vi-8XK" id="fYB-xQ-ppJ"/>
<outlet property="mxkTextField" destination="XWh-rM-iqf" id="2Bm-Ks-lAr"/>
<outlet property="mxkTextFieldLeadingConstraint" destination="Ydc-ON-VIq" id="Jap-J5-Vno"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -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 "MXKTableViewCell.h"
/**
'MXKTableViewCellWithTextView' inherits 'MXKTableViewCell' class.
It constains a 'UITextView' vertically centered.
*/
@interface MXKTableViewCellWithTextView : MXKTableViewCell
@property (strong, nonatomic) IBOutlet UITextView *mxkTextView;
/**
Leading/Trailing constraints define here spacing to nearest neighbor (no relative to margin)
*/
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkTextViewLeadingConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkTextViewTopConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkTextViewBottomConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkTextViewTrailingConstraint;
@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 "MXKTableViewCellWithTextView.h"
@implementation MXKTableViewCellWithTextView
@end

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